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/bundle6.js
require.config({"config": {
        "jsbuild":{"mage/adminhtml/wysiwyg/tiny_mce/tinymce5Adapter.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* global popups, tinyMceEditors, MediabrowserUtility, Base64 */\n/* eslint-disable strict */\ndefine([\n    'jquery',\n    'underscore',\n    'tinymce',\n    'mage/adminhtml/events',\n    'mage/adminhtml/wysiwyg/events',\n    'mage/translate',\n    'prototype',\n    'jquery/ui'\n], function (jQuery, _, tinyMCE, varienGlobalEvents, wysiwygEvents) {\n    'use strict';\n\n    var tinyMceWysiwyg = Class.create();\n\n    tinyMceWysiwyg.prototype = {\n        mediaBrowserOpener: null,\n        mediaBrowserTargetElementId: null,\n        magentoVariablesPlugin: null,\n        mode: 'exact',\n\n        /**\n         * @param {*} htmlId\n         * @param {Object} config\n         */\n        initialize: function (htmlId, config) {\n            this.id = htmlId;\n            this.config = config;\n\n            _.bindAll(\n                this,\n                'beforeSetContent',\n                'saveContent',\n                'onChangeContent',\n                'openFileBrowser',\n                'updateTextArea',\n                'onUndo',\n                'removeEvents'\n            );\n\n            varienGlobalEvents.attachEventHandler('tinymceChange', this.onChangeContent);\n            varienGlobalEvents.attachEventHandler('tinymceBeforeSetContent', this.beforeSetContent);\n            varienGlobalEvents.attachEventHandler('tinymceSetContent', this.updateTextArea);\n            varienGlobalEvents.attachEventHandler('tinymceSaveContent', this.saveContent);\n            varienGlobalEvents.attachEventHandler('tinymceUndo', this.onUndo);\n\n            if (typeof tinyMceEditors === 'undefined') {\n                window.tinyMceEditors = $H({});\n            }\n\n            tinyMceEditors.set(this.id, this);\n        },\n\n        /**\n         * Ensures the undo operation works properly\n         */\n        onUndo: function () {\n            this.addContentEditableAttributeBackToNonEditableNodes();\n        },\n\n        /**\n         * Setup TinyMCE editor\n         */\n        setup: function (mode) {\n            var deferreds = [],\n                settings,\n                self = this;\n\n            this.turnOff();\n\n            if (this.config.plugins) {\n                this.config.plugins.forEach(function (plugin) {\n                    var deferred;\n\n                    self.addPluginToToolbar(plugin.name, '|');\n\n                    if (!plugin.src) {\n                        return;\n                    }\n\n                    deferred = jQuery.Deferred();\n                    deferreds.push(deferred);\n\n                    require([plugin.src], function (factoryFn) {\n                        if (typeof factoryFn === 'function') {\n                            factoryFn(plugin.options);\n                        }\n\n                        tinyMCE.PluginManager.load(plugin.name, plugin.src);\n                        deferred.resolve();\n                    });\n                });\n            }\n\n            if (jQuery.isReady) {\n                tinyMCE.dom.Event.domLoaded = true;\n            }\n\n            settings = this.getSettings();\n\n            if (mode === 'inline') {\n                settings.inline = true;\n\n                if (!isNaN(settings.toolbarZIndex)) {\n                    tinyMCE.ui.FloatPanel.zIndex = settings.toolbarZIndex;\n                }\n\n                this.removeEvents(self.id);\n            }\n\n            jQuery.when.apply(jQuery, deferreds).done(function () {\n                tinyMCE.init(settings);\n                this.getPluginButtons().hide();\n                varienGlobalEvents.clearEventHandlers('open_browser_callback');\n                this.eventBus.clearEventHandlers('open_browser_callback');\n                this.eventBus.attachEventHandler('open_browser_callback', tinyMceEditors.get(self.id).openFileBrowser);\n            }.bind(this));\n        },\n\n        /**\n         * Remove events from instance.\n         *\n         * @param {String} wysiwygId\n         */\n        removeEvents: function (wysiwygId) {\n            var editor;\n\n            if (typeof tinyMceEditors !== 'undefined' && tinyMceEditors.get(wysiwygId)) {\n                editor = tinyMceEditors.get(wysiwygId);\n                varienGlobalEvents.removeEventHandler('tinymceChange', editor.onChangeContent);\n            }\n        },\n\n        /**\n         * Add plugin to the toolbar if not added.\n         *\n         * @param {String} plugin\n         * @param {String} separator\n         */\n        addPluginToToolbar: function (plugin, separator) {\n            var plugins = this.config.tinymce.plugins.split(' '),\n                toolbar = this.config.tinymce.toolbar.split(' ');\n\n            if (plugins.indexOf(plugin) === -1) {\n                plugins.push(plugin);\n            }\n\n            if (toolbar.indexOf(plugin) === -1) {\n                toolbar.push(separator || '', plugin);\n            }\n\n            this.config.tinymce.plugins = plugins.join(' ');\n            this.config.tinymce.toolbar = toolbar.join(' ');\n        },\n\n        /**\n         * Set the status of the toolbar to disabled or enabled (true for enabled, false for disabled)\n         * @param {Boolean} enabled\n         */\n        setToolbarStatus: function (enabled) {\n            var controlIds = this.get(this.getId()).theme.panel.rootControl.controlIdLookup;\n\n            _.each(controlIds, function (controlId) {\n                controlId.disabled(!enabled);\n                controlId.canFocus = enabled;\n\n                if (controlId.tooltip) {\n                    controlId.tooltip().state.set('rendered', enabled);\n\n                    if (enabled) {\n                        jQuery(controlId.getEl()).children('button').addBack().removeAttr('style');\n                    } else {\n                        jQuery(controlId.getEl()).children('button').addBack().attr('style', 'color: inherit;' +\n                            'background-color: inherit;' +\n                            'border-color: transparent;'\n                        );\n                    }\n                }\n            });\n        },\n\n        /**\n         * @return {Object}\n         */\n        getSettings: function () {\n            var settings,\n                eventBus = this.eventBus;\n\n            settings = {\n                selector: '#' + this.getId(),\n                theme: 'silver',\n                skin: 'oxide',\n                'toolbar_mode': 'wrap',\n                'entity_encoding': 'raw',\n                'convert_urls': false,\n                'content_css': this.config.tinymce['content_css'],\n                'relative_urls': true,\n                'valid_children': '+body[style]',\n                menubar: false,\n                plugins: this.config.tinymce.plugins,\n                toolbar: this.config.tinymce.toolbar,\n                adapter: this,\n                'body_id': 'html-body',\n\n                /**\n                 * @param {Object} editor\n                 */\n                setup: function (editor) {\n                    var onChange;\n\n                    editor.on('BeforeSetContent', function (evt) {\n                        varienGlobalEvents.fireEvent('tinymceBeforeSetContent', evt);\n                        eventBus.fireEvent(wysiwygEvents.beforeSetContent);\n                    });\n\n                    editor.on('SaveContent', function (evt) {\n                        varienGlobalEvents.fireEvent('tinymceSaveContent', evt);\n                        eventBus.fireEvent(wysiwygEvents.afterSave);\n                    });\n\n                    editor.on('paste', function (evt) {\n                        varienGlobalEvents.fireEvent('tinymcePaste', evt);\n                        eventBus.fireEvent(wysiwygEvents.afterPaste);\n                    });\n\n                    editor.on('PostProcess', function (evt) {\n                        varienGlobalEvents.fireEvent('tinymceSaveContent', evt);\n                        eventBus.fireEvent(wysiwygEvents.afterSave);\n                    });\n\n                    editor.on('undo', function (evt) {\n                        varienGlobalEvents.fireEvent('tinymceUndo', evt);\n                        eventBus.fireEvent(wysiwygEvents.afterUndo);\n                    });\n\n                    editor.on('focus', function () {\n                        eventBus.fireEvent(wysiwygEvents.afterFocus);\n                    });\n\n                    editor.on('blur', function () {\n                        eventBus.fireEvent(wysiwygEvents.afterBlur);\n                    });\n\n                    /**\n                     * @param {*} evt\n                     */\n                    onChange = function (evt) {\n                        varienGlobalEvents.fireEvent('tinymceChange', evt);\n                        eventBus.fireEvent(wysiwygEvents.afterChangeContent);\n                    };\n\n                    editor.on('Change', onChange);\n                    editor.on('keyup', onChange);\n\n                    editor.on('ExecCommand', function (cmd) {\n                        varienGlobalEvents.fireEvent('tinymceExecCommand', cmd);\n                    });\n\n                    editor.on('init', function (args) {\n                        varienGlobalEvents.fireEvent('wysiwygEditorInitialized', args.target);\n                        eventBus.fireEvent(wysiwygEvents.afterInitialization);\n                    });\n                }\n            };\n\n            // Set default initial height\n            settings['min_height'] = this.config.tinymce['min_height'] ? this.config.tinymce['min_height'] : 250;\n\n            if (this.config.skin) {\n                settings.skin = this.config.skin;\n            }\n\n            if (this.config['toolbar_mode']) {\n                settings['toolbar_mode'] = this.config['toolbar_mode'];\n            }\n\n            if (this.config.baseStaticUrl && this.config.baseStaticDefaultUrl) {\n                settings['document_base_url'] = this.config.baseStaticUrl;\n            }\n            // Set the document base URL\n            if (this.config['document_base_url']) {\n                settings['document_base_url'] = this.config['document_base_url'];\n            }\n\n            if (this.config['files_browser_window_url']) {\n                settings['file_picker_callback_types'] = 'file image media';\n\n                /**\n                 * @param {*} callback\n                 * @param {*} value\n                 * @param {*} meta\n                 */\n                settings['file_picker_callback'] = function (callback, value, meta) {\n                    var payload = {\n                        callback: callback,\n                        value: value,\n                        meta: meta\n                    };\n\n                    varienGlobalEvents.fireEvent('open_browser_callback', payload);\n                    this.eventBus.fireEvent('open_browser_callback', payload);\n                }.bind(this);\n            }\n\n            if (this.config.width) {\n                settings.width = this.config.width;\n            }\n\n            if (this.config.height) {\n                settings.height = this.config.height;\n            }\n\n            if (this.config.plugins) {\n                settings.magentoPluginsOptions = {};\n\n                _.each(this.config.plugins, function (plugin) {\n                    settings.magentoPluginsOptions[plugin.name] = plugin.options;\n                });\n            }\n\n            if (this.config.settings) {\n                Object.extend(settings, this.config.settings);\n            }\n\n            return settings;\n        },\n\n        /**\n         * @param {String} id\n         */\n        get: function (id) {\n            return tinyMCE.get(id);\n        },\n\n        /**\n         * @return {String|null}\n         */\n        getId: function () {\n            return this.id || (this.activeEditor() ? this.activeEditor().id : null) || tinyMceEditors.values()[0].id;\n        },\n\n        /**\n         * @return {Object}\n         */\n        activeEditor: function () {\n            return tinyMCE.activeEditor;\n        },\n\n        /**\n         * Insert content to active editor.\n         *\n         * @param {String} content\n         * @param {Boolean} ui\n         */\n        insertContent: function (content, ui) {\n            this.activeEditor().execCommand('mceInsertContent', typeof ui !== 'undefined' ? ui : false, content);\n        },\n\n        /**\n         * Replace entire contents of wysiwyg with string content parameter\n         *\n         * @param {String} content\n         */\n        setContent: function (content) {\n            this.get(this.getId()).setContent(content);\n        },\n\n        /**\n         * Set caret location in WYSIWYG editor.\n         *\n         * @param {Object} targetElement\n         */\n        setCaretOnElement: function (targetElement) {\n            this.activeEditor().selection.select(targetElement);\n            this.activeEditor().selection.collapse();\n        },\n\n        /**\n         * @param {Object} o\n         */\n        openFileBrowser: function (o) {\n            var typeTitle = this.translate('Select Images'),\n                storeId = this.config['store_id'] ? this.config['store_id'] : 0,\n                frameDialog = jQuery('div.mce-container[role=\"dialog\"]'),\n                self = this,\n                wUrl = this.config['files_browser_window_url'] +\n                    'target_element_id/' + this.getId() + '/' +\n                    'store/' + storeId + '/';\n\n            this.mediaBrowserOpener = o.callback;\n\n            if (typeof o.meta.filetype !== 'undefined' && o.meta.filetype !== '') { //eslint-disable-line eqeqeq\n                wUrl = wUrl + 'type/' + o.meta.filetype + '/';\n            }\n\n            frameDialog.hide();\n            jQuery('.tox-tinymce-aux').hide();\n\n            require(['mage/adminhtml/browser'], function () {\n                MediabrowserUtility.openDialog(wUrl, false, false, typeTitle, {\n                        /**\n                         * Closed.\n                         */\n                        closed: function () {\n                            frameDialog.show();\n                            jQuery('.tox-tinymce-aux').show();\n                        },\n\n                        targetElementId: self.activeEditor() ? self.activeEditor().id : null\n                    }\n                );\n            });\n        },\n\n        /**\n         * @param {String} string\n         * @return {String}\n         */\n        translate: function (string) {\n            return jQuery.mage.__ ? jQuery.mage.__(string) : string;\n        },\n\n        /**\n         * @return {null}\n         */\n        getMediaBrowserOpener: function () {\n            return this.mediaBrowserOpener;\n        },\n\n        /**\n         * @return {null}\n         */\n        getMediaBrowserTargetElementId: function () {\n            return this.mediaBrowserTargetElementId;\n        },\n\n        /**\n         * @return {jQuery|*|HTMLElement}\n         */\n        getToggleButton: function () {\n            return $('toggle' + this.getId());\n        },\n\n        /**\n         * Get plugins button.\n         */\n        getPluginButtons: function () {\n            return jQuery('#buttons' + this.getId() + ' > button.plugin');\n        },\n\n        /**\n         * @param {*} mode\n         * @return {wysiwygSetup}\n         */\n        turnOn: function (mode) {\n            this.closePopups();\n\n            this.setup(mode);\n\n            this.getPluginButtons().hide();\n\n            tinyMCE.execCommand('mceAddControl', false, this.getId());\n\n            return this;\n        },\n\n        /**\n         * @param {String} name\n         */\n        closeEditorPopup: function (name) {\n            if (typeof popups !== 'undefined' && popups[name] !== undefined && !popups[name].closed) {\n                popups[name].close();\n            }\n        },\n\n        /**\n         * @return {wysiwygSetup}\n         */\n        turnOff: function () {\n            this.closePopups();\n\n            this.getPluginButtons().show();\n\n            tinyMCE.execCommand('mceRemoveEditor', false, this.getId());\n\n            return this;\n        },\n\n        /**\n         * Close popups.\n         */\n        closePopups: function () {\n            // close all popups to avoid problems with updating parent content area\n            varienGlobalEvents.fireEvent('wysiwygClosePopups');\n            this.closeEditorPopup('browser_window' + this.getId());\n        },\n\n        /**\n         * @return {Boolean}\n         */\n        toggle: function () {\n            var content;\n\n            if (!tinyMCE.get(this.getId())) {\n                this.turnOn();\n\n                return true;\n            }\n\n            content = this.get(this.getId()) ? this.get(this.getId()).getContent() : this.getTextArea().val();\n\n            this.turnOff();\n\n            if (content.match(/{{.+?}}/g)) {\n                this.getTextArea().val(content.replace(/&quot;/g, '\"'));\n            }\n\n            return false;\n        },\n\n        /**\n         * On form validation.\n         */\n        onFormValidation: function () {\n            if (tinyMCE.get(this.getId())) {\n                $(this.getId()).value = tinyMCE.get(this.getId()).getContent();\n            }\n        },\n\n        /**\n         * On change content.\n         */\n        onChangeContent: function () {\n            // Add \"changed\" to tab class if it exists\n            var tab;\n\n            this.updateTextArea();\n\n            if (this.config['tab_id']) {\n                tab = $$('a[id$=' + this.config['tab_id'] + ']')[0];\n\n                if ($(tab) != undefined && $(tab).hasClassName('tab-item-link')) { //eslint-disable-line eqeqeq\n                    $(tab).addClassName('changed');\n                }\n            }\n        },\n\n        /**\n         * @param {Object} o\n         */\n        beforeSetContent: function (o) {\n            o.content = this.encodeContent(o.content);\n        },\n\n        /**\n         * @param {Object} o\n         */\n        saveContent: function (o) {\n            o.content = this.decodeContent(o.content);\n        },\n\n        /**\n         * Return the content stored in the WYSIWYG field\n         * @param {String} id\n         * @return {String}\n         */\n        getContent: function (id) {\n            return id ? this.get(id).getContent() : this.get(this.getId()).getContent();\n        },\n\n        /**\n         * @returns {Object}\n         */\n        getAdapterPrototype: function () {\n            return tinyMceWysiwyg;\n        },\n\n        /**\n         * Fix range selection placement when typing.  This fixes MAGETWO-84769\n         * @param {Object} editor\n         */\n        fixRangeSelection: function (editor) {\n            var selection = editor.selection,\n                dom = editor.dom,\n                rng = dom.createRng(),\n                doc = editor.getDoc(),\n                markerHtml,\n                marker;\n\n            // Validate the range we're trying to fix is contained within the current editors document\n            if (!selection.getContent().length && jQuery.contains(doc, selection.getRng().startContainer)) {\n                markerHtml = '<span id=\"mce_marker\" data-mce-type=\"bookmark\">\\uFEFF</span>';\n                selection.setContent(markerHtml);\n                marker = dom.get('mce_marker');\n                rng.setStartBefore(marker);\n                rng.setEndBefore(marker);\n                dom.remove(marker);\n                selection.setRng(rng);\n            }\n        },\n\n        /**\n         * Update text area.\n         */\n        updateTextArea: function () {\n            var editor = this.get(this.getId()),\n                content;\n\n            if (!editor || editor.id !== this.activeEditor().id) {\n                return;\n            }\n\n            this.addContentEditableAttributeBackToNonEditableNodes();\n\n            content = editor.getContent();\n            content = this.decodeContent(content);\n\n            this.getTextArea().val(content).trigger('change');\n        },\n\n        /**\n         * @return {Object} jQuery textarea element\n         */\n        getTextArea: function () {\n            return jQuery('#' + this.getId());\n        },\n\n        /**\n         * Set the status of the editor and toolbar\n         *\n         * @param {Boolean} enabled\n         */\n        setEnabledStatus: function (enabled) {\n            if (this.activeEditor()) {\n                this.activeEditor().getBody().setAttribute('contenteditable', enabled);\n                this.activeEditor().readonly = !enabled;\n                this.setToolbarStatus(enabled);\n            }\n\n            if (enabled) {\n                this.getTextArea().prop('disabled', false);\n            } else {\n                this.getTextArea().prop('disabled', 'disabled');\n            }\n        },\n\n        /**\n         * Retrieve directives URL with substituted directive value.\n         *\n         * @param {String} directive\n         */\n        makeDirectiveUrl: function (directive) {\n            return this.config['directives_url']\n                .replace(/directive/, 'directive/___directive/' + directive)\n                .replace(/\\/$/, '');\n        },\n\n        /**\n         * Convert {{directive}} style attributes syntax to absolute URLs\n         * @param {Object} content\n         * @return {*}\n         */\n        encodeDirectives: function (content) {\n            // collect all HTML tags with attributes that contain directives\n            return content.gsub(/<([a-z0-9\\-\\_]+[^>]+?)([a-z0-9\\-\\_]+=\"[^\"]*?\\{\\{.+?\\}\\}.*?\".*?)>/i, function (match) {\n                var attributesString = match[2],\n                    decodedDirectiveString;\n\n                // process tag attributes string\n                attributesString = attributesString.gsub(/([a-z0-9\\-\\_]+)=\"(.*?)(\\{\\{.+?\\}\\})(.*?)\"/i, function (m) {\n                    decodedDirectiveString = encodeURIComponent(Base64.mageEncode(m[3].replace(/&quot;/g, '\"') + m[4]));\n\n                    return m[1] + '=\"' + m[2] + this.makeDirectiveUrl(decodedDirectiveString) + '\"';\n                }.bind(this));\n\n                return '<' + match[1] + attributesString + '>';\n            }.bind(this));\n        },\n\n        /**\n         * Convert absolute URLs to {{directive}} style attributes syntax\n         * @param {Object} content\n         * @return {*}\n         */\n        decodeDirectives: function (content) {\n            var directiveUrl = this.makeDirectiveUrl('%directive%').split('?')[0], // remove query string from directive\n                // escape special chars in directives url to use in regular expression\n                regexEscapedDirectiveUrl = directiveUrl.replace(/([$^.?*!+:=()\\[\\]{}|\\\\])/g, '\\\\$1'),\n                regexDirectiveUrl = regexEscapedDirectiveUrl\n                    .replace(\n                        '%directive%',\n                        '([a-zA-Z0-9,_-]+(?:%2[A-Z]|)+\\/?)(?:(?!\").)*'\n                    ) + '/?(\\\\\\\\?[^\"]*)?', // allow optional query string\n                reg = new RegExp(regexDirectiveUrl);\n\n            return content.gsub(reg, function (match) {\n                return Base64.mageDecode(decodeURIComponent(match[1]).replace(/\\/$/, '')).replace(/\"/g, '&quot;');\n            });\n        },\n\n        /**\n         * @param {Object} attributes\n         * @return {Object}\n         */\n        parseAttributesString: function (attributes) {\n            var result = {};\n\n            // Decode &quot; entity, as regex below does not support encoded quote\n            attributes = attributes.replace(/&quot;/g, '\"');\n\n            attributes.gsub(\n                /(\\w+)(?:\\s*=\\s*(?:(?:\"((?:\\\\.|[^\"])*)\")|(?:'((?:\\\\.|[^'])*)')|([^>\\s]+)))?/,\n                function (match) {\n                    result[match[1]] = match[2];\n                }\n            );\n\n            return result;\n        },\n\n        /**\n         * @param {Object} content\n         * @return {*}\n         */\n        decodeContent: function (content) {\n            if (this.config['add_directives']) {\n                content = this.decodeDirectives(content);\n            }\n\n            content = varienGlobalEvents.fireEventReducer('wysiwygDecodeContent', content);\n\n            return content;\n        },\n\n        /**\n         * @param {Object} content\n         * @return {*}\n         */\n        encodeContent: function (content) {\n            if (this.config['add_directives']) {\n                content = this.encodeDirectives(content);\n            }\n\n            content = varienGlobalEvents.fireEventReducer('wysiwygEncodeContent', content);\n\n            return content;\n        },\n\n        /**\n         * Reinstate contenteditable attributes on .mceNonEditable nodes\n         */\n        addContentEditableAttributeBackToNonEditableNodes: function () {\n            jQuery('.mceNonEditable', this.activeEditor().getDoc()).attr('contenteditable', false);\n        },\n\n        /**\n         * Calls the save method on all editor instances in the collection.\n         */\n        triggerSave: function () {\n            tinyMCE.triggerSave();\n        }\n    };\n\n    return tinyMceWysiwyg.prototype;\n});\n","mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* global tinymce, widgetTools, Base64 */\n/* eslint-disable strict */\ndefine([\n    'wysiwygAdapter',\n    'mage/adminhtml/events',\n    'mage/adminhtml/wysiwyg/widget'\n], function (wysiwyg, varienGlobalEvents) {\n    return function (config) {\n        tinymce.create('tinymce.plugins.magentowidget', {\n\n            /**\n             * @param {tinymce.Editor} editor - Editor instance that the plugin is initialized in.\n             */\n            init: function (editor) {\n                var self = this;\n\n                this.activePlaceholder = null;\n\n                editor.addCommand('mceMagentowidget', function (img) {\n                    if (self.activePlaceholder) {\n                        img = self.activePlaceholder;\n                    }\n\n                    widgetTools.setActiveSelectedNode(img);\n                    widgetTools.openDialog(\n                        config['window_url'] + 'widget_target_id/' + editor.getElement().id + '/'\n                    );\n                });\n\n                // Register Widget plugin button\n                editor.ui.registry.addIcon(\n                    'magentowidget',\n                    '<svg width=\"24\" height=\"24\" viewBox=\"0 0 32.000000 32.000000\" ' +\n                    'preserveAspectRatio=\"xMidYMid meet\"> <g transform=\"translate(0.000000,32.000000) ' +\n                    'scale(0.100000,-0.100000)\" fill=\"#000000\" stroke=\"none\"> <path d=\"M130 290 c0 -5 13 -10 30 ' +\n                    '-10 22 0 28 -4 24 -15 -5 -11 2 -15 26 -15 21 0 30 -4 28 -12 -7 -20 -40 -22 -50 -4 -5 9 -14 16 ' +\n                    '-20 16 -6 0 -19 7 -28 15 -9 8 -25 12 -38 8 -33 -8 -27 -26 8 -21 34 5 40 -6 12 -21 -14 -7 -25 -6 ' +\n                    '-40 5 -12 8 -23 10 -27 5 -5 -8 88 -71 105 -71 3 0 29 14 58 31 l53 30 -23 18 c-13 10 -31 20 -40 ' +\n                    '24 -10 3 -18 11 -18 17 0 5 -13 10 -30 10 -16 0 -30 -4 -30 -10z m58 -82 c-3 -7 -15 -13 -28 -13 ' +\n                    '-13 0 -25 6 -27 13 -3 8 6 12 27 12 21 0 30 -4 28 -12z\"/> <path d=\"M30 151 l0 -60 61 -36 c33 ' +\n                    '-19 64 -35 69 -35 5 0 36 16 69 35 l61 36 0 60 0 61 -65 -37 -65 -36 -65 36 -65 37 0 -61z\"/> </g>' +\n                    '</svg>'\n                );\n                editor.ui.registry.addToggleButton('magentowidget', {\n                    icon: 'magentowidget',\n                    tooltip: jQuery.mage.__('Insert Widget'),\n\n                    /**\n                     * execute openVariablesSlideout for onAction callback\n                     */\n                    onAction: function () {\n                        editor.execCommand('mceMagentowidget');\n                    },\n\n                    /**\n                     * Add a node change handler, selects the button in the UI when a image is selected\n                     * @param {ToolbarToggleButtonInstanceApi} api\n                     */\n                    onSetup: function (api) {\n                        /**\n                         * NodeChange handler\n                         */\n                        editor.on('NodeChange', function (e) {\n                            var placeholder = e.element;\n\n                            if (self.isWidgetPlaceholderSelected(placeholder)) {\n                                widgetTools.setEditMode(true);\n                                api.setActive(true);\n                            } else {\n                                widgetTools.setEditMode(false);\n                                api.setActive(false);\n                            }\n                        });\n                    }\n                });\n\n                // Add a widget placeholder image double click callback\n                editor.on('dblClick', function (e) {\n                    var placeholder = e.target;\n\n                    if (self.isWidgetPlaceholderSelected(placeholder)) {\n                        widgetTools.setEditMode(true);\n                        this.execCommand('mceMagentowidget', null);\n                    }\n                });\n\n                /**\n                 * Attach event handler for when wysiwyg editor is about to encode its content\n                 */\n                varienGlobalEvents.attachEventHandler('wysiwygEncodeContent', function (content) {\n                    content = self.encodeWidgets(self.decodeWidgets(content));\n                    content = self.removeDuplicateAncestorWidgetSpanElement(content);\n\n                    return content;\n                });\n\n                /**\n                 * Attach event handler for when wysiwyg editor is about to decode its content\n                 */\n                varienGlobalEvents.attachEventHandler('wysiwygDecodeContent', function (content) {\n                    content = self.decodeWidgets(content);\n\n                    return content;\n                });\n\n                /**\n                 * Attach event handler for when popups associated with wysiwyg are about to be closed\n                 */\n                varienGlobalEvents.attachEventHandler('wysiwygClosePopups', function () {\n                    wysiwyg.closeEditorPopup('widget_window' + wysiwyg.getId());\n                });\n            },\n\n            /**\n             * @param {Object} placeholder - Contains the selected node\n             * @returns {Boolean}\n             */\n            isWidgetPlaceholderSelected: function (placeholder) {\n                var isSelected = false;\n\n                if (placeholder.nodeName &&\n                    (placeholder.nodeName === 'SPAN' || placeholder.nodeName === 'IMG') &&\n                    placeholder.className && placeholder.className.indexOf('magento-widget') !== -1\n                ) {\n                    this.activePlaceholder = placeholder;\n                    isSelected = true;\n                } else {\n                    this.activePlaceholder = null;\n                }\n\n                return isSelected;\n            },\n\n            /**\n             * Convert {{widget}} style syntax to image placeholder HTML\n             * @param {String} content\n             * @return {*}\n             */\n            encodeWidgets: function (content) {\n                return content.gsub(/\\{\\{widget([\\S\\s]*?)\\}\\}/i, function (match) {\n                    var attributes = wysiwyg.parseAttributesString(match[1]),\n                        imageSrc,\n                        imageHtml = '';\n\n                    if (attributes.type) {\n                        attributes.type = attributes.type.replace(/\\\\\\\\/g, '\\\\');\n                        imageSrc = config.placeholders[attributes.type];\n\n                        if (imageSrc) {\n                            imageHtml += '<span class=\"magento-placeholder magento-widget mceNonEditable\" ' +\n                                'contenteditable=\"false\">';\n                        } else {\n                            imageSrc = config['error_image_url'];\n                            imageHtml += '<span ' +\n                                'class=\"magento-placeholder magento-placeholder-error magento-widget mceNonEditable\" ' +\n                                'contenteditable=\"false\">';\n                        }\n\n                        imageHtml += '<img';\n                        imageHtml += ' id=\"' + Base64.idEncode(match[0]) + '\"';\n                        imageHtml += ' src=\"' + imageSrc + '\"';\n                        imageHtml += ' />';\n\n                        if (config.types[attributes.type]) {\n                            imageHtml += config.types[attributes.type];\n                        }\n\n                        imageHtml += '</span>';\n\n                        return imageHtml;\n                    }\n                });\n            },\n\n            /**\n             * Convert image placeholder HTML to {{widget}} style syntax\n             * @param {String} content\n             * @return {*}\n             */\n            decodeWidgets: function (content) {\n                return content.gsub(\n                    /(<span class=\"[^\"]*magento-widget[^\"]*\"[^>]*>)?<img([^>]+id=\"[^>]+)>(([^>]*)<\\/span>)?/i,\n                    function (match) {\n                        var attributes = wysiwyg.parseAttributesString(match[2]),\n                            widgetCode,\n                            result = match[0];\n\n                        if (attributes.id) {\n                            try {\n                                widgetCode = Base64.idDecode(attributes.id);\n                            } catch (e) {\n                                // Ignore and continue.\n                            }\n\n                            if (widgetCode && widgetCode.indexOf('{{widget') !== -1) {\n                                result = widgetCode;\n                            }\n                        }\n\n                        return result;\n                    }\n                );\n            },\n\n            /**\n             * Tinymce has strange behavior with html and this removes one of its side-effects\n             * @param {String} content\n             * @return {String}\n             */\n            removeDuplicateAncestorWidgetSpanElement: function (content) {\n                var parser, doc, returnval = '';\n\n                if (!window.DOMParser) {\n                    return content;\n                }\n\n                parser = new DOMParser();\n                doc = parser.parseFromString(content.replace(/&quot;/g, '&amp;quot;'), 'text/html');\n\n                [].forEach.call(doc.querySelectorAll('.magento-widget'), function (widgetEl) {\n                    var widgetChildEl = widgetEl.querySelector('.magento-widget');\n\n                    if (!widgetChildEl) {\n                        return;\n                    }\n\n                    [].forEach.call(widgetEl.childNodes, function (el) {\n                        widgetEl.parentNode.insertBefore(el, widgetEl);\n                    });\n\n                    widgetEl.parentNode.removeChild(widgetEl);\n                });\n\n                returnval += doc.head ? doc.head.innerHTML.replace(/&amp;quot;/g, '&quot;') : '';\n                returnval += doc.body ? doc.body.innerHTML.replace(/&amp;quot;/g, '&quot;') : '';\n\n                return returnval ? returnval : content;\n            },\n\n            /**\n             * @return {Object}\n             */\n            getInfo: function () {\n                return {\n                    longname: 'Magento Widget Manager Plugin',\n                    author: 'Magento Core Team',\n                    authorurl: 'http://magentocommerce.com',\n                    infourl: 'http://magentocommerce.com',\n                    version: '1.0'\n                };\n            }\n        });\n\n        // Register plugin\n        tinymce.PluginManager.add('magentowidget', tinymce.plugins.magentowidget);\n    };\n});\n","mage/adminhtml/wysiwyg/tiny_mce/plugins/magentovariable/editor_plugin.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* global tinymce, MagentovariablePlugin, varienGlobalEvents, Base64 */\n/* eslint-disable strict */\ndefine([\n    'Magento_Variable/js/config-directive-generator',\n    'Magento_Variable/js/custom-directive-generator',\n    'wysiwygAdapter',\n    'jquery',\n    'mage/adminhtml/tools'\n], function (configDirectiveGenerator, customDirectiveGenerator, wysiwyg, jQuery) {\n    return function (config) {\n        tinymce.create('tinymce.plugins.magentovariable', {\n\n            /**\n             * Initialize editor plugin.\n             *\n             * @param {tinymce.editor} editor - Editor instance that the plugin is initialized in.\n             */\n            init: function (editor) {\n                var self = this;\n\n                /**\n                 * Add new command to open variables selector slideout.\n                 */\n                editor.addCommand('openVariablesSlideout', function (commandConfig) {\n                    var selectedElement;\n\n                    if (commandConfig) {\n                        selectedElement = commandConfig.selectedElement;\n                    } else {\n                        selectedElement = tinymce.activeEditor.selection.getNode();\n                    }\n                    MagentovariablePlugin.setEditor(editor);\n                    MagentovariablePlugin.loadChooser(\n                        config.url,\n                        wysiwyg.getId(),\n                        selectedElement\n                    );\n                });\n\n                /**\n                 * Add button to the editor toolbar.\n                 */\n                editor.ui.registry.addIcon(\n                    'magentovariable',\n                    '<svg width=\"24\" height=\"24\" viewBox=\"0 0 32.000000 32.000000\" ' +\n                    'preserveAspectRatio=\"xMidYMid meet\"><g transform=\"translate(0.000000,32.000000) ' +\n                    'scale(0.100000,-0.100000)\" fill=\"#000000\" stroke=\"none\"><path d=\"M68 250 c-56 -44 -75 -136 -37 ' +\n                    '-184 27 -34 42 -33 23 2 -26 50 -9 129 38 179 26 28 10 30 -24 3z\"/><path d=\"M266 253 c5 -10 9 ' +\n                    '-41 9 -70 0 -42 -6 -60 -32 -97 -36 -51 -35 -56 7 -26 54 39 78 139 44 188 -18 26 -40 30 -28 5z\"/>' +\n                    '<path d=\"M128 223 c-15 -4 -15 -6 0 -33 16 -28 16 -30 -11 -58 -30 -31 -34 -42 -13 -42 8 0 17 11 ' +\n                    '20 25 4 14 11 25 16 25 5 0 12 -11 16 -25 6 -25 37 -35 49 -15 3 5 2 10 -3 10 -23 0 -20 44 5 76 ' +\n                    '25 34 25 34 4 34 -12 0 -20 -4 -17  -8 2 -4 0 -14 -5 -22 -7 -10 -12 -11 -15 -4 -10 25 -30 40 ' +\n                    '-46 37z\"/></g>' +\n                    '</svg>'\n                );\n                editor.ui.registry.addToggleButton('magentovariable', {\n                    icon: 'magentovariable',\n                    tooltip: jQuery.mage.__('Insert Variable'),\n\n                    /**\n                     * execute openVariablesSlideout for onAction callback\n                     */\n                    onAction: function () {\n                        editor.execCommand('openVariablesSlideout');\n                    },\n\n                    /**\n                     * Highlight or dismiss Insert Variable button when variable is selected or deselected.\n                     */\n                    onSetup: function (api) {\n                        /**\n                         * Toggle active state of Insert Variable button.\n                         *\n                         * @param {Object} e\n                         */\n                        var toggleVariableButton = function (e) {\n                            api.setActive(false);\n\n                            if (jQuery(e.target).hasClass('magento-variable')) {\n                                api.setActive(true);\n                            }\n                        };\n\n                        editor.on('click', toggleVariableButton);\n                        editor.on('change', toggleVariableButton);\n                    }\n                });\n\n                /**\n                 * Double click handler on the editor to handle dbl click on variable placeholder.\n                 */\n                editor.on('dblclick', function (evt) {\n                    if (jQuery(evt.target).hasClass('magento-variable')) {\n                        editor.selection.collapse(false);\n                        editor.execCommand('openVariablesSlideout', {\n                            ui: true,\n                            selectedElement: evt.target\n                        });\n                    }\n                });\n\n                /**\n                 * Attach event handler for when wysiwyg editor is about to encode its content\n                 */\n                varienGlobalEvents.attachEventHandler('wysiwygEncodeContent', function (content) {\n                    content = self.encodeVariables(content);\n\n                    return content;\n                });\n\n                /**\n                 * Attach event handler for when wysiwyg editor is about to decode its content\n                 */\n                varienGlobalEvents.attachEventHandler('wysiwygDecodeContent', function (content) {\n                    content = self.decodeVariables(content);\n\n                    return content;\n                });\n            },\n\n            /**\n             * Encode variables in content\n             *\n             * @param {String} content\n             * @returns {*}\n             */\n            encodeVariables: function (content) {\n                content = content.gsub(/\\{\\{config path=\\\"([^\\\"]+)\\\"\\}\\}/i, function (match) {\n                    var path = match[1],\n                        magentoVariables,\n                        imageHtml;\n\n                    magentoVariables = JSON.parse(config.placeholders);\n\n                    if (magentoVariables[match[1]] && magentoVariables[match[1]]['variable_type'] === 'default') {\n                        imageHtml = '<span id=\"%id\" class=\"magento-variable magento-placeholder mceNonEditable\">' +\n                            '%s</span>';\n                        imageHtml = imageHtml.replace('%s', magentoVariables[match[1]]['variable_name']);\n                    } else {\n                        imageHtml = '<span id=\"%id\" class=\"' +\n                            'magento-variable magento-placeholder magento-placeholder-error ' +\n                            'mceNonEditable' +\n                            '\">' +\n                            'Not found' +\n                            '</span>';\n                    }\n\n                    return imageHtml.replace('%id', Base64.idEncode(path));\n                });\n\n                content = content.gsub(/\\{\\{customVar code=([^\\}\\\"]+)\\}\\}/i, function (match) {\n                    var path = match[1],\n                        magentoVariables,\n                        imageHtml;\n\n                    magentoVariables = JSON.parse(config.placeholders);\n\n                    if (magentoVariables[match[1]] && magentoVariables[match[1]]['variable_type'] === 'custom') {\n                        imageHtml = '<span id=\"%id\" class=\"magento-variable magento-custom-var magento-placeholder ' +\n                            'mceNonEditable\">%s</span>';\n                        imageHtml = imageHtml.replace('%s', magentoVariables[match[1]]['variable_name']);\n                    } else {\n                        imageHtml = '<span id=\"%id\" class=\"' +\n                            'magento-variable magento-custom-var magento-placeholder ' +\n                            'magento-placeholder-error mceNonEditable' +\n                            '\">' +\n                            match[1] +\n                            '</span>';\n                    }\n\n                    return imageHtml.replace('%id', Base64.idEncode(path));\n                });\n\n                return content;\n            },\n\n            /**\n             * Decode variables in content.\n             *\n             * @param {String} content\n             * @returns {String}\n             */\n            decodeVariables: function (content) {\n                var doc = new DOMParser().parseFromString(content.replace(/&quot;/g, '&amp;quot;'), 'text/html'),\n                    returnval = '';\n\n                [].forEach.call(doc.querySelectorAll('span.magento-variable'), function (el) {\n                    var $el = jQuery(el);\n\n                    if ($el.hasClass('magento-custom-var')) {\n                        $el.replaceWith(\n                            customDirectiveGenerator.processConfig(\n                                Base64.idDecode(\n                                    $el.attr('id')\n                                )\n                            )\n                        );\n                    } else {\n                        $el.replaceWith(\n                            configDirectiveGenerator.processConfig(\n                                Base64.idDecode(\n                                    $el.attr('id')\n                                )\n                            )\n                        );\n                    }\n                });\n\n                returnval += doc.head ? doc.head.innerHTML.replace(/&amp;quot;/g, '&quot;') : '';\n                returnval += doc.body ? doc.body.innerHTML.replace(/&amp;quot;/g, '&quot;') : '';\n\n                return returnval ? returnval : content;\n            },\n\n            /**\n             * @return {Object}\n             */\n            getInfo: function () {\n                return {\n                    longname: 'Magento Variable Manager Plugin',\n                    author: 'Magento Core Team',\n                    authorurl: 'http://magentocommerce.com',\n                    infourl: 'http://magentocommerce.com',\n                    version: '1.0'\n                };\n            }\n        });\n\n        /**\n         * Register plugin\n         */\n        tinymce.PluginManager.add('magentovariable', tinymce.plugins.magentovariable);\n    };\n});\n","mage/requirejs/text.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/* inspired by http://github.com/requirejs/text */\n/*global XDomainRequest */\n\ndefine(['module'], function (module) {\n    'use strict';\n\n    var xmlRegExp = /^\\s*<\\?xml(\\s)+version=[\\'\\\"](\\d)*.(\\d)*[\\'\\\"](\\s)*\\?>/im,\n        bodyRegExp = /<body[^>]*>\\s*([\\s\\S]+)\\s*<\\/body>/im,\n        stripReg = /!strip$/i,\n        defaultConfig = module.config && module.config() || {};\n\n    /**\n     * Strips <?xml ...?> declarations so that external SVG and XML documents can be\n     * added to a document without worry.\n     * Also, if the string is an HTML document, only the part inside the body tag is returned.\n     *\n     * @param {String} external\n     * @returns {String}\n     */\n    function stripContent(external) {\n        var matches;\n\n        if (!external) {\n            return '';\n        }\n\n        matches = external.match(bodyRegExp);\n        external = matches ?\n            matches[1] :\n            external.replace(xmlRegExp, '');\n\n        return external;\n    }\n\n    /**\n     * Checks that url match current location\n     *\n     * @param {String} url\n     * @returns {Boolean}\n     */\n    function sameDomain(url) {\n        var uProtocol, uHostName, uPort,\n            xdRegExp = /^([\\w:]+)?\\/\\/([^\\/\\\\]+)/i,\n            location = window.location,\n            match = xdRegExp.exec(url);\n\n        if (!match) {\n            return true;\n        }\n        uProtocol = match[1];\n        uHostName = match[2];\n\n        uHostName = uHostName.split(':');\n        uPort = uHostName[1] || '';\n        uHostName = uHostName[0];\n\n        return (!uProtocol || uProtocol === location.protocol) &&\n            (!uHostName || uHostName.toLowerCase() === location.hostname.toLowerCase()) &&\n            (!uPort && !uHostName || uPort === location.port);\n    }\n\n    /**\n     * @returns {XMLHttpRequest|XDomainRequest|null}\n     */\n    function createRequest(url) {\n        var xhr = new XMLHttpRequest();\n\n        if (!sameDomain(url) && typeof XDomainRequest !== 'undefined') {\n            xhr = new XDomainRequest();\n        }\n\n        return xhr;\n    }\n\n    /**\n     * XHR requester. Returns value to callback.\n     *\n     * @param {String} url\n     * @param {Function} callback\n     * @param {Function} fail\n     * @param {Object} headers\n     */\n    function getContent(url, callback, fail, headers) {\n        var xhr = createRequest(url),\n            header;\n\n        xhr.open('GET', url);\n\n        /*eslint-disable max-depth */\n        if ('setRequestHeader' in xhr && headers) {\n            for (header in headers) {\n                if (headers.hasOwnProperty(header)) {\n                    xhr.setRequestHeader(header.toLowerCase(), headers[header]);\n                }\n            }\n        }\n\n        /**\n         * @inheritdoc\n         */\n        xhr.onreadystatechange = function () {\n            var status, err;\n\n            //Do not explicitly handle errors, those should be\n            //visible via console output in the browser.\n            if (xhr.readyState === 4) {\n                status = xhr.status || 0;\n\n                if (status > 399 && status < 600) {\n                    //An http 4xx or 5xx error. Signal an error.\n                    err = new Error(url + ' HTTP status: ' + status);\n                    err.xhr = xhr;\n\n                    if (fail) {\n                        fail(err);\n                    }\n                } else {\n                    callback(xhr.responseText);\n\n                    if (defaultConfig.onXhrComplete) {\n                        defaultConfig.onXhrComplete(xhr, url);\n                    }\n                }\n            }\n        };\n\n        /*eslint-enable max-depth */\n\n        if (defaultConfig.onXhr) {\n            defaultConfig.onXhr(xhr, url);\n        }\n\n        xhr.send();\n    }\n\n    /**\n     * Main method used by RequireJs.\n     *\n     * @param {String} name - has format: some.module.filext!strip\n     * @param {Function} req\n     * @param {Function|undefined} onLoad\n     */\n    function loadContent(name, req, onLoad) {\n\n        var toStrip = stripReg.test(name),\n            url = req.toUrl(name.replace(stripReg, '')),\n            headers = defaultConfig.headers;\n\n        getContent(url, function (content) {\n                content = toStrip ? stripContent(content) : content;\n                onLoad(content);\n            }, onLoad.error, headers);\n    }\n\n    return {\n        load: loadContent,\n        get: getContent\n    };\n});\n","mage/requirejs/baseUrlResolver.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * Sample configuration:\n *\n require.config({\n        \"config\": {\n            \"baseUrlInterceptor\": {\n                \"Magento_Ui/js/lib/knockout/bindings/collapsible.js\": \"../../../../frontend/Magento/luma/en_US/\"\n            }\n        }\n    });\n */\n\n/* global jsSuffixRegExp */\n/* eslint-disable max-depth */\ndefine('baseUrlInterceptor', [\n    'module'\n], function (module) {\n    'use strict';\n\n    /**\n     * RequireJS Context object\n     */\n    var ctx = require.s.contexts._,\n\n        /**\n         * Original function\n         *\n         * @type {Function}\n         */\n        origNameToUrl = ctx.nameToUrl,\n\n        /**\n         * Original function\n         *\n         * @type {Function}\n         */\n        newContextConstr = require.s.newContext;\n\n    /**\n     * Remove dots from URL\n     *\n     * @param {Array} ary\n     */\n    function trimDots(ary) {\n        var i, part, length = ary.length;\n\n        for (i = 0; i < length; i++) {\n            part = ary[i];\n\n            if (part === '.') {\n                ary.splice(i, 1);\n                i -= 1;\n            } else if (part === '..') {\n                if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {\n                    //End of the line. Keep at least one non-dot\n                    //path segment at the front so it can be mapped\n                    //correctly to disk. Otherwise, there is likely\n                    //no path mapping for a path starting with '..'.\n                    //This can still fail, but catches the most reasonable\n                    //uses of ..\n                    break;\n                } else if (i > 0) {\n                    ary.splice(i - 1, 2);\n                    i -= 2;\n                }\n            }\n        }\n    }\n\n    /**\n     * Normalize URL string (remove '/../')\n     *\n     * @param {String} name\n     * @param {String} baseName\n     * @param {Object} applyMap\n     * @param {Object} localContext\n     * @returns {*}\n     */\n    function normalize(name, baseName, applyMap, localContext) {\n        var lastIndex,\n            baseParts = baseName && baseName.split('/'),\n            normalizedBaseParts = baseParts;\n\n        //Adjust any relative paths.\n        if (name && name.charAt(0) === '.') {\n            //If have a base name, try to normalize against it,\n            //otherwise, assume it is a top-level require that will\n            //be relative to baseUrl in the end.\n            if (baseName) {\n                //Convert baseName to array, and lop off the last part,\n                //so that . matches that 'directory' and not name of the baseName's\n                //module. For instance, baseName of 'one/two/three', maps to\n                //'one/two/three.js', but we want the directory, 'one/two' for\n                //this normalization.\n                normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);\n                name = name.split('/');\n                lastIndex = name.length - 1;\n\n                // If wanting node ID compatibility, strip .js from end\n                // of IDs. Have to do this here, and not in nameToUrl\n                // because node allows either .js or non .js to map\n                // to same file.\n                if (localContext.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {\n                    name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');\n                }\n\n                name = normalizedBaseParts.concat(name);\n                trimDots(name);\n                name = name.join('/');\n            } else if (name.indexOf('./') === 0) {\n                // No baseName, so this is ID is resolved relative\n                // to baseUrl, pull off the leading dot.\n                name = name.substring(2);\n            }\n        }\n\n        return name;\n    }\n\n    /**\n     * Get full url.\n     *\n     * @param {Object} context\n     * @param {String} url\n     * @return {String}\n     */\n    function getUrl(context, url) {\n        var baseUrl = context.config.baseUrl,\n            newConfig = context.config,\n            modulePath = url.replace(baseUrl, ''),\n            newBaseUrl,\n            rewrite = module.config()[modulePath];\n\n        if (!rewrite) {\n            return url;\n        }\n\n        newBaseUrl = normalize(rewrite, baseUrl, undefined, newConfig);\n\n        return newBaseUrl + modulePath;\n    }\n\n    /**\n     * Replace original function.\n     *\n     * @returns {*}\n     */\n    ctx.nameToUrl = function () {\n        return getUrl(ctx, origNameToUrl.apply(ctx, arguments));\n    };\n\n    /**\n     * Replace original function.\n     *\n     * @return {*}\n     */\n    require.s.newContext = function () {\n        var newCtx = newContextConstr.apply(require.s, arguments),\n            newOrigNameToUrl = newCtx.nameToUrl;\n\n        /**\n         * New implementation of native function.\n         *\n         * @returns {String}\n         */\n        newCtx.nameToUrl = function () {\n            return getUrl(newCtx, newOrigNameToUrl.apply(newCtx, arguments));\n        };\n\n        return newCtx;\n    };\n});\n\nrequire(['baseUrlInterceptor'], function () {\n    'use strict';\n\n});\n","mage/requirejs/resolver.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'underscore',\n    'domReady!'\n], function (_) {\n    'use strict';\n\n    var context = require.s.contexts._,\n        execCb = context.execCb,\n        registry = context.registry,\n        callbacks = [],\n        retries = 10,\n        updateDelay = 1,\n        ready,\n        update;\n\n    /**\n     * Checks if provided callback already exists in the callbacks list.\n     *\n     * @param {Object} callback - Callback object to be checked.\n     * @returns {Boolean}\n     */\n    function isSubscribed(callback) {\n        return !!_.findWhere(callbacks, callback);\n    }\n\n    /**\n     * Checks if provided module is rejected during load.\n     *\n     * @param {Object} module - Module to be checked.\n     * @return {Boolean}\n     */\n    function isRejected(module) {\n        return registry[module.id] && (registry[module.id].inited || registry[module.id].error);\n    }\n\n    /**\n     * Checks if provided module had path fallback triggered.\n     *\n     * @param {Object} module - Module to be checked.\n     * @return {Boolean}\n     */\n    function isPathFallback(module) {\n        return registry[module.id] && registry[module.id].events.error;\n    }\n\n    /**\n     * Checks if provided module has unresolved dependencies.\n     *\n     * @param {Object} module - Module to be checked.\n     * @returns {Boolean}\n     */\n    function isPending(module) {\n        if (!module.depCount) {\n            return false;\n        }\n\n        return module.depCount >\n            _.filter(module.depMaps, isRejected).length + _.filter(module.depMaps, isPathFallback).length;\n    }\n\n    /**\n     * Checks if requirejs's registry object contains pending modules.\n     *\n     * @returns {Boolean}\n     */\n    function hasPending() {\n        return _.some(registry, isPending);\n    }\n\n    /**\n     * Checks if 'resolver' module is in ready\n     * state and that there are no pending modules.\n     *\n     * @returns {Boolean}\n     */\n    function isReady() {\n        return ready && !hasPending();\n    }\n\n    /**\n     * Invokes provided callback handler.\n     *\n     * @param {Object} callback\n     */\n    function invoke(callback) {\n        callback.handler.call(callback.ctx);\n    }\n\n    /**\n     * Sets 'resolver' module to a ready state\n     * and invokes pending callbacks.\n     */\n    function resolve() {\n        ready = true;\n\n        callbacks.splice(0).forEach(invoke);\n    }\n\n    /**\n     * Drops 'ready' flag and runs the update process.\n     */\n    function tick() {\n        ready = false;\n\n        update(retries);\n    }\n\n    /**\n     * Adds callback which will be invoked\n     * when all of the pending modules are initiated.\n     *\n     * @param {Function} handler - 'Ready' event handler function.\n     * @param {Object} [ctx] - Optional context with which handler\n     *      will be invoked.\n     */\n    function subscribe(handler, ctx) {\n        var callback = {\n            handler: handler,\n            ctx: ctx\n        };\n\n        if (!isSubscribed(callback)) {\n            callbacks.push(callback);\n\n            if (isReady()) {\n                _.defer(tick);\n            }\n        }\n    }\n\n    /**\n     * Checks for all modules to be initiated\n     * and invokes pending callbacks if it's so.\n     *\n     * @param {Number} [retry] - Number of retries\n     *      that will be used to repeat the 'update' function\n     *      invokation in case if there are no pending requests.\n     */\n    update = _.debounce(function (retry) {\n        if (!hasPending()) {\n            retry ? update(--retry) : resolve();\n        }\n    }, updateDelay);\n\n    /**\n     * Overrides requirejs's original 'execCb' method\n     * in order to track pending modules.\n     *\n     * @returns {*} Result of original method call.\n     */\n    context.execCb = function () {\n        var exported = execCb.apply(context, arguments);\n\n        tick();\n\n        return exported;\n    };\n\n    return subscribe;\n});\n","mage/msie/file-reader.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    /**\n     * Init \"readAsBinaryString\" function for FileReader class.\n     * It need for IE11\n     * @param {Blob} fileData\n     */\n    var readAsBinaryStringIEFunc = function (fileData) {\n        var binary = '',\n            self = this,\n            reader = new FileReader();\n\n        /**\n         * Read file as binary string\n         */\n        reader.onload = function () {\n            var bytes, length, index;\n\n            /* eslint-disable no-undef */\n            bytes = new Uint8Array(reader.result);\n            /* eslint-enable */\n            length = bytes.length;\n\n            for (index = 0; index < length; index++) {\n                binary += String.fromCharCode(bytes[index]);\n            }\n            //self.result  - readonly so assign binary\n            self.content = binary;\n            $(self).trigger('onload');\n        };\n        reader.readAsArrayBuffer(fileData);\n    };\n\n    if (typeof FileReader.prototype.readAsBinaryString === 'undefined') {\n        FileReader.prototype.readAsBinaryString = readAsBinaryStringIEFunc;\n    }\n});\n","mage/apply/main.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'underscore',\n    'jquery',\n    './scripts'\n], function (_, $, processScripts) {\n    'use strict';\n\n    var dataAttr = 'data-mage-init',\n        nodeSelector = '[' + dataAttr + ']';\n\n    /**\n     * Initializes components assigned to a specified element via data-* attribute.\n     *\n     * @param {HTMLElement} el - Element to initialize components with.\n     * @param {Object|String} config - Initial components' config.\n     * @param {String} component - Components' path.\n     */\n    function init(el, config, component) {\n        require([component], function (fn) {\n            var $el;\n\n            if (typeof fn === 'object') {\n                fn = fn[component].bind(fn);\n            }\n\n            if (_.isFunction(fn)) {\n                fn = fn.bind(null, config, el);\n            } else {\n                $el = $(el);\n\n                if ($el[component]) {\n                    // eslint-disable-next-line jquery-no-bind-unbind\n                    fn = $el[component].bind($el, config);\n                }\n            }\n            // Init module in separate task to prevent blocking main thread.\n            setTimeout(fn);\n        }, function (error) {\n            if ('console' in window && typeof window.console.error === 'function') {\n                console.error(error);\n            }\n\n            return true;\n        });\n    }\n\n    /**\n     * Parses elements 'data-mage-init' attribute as a valid JSON data.\n     * Note: data-mage-init attribute will be removed.\n     *\n     * @param {HTMLElement} el - Element whose attribute should be parsed.\n     * @returns {Object}\n     */\n    function getData(el) {\n        var data = el.getAttribute(dataAttr);\n\n        el.removeAttribute(dataAttr);\n\n        return {\n            el: el,\n            data: JSON.parse(data)\n        };\n    }\n\n    return {\n        /**\n         * Initializes components assigned to HTML elements via [data-mage-init].\n         *\n         * @example Sample 'data-mage-init' declaration.\n         *      data-mage-init='{\"path/to/component\": {\"foo\": \"bar\"}}'\n         */\n        apply: function (context) {\n            var virtuals = processScripts(!context ? document : context),\n                nodes = document.querySelectorAll(nodeSelector);\n\n            _.toArray(nodes)\n                .map(getData)\n                .concat(virtuals)\n                .forEach(function (itemContainer) {\n                    var element = itemContainer.el;\n\n                    _.each(itemContainer.data, function (obj, key) {\n                            if (obj.mixins) {\n                                require(obj.mixins, function () { //eslint-disable-line max-nested-callbacks\n                                    var i, len;\n\n                                    for (i = 0, len = arguments.length; i < len; i++) {\n                                        $.extend(\n                                            true,\n                                            itemContainer.data[key],\n                                            arguments[i](itemContainer.data[key], element)\n                                        );\n                                    }\n\n                                    delete obj.mixins;\n                                    init.call(null, element, obj, key);\n                                });\n                            } else {\n                                init.call(null, element, obj, key);\n                            }\n\n                        }\n                    );\n\n                });\n        },\n        applyFor: init\n    };\n});\n","mage/apply/scripts.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'underscore',\n    'jquery'\n], function (_, $) {\n    'use strict';\n\n    var scriptSelector = 'script[type=\"text/x-magento-init\"]',\n        dataAttr = 'data-mage-init',\n        virtuals = [];\n\n    /**\n     * Adds components to the virtual list.\n     *\n     * @param {Object} components\n     */\n    function addVirtual(components) {\n        virtuals.push({\n            el: false,\n            data: components\n        });\n    }\n\n    /**\n     * Merges provided data with a current data\n     * of a elements' \"data-mage-init\" attribute.\n     *\n     * @param {Object} components - Object with components and theirs configuration.\n     * @param {HTMLElement} elem - Element whose data should be modified.\n     */\n    function setData(components, elem) {\n        var data = elem.getAttribute(dataAttr);\n\n        data = data ? JSON.parse(data) : {};\n        _.each(components, function (obj, key) {\n            if (_.has(obj, 'mixins')) {\n                data[key] = data[key] || {};\n                data[key].mixins = data[key].mixins || [];\n                data[key].mixins = data[key].mixins.concat(obj.mixins);\n                delete obj.mixins;\n            }\n        });\n\n        data = $.extend(true, data, components);\n        data = JSON.stringify(data);\n        elem.setAttribute(dataAttr, data);\n    }\n\n    /**\n     * Search for the elements by privded selector and extends theirs data.\n     *\n     * @param {Object} components - Object with components and theirs configuration.\n     * @param {String} selector - Selector for the elements.\n     */\n    function processElems(components, selector) {\n        var elems,\n            iterator;\n\n        if (selector === '*') {\n            addVirtual(components);\n\n            return;\n        }\n\n        elems = document.querySelectorAll(selector);\n        iterator = setData.bind(null, components);\n\n        _.toArray(elems).forEach(iterator);\n    }\n\n    /**\n     * Parses content of a provided script node.\n     * Note: node will be removed from DOM.\n     *\n     * @param {HTMLScriptElement} node - Node to be processed.\n     * @returns {Object}\n     */\n    function getNodeData(node) {\n        var data = node.textContent;\n\n        node.parentNode.removeChild(node);\n\n        return JSON.parse(data);\n    }\n\n    /**\n     * Parses 'script' tags with a custom type attribute and moves it's data\n     * to a 'data-mage-init' attribute of an element found by provided selector.\n     * Note: All found script nodes will be removed from DOM.\n     *\n     * @returns {Array} An array of components not assigned to the specific element.\n     *\n     * @example Sample declaration.\n     *      <script type=\"text/x-magento-init\">\n     *          {\n     *              \"body\": {\n     *                  \"path/to/component\": {\"foo\": \"bar\"}\n     *              }\n     *          }\n     *      </script>\n     *\n     * @example Providing data without selector.\n     *      {\n     *          \"*\": {\n     *              \"path/to/component\": {\"bar\": \"baz\"}\n     *          }\n     *      }\n     */\n    return function () {\n        var nodes = document.querySelectorAll(scriptSelector);\n\n        _.toArray(nodes)\n            .map(getNodeData)\n            .forEach(function (item) {\n                _.each(item, processElems);\n            });\n\n        return virtuals.splice(0, virtuals.length);\n    };\n});\n","mage/backend/tree-suggest.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    'mage/backend/suggest'\n], function ($) {\n    'use strict';\n\n    /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */\n    var hover_node, dehover_node, select_node, init;\n\n    $.extend(true, $, {\n        // @TODO: Move method 'treeToList' in file with utility functions\n        mage: {\n            /**\n             * @param {Array} list\n             * @param {Object} nodes\n             * @param {*} level\n             * @param {*} path\n             * @return {*}\n             */\n            treeToList: function (list, nodes, level, path) {\n                $.each(nodes, function () {\n                    if (typeof this === 'object') {\n                        list.push({\n                            label: this.label,\n                            id: this.id,\n                            level: level,\n                            item: this,\n                            path: path + this.label\n                        });\n\n                        if (this.children) {\n                            $.mage.treeToList(list, this.children, level + 1, path + this.label + ' / ');\n                        }\n                    }\n                });\n\n                return list;\n            }\n        }\n    });\n\n    hover_node = $.jstree.core.prototype.hover_node;\n    dehover_node = $.jstree.core.prototype.dehover_node;\n    select_node = $.jstree.core.prototype.select_node;\n    init = $.jstree.core.prototype.init;\n\n    $.extend(true, $.jstree.core.prototype, {\n        /**\n         * @override\n         */\n        init: function () {\n            this.get_container()\n                .show()\n                .on('keydown', $.proxy(function (e) {\n                    var o;\n\n                    if (e.keyCode === $.ui.keyCode.ENTER) {\n                        o = this.data.ui.hovered || this.data.ui.last_selected || -1;\n                        this.select_node(o, true);\n                    }\n                }, this));\n            init.call(this);\n        },\n\n        /**\n         * @override\n         */\n        hover_node: function (obj) {\n            hover_node.apply(this, arguments);\n            obj = this._get_node(obj);\n\n            if (!obj.length) {\n                return false;\n            }\n            this.get_container().trigger('hover_node', [{\n                item: obj.find('a:first')\n            }]);\n        },\n\n        /**\n         * @override\n         */\n        dehover_node: function () {\n            dehover_node.call(this);\n            this.get_container().trigger('dehover_node');\n        },\n\n        /**\n         * @override\n         */\n        select_node: function (o) {\n            var node;\n\n            select_node.apply(this, arguments);\n            node = this._get_node(o);\n\n            (node ? $(node) : this.data.ui.last_selected)\n                .trigger('select_tree_node');\n        }\n    });\n\n    $.widget('mage.treeSuggest', $.mage.suggest, {\n        widgetEventPrefix: 'suggest',\n        options: {\n            template:\n                '<% if (data.items.length) { %>' +\n                    '<% if (data.allShown()) { %>' +\n                        '<% if (typeof data.nested === \"undefined\") { %>' +\n                            '<div style=\"display:none;\" data-mage-init=\"{&quot;jstree&quot;:{&quot;plugins&quot;:[&quot;themes&quot;,&quot;html_data&quot;,&quot;ui&quot;,&quot;hotkeys&quot;],&quot;themes&quot;:{&quot;theme&quot;:&quot;default&quot;,&quot;dots&quot;:false,&quot;icons&quot;:false}}}\">' + //eslint-disable-line max-len\n                        '<% } %>' +\n                        '<ul>' +\n                            '<% _.each(data.items, function(value) { %>' +\n                                '<li class=\"<% if (data.itemSelected(value)) { %>mage-suggest-selected<% } %>' +\n                '                   <% if (value.is_active == 0) { %> mage-suggest-not-active<% } %>\">' +\n                                    '<a href=\"#\" <%= data.optionData(value) %>><%- value.label %></a>' +\n                                    '<% if (value.children && value.children.length) { %>' +\n                                        '<%= data.renderTreeLevel(value.children) %>' +\n                                    '<% } %>' +\n                                '</li>' +\n                            '<% }); %>' +\n                        '</ul>' +\n                        '<% if (typeof data.nested === \"undefined\") { %>' +\n                            '</div>' +\n                        '<% } %>' +\n                    '<% } else { %>' +\n                        '<ul data-mage-init=\"{&quot;menu&quot;:[]}\">' +\n                            '<% _.each(data.items, function(value) { %>' +\n                                '<% if (!data.itemSelected(value)) {%>' +\n                                    '<li <%= data.optionData(value) %>>' +\n                                        '<a href=\"#\">' +\n                                            '<span class=\"category-label\"><%- value.label %></span>' +\n                                            '<span class=\"category-path\"><%- value.path %></span>' +\n                                        '</a>' +\n                                    '</li>' +\n                                '<% } %>' +\n                            '<% }); %>' +\n                        '</ul>' +\n                    '<% } %>' +\n                '<% } else { %>' +\n                    '<span class=\"mage-suggest-no-records\"><%- data.noRecordsText %></span>' +\n                '<% } %>',\n            controls: {\n                selector: ':ui-menu, :mage-menu, .jstree',\n                eventsMap: {\n                    focus: ['menufocus', 'hover_node'],\n                    blur: ['menublur', 'dehover_node'],\n                    select: ['menuselect', 'select_tree_node']\n                }\n            }\n        },\n\n        /**\n         * @override\n         */\n        _bind: function () {\n            this._super();\n            this._on({\n                /**\n                 * @param {jQuery.Event} event\n                 */\n                keydown: function (event) {\n                    var keyCode = $.ui.keyCode;\n\n                    switch (event.keyCode) {\n                        case keyCode.LEFT:\n                        case keyCode.RIGHT:\n\n                            if (this.isDropdownShown()) {\n                                event.preventDefault();\n                                this._proxyEvents(event);\n                            }\n                            break;\n                    }\n                }\n            });\n        },\n\n        /**\n         * @override\n         */\n        close: function (e) {\n            var eType = e ? e.type : null;\n\n            if (eType === 'select_tree_node') {\n                this.element.focus();\n            } else {\n                this._superApply(arguments);\n            }\n        },\n\n        /**\n         * @override\n         */\n        _filterSelected: function (items, context) {\n            if (context._allShown) {\n                return items;\n            }\n\n            return this._superApply(arguments);\n        },\n\n        /**\n         * @override\n         */\n        _prepareDropdownContext: function () {\n            var context = this._superApply(arguments),\n                optionData = context.optionData,\n                templates = this.templates,\n                tmplName = this.templateName;\n\n            /**\n             * @param {Object} item\n             * @return {*|String}\n             */\n            context.optionData = function (item) {\n                item = $.extend({}, item);\n                delete item.children;\n\n                return optionData(item);\n            };\n\n            return $.extend(context, {\n                /**\n                 * @param {*} children\n                 * @return {*|jQuery}\n                 */\n                renderTreeLevel: function (children) {\n                    var _context = $.extend({}, this, {\n                        items: children,\n                        nested: true\n                    }),\n                    tmpl = templates[tmplName];\n\n                    tmpl = tmpl({\n                        data: _context\n                    });\n\n                    return $('<div>').append($(tmpl)).html();\n                }\n            });\n        },\n\n        /**\n         * @override\n         */\n        _processResponse: function (e, items, context) {\n            var control;\n\n            if (context && !context._allShown) {\n                items = this.filter($.mage.treeToList([], items, 0, ''), this._term);\n            }\n            control = this.dropdown.find(this._control.selector);\n\n            if (control.length && control.hasClass('jstree')) {\n                control.jstree('destroy');\n            }\n            this._superApply([e, items, context]);\n        }\n    });\n\n    return $.mage.treeSuggest;\n});\n","mage/backend/tabs.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* global FORM_KEY */\ndefine([\n    'jquery',\n    'jquery/ui',\n    'jquery/ui-modules/widgets/tabs'\n], function ($) {\n    'use strict';\n\n    var rhash, isLocal;\n\n    // mage.tabs base functionality\n    $.widget('mage.tabs', $.ui.tabs, {\n        options: {\n            spinner: false,\n            groups: null,\n            tabPanelClass: '',\n            excludedPanel: ''\n        },\n\n        /**\n         * Tabs creation\n         * @protected\n         */\n        _create: function () {\n            var activeIndex = this._getTabIndex(this.options.active);\n\n            this.options.active = activeIndex >= 0 ? activeIndex : 0;\n            this._super();\n        },\n\n        /**\n         * @override\n         * @private\n         * @return {Array} Array of DOM-elements\n         */\n        _getList: function () {\n            if (this.options.groups) {\n                return this.element.find(this.options.groups);\n            }\n\n            return this._super();\n        },\n\n        /**\n         * Get active anchor\n         * @return {Element}\n         */\n        activeAnchor: function () {\n            return this.anchors.eq(this.option('active'));\n        },\n\n        /**\n         * Get tab index by tab id\n         * @protected\n         * @param {String} id - id of tab\n         * @return {Number}\n         */\n        _getTabIndex: function (id) {\n            var anchors = this.anchors ?\n                this.anchors :\n                this._getList().find('> li > a[href]');\n\n            return anchors.index($('#' + id));\n        },\n\n        /**\n         * Switch between tabs\n         * @protected\n         * @param {Object} event - event object\n         * @param {undefined|Object} eventData\n         */\n        _toggle: function (event, eventData) {\n            var anchor = $(eventData.newTab).find('a');\n\n            if ($(eventData.newTab).find('a').data().tabType === 'link') {\n                location.href = anchor.prop('href');\n            } else {\n                this._superApply(arguments);\n            }\n        }\n    });\n\n    rhash = /#.*$/;\n\n    /**\n     * @param {*} anchor\n     * @return {Boolean}\n     */\n    isLocal = function (anchor) {\n        return anchor.hash.length > 1 &&\n            anchor.href.replace(rhash, '') ===\n                location.href.replace(rhash, '')\n                    // support: Safari 5.1\n                    // Safari 5.1 doesn't encode spaces in window.location\n                    // but it does encode spaces from anchors (#8777)\n                    .replace(/\\s/g, '%20');\n    };\n\n    // Extension for mage.tabs - Move panels in destination element\n    $.widget('mage.tabs', $.mage.tabs, {\n        /**\n         * Move panels in destination element on creation\n         * @protected\n         * @override\n         */\n        _create: function () {\n            this._super();\n            this._movePanelsInDestination(this.panels);\n        },\n\n        /**\n         * Get panel for tab. If panel no exist in tabs container, then find panel in destination element\n         * @protected\n         * @override\n         * @param {Element} tab - tab \"li\" DOM-element\n         * @return {Element}\n         */\n        _getPanelForTab: function (tab) {\n            var panel = this._superApply(arguments),\n                id;\n\n            if (!panel.length) {\n                id = $(tab).attr('aria-controls');\n                panel = $(this.options.destination).find(this._sanitizeSelector('#' + id));\n            }\n\n            return panel;\n        },\n\n        /**\n         * @private\n         */\n        _processTabs: function () {\n            var that = this;\n\n            this.tablist = this._getList()\n                .addClass('ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all')\n                .attr('role', 'tablist');\n\n            this.tabs = this.tablist.find('> li:has(a[href])')\n                .addClass('ui-state-default ui-corner-top')\n                .attr({\n                    role: 'tab',\n                    tabIndex: -1\n                });\n\n            this.anchors = this.tabs.map(function () {\n                return $('a', this)[ 0 ];\n            })\n                .addClass('ui-tabs-anchor')\n                .attr({\n                    role: 'presentation',\n                    tabIndex: -1\n                });\n\n            this.panels = $();\n\n            this.anchors.each(function (i, anchor) {\n                var selector, panel, panelId,\n                    anchorId = $(anchor).uniqueId().attr('id'),\n                    tab = $(anchor).closest('li'),\n                    originalAriaControls = tab.attr('aria-controls');\n\n                // inline tab\n                if (isLocal(anchor)) {\n                    selector = anchor.hash;\n                    panel = that.document.find(that._sanitizeSelector(selector));\n                    // remote tab\n                } else {\n                    panelId = tab.attr('aria-controls') || $({}).uniqueId()[ 0 ].id;\n                    selector = '#' + panelId;\n                    panel = that.element.find(selector);\n\n                    if (!panel.length) {\n                        panel = that._createPanel(panelId);\n                        panel.insertAfter(that.panels[ i - 1 ] || that.tablist);\n                    }\n                    panel.attr('aria-live', 'polite');\n                }\n\n                if (panel.length) {\n                    that.panels = that.panels.add(panel);\n                }\n\n                if (originalAriaControls) {\n                    tab.data('ui-tabs-aria-controls', originalAriaControls);\n                }\n                tab.attr({\n                    'aria-controls': selector.substring(1),\n                    'aria-labelledby': anchorId\n                });\n                panel.attr('aria-labelledby', anchorId);\n\n                if (that.options.excludedPanel.indexOf(anchorId + '_content') < 0) {\n                    panel.addClass(that.options.tabPanelClass);\n                }\n            });\n\n            this.panels\n                .addClass('ui-tabs-panel ui-widget-content ui-corner-bottom')\n                .attr('role', 'tabpanel');\n        },\n\n        /**\n         * Move panels in destination element\n         * @protected\n         * @override\n         */\n        _movePanelsInDestination: function (panels) {\n            if (this.options.destination && !panels.parents(this.options.destination).length) {\n                this.element.trigger('beforePanelsMove', panels);\n\n                panels.find('script:not([type]), script[type=\"text/javascript\"]').remove();\n\n                panels.appendTo(this.options.destination)\n                    .each($.proxy(function (i, panel) {\n                        $(panel).trigger('move.tabs', this.anchors.eq(i));\n                    }, this));\n            }\n        },\n\n        /**\n         * Move panels in destination element on tabs switching\n         * @protected\n         * @override\n         * @param {Object} event - event object\n         * @param {Object} eventData\n         */\n        _toggle: function (event, eventData) {\n            this._movePanelsInDestination(eventData.newPanel);\n            this._superApply(arguments);\n        }\n    });\n\n    // Extension for mage.tabs - Ajax functionality for tabs\n    $.widget('mage.tabs', $.mage.tabs, {\n        options: {\n            /**\n             * Add form key to ajax call\n             * @param {Object} event - event object\n             * @param {Object} ui\n             */\n            beforeLoad: function (event, ui) {\n                ui.ajaxSettings.type = 'POST';\n                ui.ajaxSettings.hasContent = true;\n                ui.jqXHR.setRequestHeader('Content-Type', ui.ajaxSettings.contentType);\n                ui.ajaxSettings.data = jQuery.param(\n                    {\n                        isAjax: true,\n                        'form_key': typeof FORM_KEY !== 'undefined' ? FORM_KEY : null\n                    },\n                    ui.ajaxSettings.traditional\n                );\n            },\n\n            /**\n             * Replacing href attribute with loaded panel id\n             * @param {Object} event - event object\n             * @param {Object} ui\n             */\n            load: function (event, ui) {\n                var panel = $(ui.panel);\n\n                $(ui.tab).prop('href', '#' + panel.prop('id'));\n                panel.trigger('contentUpdated');\n            }\n        }\n    });\n\n    // Extension for mage.tabs - Attach event handlers to tabs\n    $.widget('mage.tabs', $.mage.tabs, {\n        options: {\n            tabIdArgument: 'tab',\n            tabsBlockPrefix: null\n        },\n\n        /**\n         * Attach event handlers to tabs, on creation\n         * @protected\n         * @override\n         */\n        _refresh: function () {\n            this._super();\n            $.each(this.tabs, $.proxy(function (i, tab) {\n                $(this._getPanelForTab(tab))\n                    .off('changed' + this.eventNamespace)\n                    .off('highlight.validate' + this.eventNamespace)\n                    .off('focusin' + this.eventNamespace)\n\n                    .on('changed' + this.eventNamespace, {\n                        index: i\n                    }, $.proxy(this._onContentChange, this))\n                    .on('highlight.validate' + this.eventNamespace, {\n                        index: i\n                    }, $.proxy(this._onInvalid, this))\n                    .on('focusin' + this.eventNamespace, {\n                        index: i\n                    }, $.proxy(this._onFocus, this));\n            }, this));\n\n            ($(this.options.destination).is('form') ?\n                $(this.options.destination) :\n                $(this.options.destination).closest('form'))\n                    .off('beforeSubmit' + this.eventNamespace)\n                    .on('beforeSubmit' + this.eventNamespace, $.proxy(this._onBeforeSubmit, this));\n        },\n\n        /**\n         * Mark tab as changed if some field inside tab panel is changed\n         * @protected\n         * @param {Object} e - event object\n         */\n        _onContentChange: function (e) {\n            var cssChanged = '_changed';\n\n            this.anchors.eq(e.data.index).addClass(cssChanged);\n            this._updateNavTitleMessages(e, cssChanged);\n        },\n\n        /**\n         * Clone messages (tooltips) from anchor to parent element\n         * @protected\n         * @param {Object} e - event object\n         * @param {String} messageType - changed or error\n         */\n        _updateNavTitleMessages: function (e, messageType) {\n            var curAnchor = this.anchors.eq(e.data.index),\n                curItem = curAnchor.parents('[data-role=\"container\"]').find('[data-role=\"title\"]'),\n                curItemMessages = curItem.find('[data-role=\"title-messages\"]'),\n                activeClass = '_active';\n\n            if (curItemMessages.is(':empty')) {\n                curAnchor\n                    .find('[data-role=\"item-messages\"]')\n                    .clone()\n                    .appendTo(curItemMessages);\n            }\n\n            curItemMessages.find('.' + messageType).addClass(activeClass);\n        },\n\n        /**\n         * Mark tab as error if some field inside tab panel is not passed validation\n         * @param {Object} e - event object\n         * @protected\n         */\n        _onInvalid: function (e) {\n            var cssError = '_error',\n                fakeEvent = e;\n\n            fakeEvent.currentTarget = $(this.anchors).eq(e.data.index);\n            this._eventHandler(fakeEvent);\n            this.anchors.eq(e.data.index).addClass(cssError).find('.' + cssError).show();\n            this._updateNavTitleMessages(e, cssError);\n        },\n\n        /**\n         * Show tab panel if focus event triggered of some field inside tab panel\n         * @param {Object} e - event object\n         * @protected\n         */\n        _onFocus: function (e) {\n            this.option('_active', e.data.index);\n        },\n\n        /**\n         * Add active tab id in data object when \"beforeSubmit\" event is triggered\n         * @param {Object} e - event object\n         * @param {Object} data - event data object\n         * @protected\n         */\n        _onBeforeSubmit: function (e, data) { //eslint-disable-line no-unused-vars\n            var activeAnchor = this.activeAnchor(),\n                activeTabId = activeAnchor.prop('id'),\n                options;\n\n            if (this.options.tabsBlockPrefix) {\n                if (activeAnchor.is('[id*=\"' + this.options.tabsBlockPrefix + '\"]')) {\n                    activeTabId = activeAnchor.prop('id').substr(this.options.tabsBlockPrefix.length);\n                }\n            }\n            $(this.anchors).removeClass('error');\n            options = {\n                action: {\n                    args: {}\n                }\n            };\n            options.action.args[this.options.tabIdArgument] = activeTabId;\n        }\n    });\n\n    // Extension for mage.tabs - Shadow tabs functionality\n    $.widget('mage.tabs', $.mage.tabs, {\n        /**\n         * Add shadow tabs functionality on creation\n         * @protected\n         * @override\n         */\n        _refresh: function () {\n            var anchors, shadowTabs, tabs;\n\n            this._super();\n            anchors = this.anchors;\n            shadowTabs = this.options.shadowTabs;\n            tabs = this.tabs;\n\n            if (shadowTabs) {\n                anchors.each($.proxy(function (i, anchor) {\n                    var anchorId = $(anchor).prop('id');\n\n                    if (shadowTabs[anchorId]) {\n                        $(anchor).parents('li').on('click', $.proxy(function () {\n                            $.each(shadowTabs[anchorId], $.proxy(function (key, id) {\n                                this.load($(tabs).index($('#' + id).parents('li')), {});\n                            }, this));\n                        }, this));\n                    }\n                }, this));\n            }\n        }\n    });\n\n    return $.mage.tabs;\n});\n","mage/backend/suggest.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'mage/template',\n    'mage/mage',\n    'jquery/ui',\n    'mage/backend/menu',\n    'mage/translate'\n], function ($, mageTemplate) {\n    'use strict';\n\n    /**\n     * Implement base functionality\n     */\n    $.widget('mage.suggest', {\n        widgetEventPrefix: 'suggest',\n        options: {\n            template: '<% if (data.items.length) { %>' +\n                '<% if (!data.term && !data.allShown() && data.recentShown()) { %>' +\n                '<h5 class=\"title\"><%- data.recentTitle %></h5>' +\n                '<% } %>' +\n                '<ul data-mage-init=\\'{\"menu\":[]}\\'>' +\n                '<% _.each(data.items, function(value){ %>' +\n                '<% if (!data.itemSelected(value)) { %><li <%= data.optionData(value) %>>' +\n                '<a href=\"#\"><%- value.label %></a></li><% } %>' +\n                '<% }); %>' +\n                '<% if (!data.term && !data.allShown() && data.recentShown()) { %>' +\n                '<li data-mage-init=\\'{\"actionLink\":{\"event\":\"showAll\"}}\\' class=\"show-all\">' +\n                '<a href=\"#\"><%- data.showAllTitle %></a></li>' +\n                '<% } %>' +\n                '</ul><% } else { %><span class=\"mage-suggest-no-records\"><%- data.noRecordsText %></span><% } %>',\n            minLength: 1,\n\n            /**\n             * @type {(String|Array)}\n             */\n            source: null,\n            delay: 500,\n            loadingClass: 'mage-suggest-state-loading',\n            events: {},\n            appendMethod: 'after',\n            controls: {\n                selector: ':ui-menu, :mage-menu',\n                eventsMap: {\n                    focus: ['menufocus'],\n                    blur: ['menublur'],\n                    select: ['menuselect']\n                }\n            },\n            termAjaxArgument: 'label_part',\n            filterProperty: 'label',\n            className: null,\n            inputWrapper: '<div class=\"mage-suggest\"><div class=\"mage-suggest-inner\"></div></div>',\n            dropdownWrapper: '<div class=\"mage-suggest-dropdown\"></div>',\n            preventClickPropagation: true,\n            currentlySelected: null,\n            submitInputOnEnter: true\n        },\n\n        /**\n         * Component's constructor\n         * @private\n         */\n        _create: function () {\n            this._term = null;\n            this._nonSelectedItem = {\n                id: '',\n                label: ''\n            };\n            this.templates = {};\n            this._renderedContext = null;\n            this._selectedItem = this._nonSelectedItem;\n            this._control = this.options.controls || {};\n            this._setTemplate();\n            this._prepareValueField();\n            this._render();\n            this._bind();\n        },\n\n        /**\n         * Render base elements for suggest component\n         * @private\n         */\n        _render: function () {\n            var wrapper;\n\n            this.dropdown = $(this.options.dropdownWrapper).hide();\n            wrapper = this.options.className ?\n                $(this.options.inputWrapper).addClass(this.options.className) :\n                $(this.options.inputWrapper);\n            this.element\n                .wrap(wrapper)[this.options.appendMethod](this.dropdown)\n                .attr('autocomplete', 'off');\n        },\n\n        /**\n         * Define a field for storing item id (find in DOM or create a new one)\n         * @private\n         */\n        _prepareValueField: function () {\n            if (this.options.valueField) {\n                this.valueField = $(this.options.valueField);\n            } else {\n                this.valueField = this._createValueField()\n                    .insertBefore(this.element)\n                    .attr('name', this.element.attr('name'));\n                this.element.removeAttr('name');\n            }\n        },\n\n        /**\n         * Create value field which keeps a id for selected option\n         * can be overridden in descendants\n         * @return {jQuery}\n         * @private\n         */\n        _createValueField: function () {\n            return $('<input/>', {\n                type: 'hidden'\n            });\n        },\n\n        /**\n         * Component's destructor\n         * @private\n         */\n        _destroy: function () {\n            this.element\n                .unwrap()\n                .removeAttr('autocomplete');\n\n            if (!this.options.valueField) {\n                this.element.attr('name', this.valueField.attr('name'));\n                this.valueField.remove();\n            }\n\n            this.dropdown.remove();\n            this._off(this.element, 'keydown keyup blur');\n        },\n\n        /**\n         * Return actual value of an \"input\"-element\n         * @return {String}\n         * @private\n         */\n        _value: function () {\n            return this.element[this.element.is(':input') ? 'val' : 'text']().trim();\n        },\n\n        /**\n         * Pass original event to a control component for handling it as it's own event\n         * @param {Object} event - event object\n         * @private\n         */\n        _proxyEvents: function (event) {\n            var fakeEvent = $.extend({}, $.Event(event.type), {\n                    ctrlKey: event.ctrlKey,\n                    keyCode: event.keyCode,\n                    which: event.keyCode\n                }),\n                target = this._control.selector ? this.dropdown.find(this._control.selector) : this.dropdown;\n\n            target.trigger(fakeEvent);\n        },\n\n        /**\n         * Bind handlers on specific events\n         * @private\n         */\n        _bind: function () {\n            this._on($.extend({\n                /**\n                 * @param {jQuery.Event} event\n                 */\n                keydown: function (event) {\n                    var keyCode = $.ui.keyCode,\n                        suggestList,\n                        hasSuggestedItems,\n                        hasSelectedItems,\n                        selectedItem;\n\n                    switch (event.keyCode) {\n                        case keyCode.PAGE_UP:\n                        case keyCode.UP:\n                            if (!event.shiftKey) {\n                                event.preventDefault();\n                                this._proxyEvents(event);\n                            }\n\n                            suggestList = event.currentTarget.parentNode.getElementsByTagName('ul')[0];\n                            hasSuggestedItems = event.currentTarget\n                                    .parentNode.getElementsByTagName('ul')[0].children.length >= 0;\n\n                            if (hasSuggestedItems) {\n                                selectedItem =  $(suggestList.getElementsByClassName('_active')[0])\n                                    .removeClass('_active').prev().addClass('_active');\n                                event.currentTarget.value = selectedItem.find('a').text();\n                            }\n\n                            break;\n\n                        case keyCode.PAGE_DOWN:\n                        case keyCode.DOWN:\n                            if (!event.shiftKey) {\n                                event.preventDefault();\n                                this._proxyEvents(event);\n                            }\n\n                            suggestList = event.currentTarget.parentNode.getElementsByTagName('ul')[0];\n                            hasSuggestedItems = event.currentTarget\n                                .parentNode.getElementsByTagName('ul')[0].children.length >= 0;\n\n                            if (hasSuggestedItems) {\n                                hasSelectedItems = suggestList.getElementsByClassName('_active').length === 0;\n\n                                if (hasSelectedItems) { //eslint-disable-line max-depth\n                                    selectedItem = $(suggestList.children[0]).addClass('_active');\n                                    event.currentTarget.value = selectedItem.find('a').text();\n                                } else {\n                                    selectedItem = $(suggestList.getElementsByClassName('_active')[0])\n                                        .removeClass('_active').next().addClass('_active');\n                                    event.currentTarget.value = selectedItem.find('a').text();\n                                }\n                            }\n                            break;\n\n                        case keyCode.TAB:\n                            if (this.isDropdownShown()) {\n                                this._onSelectItem(event, null);\n                                event.preventDefault();\n                            }\n                            break;\n\n                        case keyCode.ENTER:\n                        case keyCode.NUMPAD_ENTER:\n                            this._toggleEnter(event);\n\n                            if (this.isDropdownShown() && this._focused) {\n                                this._proxyEvents(event);\n                                event.preventDefault();\n                            }\n                            break;\n\n                        case keyCode.ESCAPE:\n                            if (this.isDropdownShown()) {\n                                event.stopPropagation();\n                            }\n                            this.close(event);\n                            this._blurItem();\n                            break;\n                    }\n                },\n\n                /**\n                 * @param {jQuery.Event} event\n                 */\n                keyup: function (event) {\n                    var keyCode = $.ui.keyCode;\n\n                    switch (event.keyCode) {\n                        case keyCode.HOME:\n                        case keyCode.END:\n                        case keyCode.PAGE_UP:\n                        case keyCode.PAGE_DOWN:\n                        case keyCode.ESCAPE:\n                        case keyCode.UP:\n                        case keyCode.DOWN:\n                        case keyCode.LEFT:\n                        case keyCode.RIGHT:\n                        case keyCode.TAB:\n                            break;\n\n                        case keyCode.ENTER:\n                        case keyCode.NUMPAD_ENTER:\n                            if (this.isDropdownShown()) {\n                                event.preventDefault();\n                            }\n                            break;\n                        default:\n                            this.search(event);\n                    }\n                },\n\n                /**\n                 * @param {jQuery.Event} event\n                 */\n                blur: function (event) {\n                    if (!this.preventBlur) {\n                        this._abortSearch();\n                        this.close(event);\n                        this._change(event);\n                    } else {\n                        this.element.trigger('focus');\n                    }\n                },\n                cut: this.search,\n                paste: this.search,\n                input: this.search,\n                selectItem: this._onSelectItem,\n                click: this.search\n            }, this.options.events));\n\n            this._bindSubmit();\n            this._bindDropdown();\n        },\n\n        /**\n         * @param {Object} event\n         * @private\n         */\n        _toggleEnter: function (event) {\n            var suggestList,\n                activeItems,\n                selectedItem;\n\n            if (!this.options.submitInputOnEnter) {\n                event.preventDefault();\n            }\n\n            suggestList = $(event.currentTarget.parentNode).find('ul').first();\n            activeItems = suggestList.find('._active');\n\n            if (activeItems.length >= 0) {\n                selectedItem = activeItems.first();\n\n                if (selectedItem.find('a') && selectedItem.find('a').attr('href') !== undefined) {\n                    window.location = selectedItem.find('a').attr('href');\n                    event.preventDefault();\n                }\n            }\n        },\n\n        /**\n         * Bind handlers for submit on enter\n         * @private\n         */\n        _bindSubmit: function () {\n            this.element.parents('form').on('submit', function (event) {\n                if (!this.submitInputOnEnter) {\n                    event.preventDefault();\n                }\n            });\n        },\n\n        /**\n         * @param {Object} e - event object\n         * @private\n         */\n        _change: function (e) {\n            if (this._term !== this._value()) {\n                this._trigger('change', e);\n            }\n        },\n\n        /**\n         * Bind handlers for dropdown element on specific events\n         * @private\n         */\n        _bindDropdown: function () {\n            var events = {\n                /**\n                 * @param {jQuery.Event} e\n                 */\n                click: function (e) {\n                    // prevent default browser's behavior of changing location by anchor href\n                    e.preventDefault();\n                },\n\n                /**\n                 * @param {jQuery.Event} e\n                 */\n                mousedown: function (e) {\n                    e.preventDefault();\n                }\n            };\n\n            $.each(this._control.eventsMap, $.proxy(function (suggestEvent, controlEvents) {\n                $.each(controlEvents, $.proxy(function (i, handlerName) {\n                    switch (suggestEvent) {\n                        case 'select':\n                            events[handlerName] = this._onSelectItem;\n                            break;\n\n                        case 'focus':\n                            events[handlerName] = this._focusItem;\n                            break;\n\n                        case 'blur':\n                            events[handlerName] = this._blurItem;\n                            break;\n                    }\n                }, this));\n            }, this));\n\n            if (this.options.preventClickPropagation) {\n                this._on(this.dropdown, events);\n            }\n            // Fix for IE 8\n            this._on(this.dropdown, {\n                /**\n                 * Mousedown.\n                 */\n                mousedown: function () {\n                    this.preventBlur = true;\n                },\n\n                /**\n                 * Mouseup.\n                 */\n                mouseup: function () {\n                    this.preventBlur = false;\n                }\n            });\n        },\n\n        /**\n         * @override\n         */\n        _trigger: function (type, event) {\n            var result = this._superApply(arguments);\n\n            if (result === false && event) {\n                event.stopImmediatePropagation();\n                event.preventDefault();\n            }\n\n            return result;\n        },\n\n        /**\n         * Handle focus event of options item\n         * @param {Object} e - event object\n         * @param {Object} ui - object that can contain information about focused item\n         * @private\n         */\n        _focusItem: function (e, ui) {\n            if (ui && ui.item) {\n                this._focused = $(ui.item).prop('tagName') ?\n                    this._readItemData(ui.item) :\n                    ui.item;\n\n                this.element.val(this._focused.label);\n                this._trigger('focus', e, {\n                    item: this._focused\n                });\n            }\n        },\n\n        /**\n         * Handle blur event of options item\n         * @private\n         */\n        _blurItem: function () {\n            this._focused = null;\n            this.element.val(this._term);\n        },\n\n        /**\n         * @param {Object} e - event object\n         * @param {Object} item\n         * @private\n         */\n        _onSelectItem: function (e, item) {\n            if (item && typeof item === 'object' && $(e.target).is(this.element)) {\n                this._focusItem(e, {\n                    item: item\n                });\n            }\n\n            if (this._trigger('beforeselect', e || null, {\n                    item: this._focused\n                }) === false) {\n                return;\n            }\n            this._selectItem(e);\n            this._blurItem();\n            this._trigger('select', e || null, {\n                item: this._selectedItem\n            });\n        },\n\n        /**\n         * Save selected item and hide dropdown\n         * @private\n         * @param {Object} e - event object\n         */\n        _selectItem: function (e) {\n            if (this._focused) {\n                this._selectedItem = this._focused;\n\n                if (this._selectedItem !== this._nonSelectedItem) {\n                    this._term = this._selectedItem.label;\n                    this.valueField.val(this._selectedItem.id);\n                    this.close(e);\n                }\n            }\n        },\n\n        /**\n         * Read option data from item element\n         * @param {Element} element\n         * @return {Object}\n         * @private\n         */\n        _readItemData: function (element) {\n            return element.data('suggestOption') || this._nonSelectedItem;\n        },\n\n        /**\n         * Check if dropdown is shown\n         * @return {Boolean}\n         */\n        isDropdownShown: function () {\n            return this.dropdown.is(':visible');\n        },\n\n        /**\n         * Open dropdown\n         * @private\n         * @param {Object} e - event object\n         */\n        open: function (e) {\n            if (!this.isDropdownShown()) {\n                this.element.addClass('_suggest-dropdown-open');\n                this.dropdown.show();\n                this._trigger('open', e);\n            }\n        },\n\n        /**\n         * Close and clear dropdown content\n         * @private\n         * @param {Object} e - event object\n         */\n        close: function (e) {\n            this._renderedContext = null;\n\n            if (this.dropdown.length) {\n                this.element.removeClass('_suggest-dropdown-open');\n                this.dropdown.hide().empty();\n            }\n\n            this._trigger('close', e);\n        },\n\n        /**\n         * Acquire content template\n         * @private\n         */\n        _setTemplate: function () {\n            this.templateName = 'suggest' + Math.random().toString(36).substr(2);\n\n            this.templates[this.templateName] = mageTemplate(this.options.template);\n        },\n\n        /**\n         * Execute search process\n         * @public\n         * @param {Object} e - event object\n         */\n        search: function (e) {\n            var term = this._value();\n\n            if ((this._term !== term || term.length === 0) && !this.preventBlur) {\n                this._term = term;\n\n                if (typeof term === 'string' && term.length >= this.options.minLength) {\n                    if (this._trigger('search', e) === false) { //eslint-disable-line max-depth\n                        return;\n                    }\n                    this._search(e, term, {});\n                } else {\n                    this._selectedItem = this._nonSelectedItem;\n                    this._resetSuggestValue();\n                }\n            }\n        },\n\n        /**\n         * Clear suggest hidden input\n         * @private\n         */\n        _resetSuggestValue: function () {\n            this.valueField.val(this._nonSelectedItem.id);\n        },\n\n        /**\n         * Actual search method, can be overridden in descendants\n         * @param {Object} e - event object\n         * @param {String} term - search phrase\n         * @param {Object} context - search context\n         * @private\n         */\n        _search: function (e, term, context) {\n            var response = $.proxy(function (items) {\n                return this._processResponse(e, items, context || {});\n            }, this);\n\n            this.element.addClass(this.options.loadingClass);\n\n            if (this.options.delay) {\n                if (typeof this.options.data !== 'undefined') {\n                    response(this.filter(this.options.data, term));\n                }\n                clearTimeout(this._searchTimeout);\n                this._searchTimeout = this._delay(function () {\n                    this._source(term, response);\n                }, this.options.delay);\n            } else {\n                this._source(term, response);\n            }\n        },\n\n        /**\n         * Extend basic context with additional data (search results, search term)\n         * @param {Object} context\n         * @return {Object}\n         * @private\n         */\n        _prepareDropdownContext: function (context) {\n            return $.extend(context, {\n                items: this._items,\n                term: this._term,\n\n                /**\n                 * @param {Object} item\n                 * @return {String}\n                 */\n                optionData: function (item) {\n                    return 'data-suggest-option=\"' +\n                        $('<div>').text(JSON.stringify(item)).html().replace(/\"/g, '&quot;') + '\"';\n                },\n                itemSelected: $.proxy(this._isItemSelected, this),\n                noRecordsText: $.mage.__('No records found.')\n            });\n        },\n\n        /**\n         * @param {Object} item\n         * @return {Boolean}\n         * @private\n         */\n        _isItemSelected: function (item) {\n            return item.id == (this._selectedItem && this._selectedItem.id ? //eslint-disable-line eqeqeq\n                this._selectedItem.id :\n                this.options.currentlySelected);\n        },\n\n        /**\n         * Render content of suggest's dropdown\n         * @param {Object} e - event object\n         * @param {Array} items - list of label+id objects\n         * @param {Object} context - template's context\n         * @private\n         */\n        _renderDropdown: function (e, items, context) {\n            var tmpl = this.templates[this.templateName];\n\n            this._items = items;\n\n            tmpl = tmpl({\n                data: this._prepareDropdownContext(context)\n            });\n\n            $(tmpl).appendTo(this.dropdown.empty());\n\n            this.dropdown.trigger('contentUpdated')\n                .find(this._control.selector).on('focus', function (event) {\n                    event.preventDefault();\n                });\n\n            this._renderedContext = context;\n            this.element.removeClass(this.options.loadingClass);\n            this.open(e);\n        },\n\n        /**\n         * @param {Object} e\n         * @param {Object} items\n         * @param {Object} context\n         * @private\n         */\n        _processResponse: function (e, items, context) {\n            var renderer = $.proxy(function (i) {\n                return this._renderDropdown(e, i, context || {});\n            }, this);\n\n            if (this._trigger('response', e, [items, renderer]) === false) {\n                return;\n            }\n            this._renderDropdown(e, items, context);\n        },\n\n        /**\n         * Implement search process via spesific source\n         * @param {String} term - search phrase\n         * @param {Function} response - search results handler, process search result\n         * @private\n         */\n        _source: function (term, response) {\n            var o = this.options,\n                ajaxData;\n\n            if (Array.isArray(o.source)) {\n                response(this.filter(o.source, term));\n            } else if (typeof o.source === 'string') {\n                ajaxData = {};\n                ajaxData[this.options.termAjaxArgument] = term;\n\n                this._xhr = $.ajax($.extend(true, {\n                    url: o.source,\n                    type: 'POST',\n                    dataType: 'json',\n                    data: ajaxData,\n                    success: $.proxy(function (items) {\n                        this.options.data = items;\n                        response.apply(response, arguments);\n                    }, this)\n                }, o.ajaxOptions || {}));\n            } else if (typeof o.source === 'function') {\n                o.source.apply(o.source, arguments);\n            }\n        },\n\n        /**\n         * Abort search process\n         * @private\n         */\n        _abortSearch: function () {\n            this.element.removeClass(this.options.loadingClass);\n            clearTimeout(this._searchTimeout);\n        },\n\n        /**\n         * Perform filtering in advance loaded items and returns search result\n         * @param {Array} items - all available items\n         * @param {String} term - search phrase\n         * @return {Object}\n         */\n        filter: function (items, term) {\n            var matcher = new RegExp(term.replace(/[\\-\\/\\\\\\^$*+?.()|\\[\\]{}]/g, '\\\\$&'), 'i'),\n                itemsArray = Array.isArray(items) ? items : $.map(items, function (element) {\n                    return element;\n                }),\n                property = this.options.filterProperty;\n\n            return $.grep(\n                itemsArray,\n                function (value) {\n                    return matcher.test(value[property] || value.id || value);\n                }\n            );\n        }\n    });\n\n    /**\n     * Implement show all functionality and storing and display recent searches\n     */\n    $.widget('mage.suggest', $.mage.suggest, {\n        options: {\n            showRecent: false,\n            showAll: false,\n            storageKey: 'suggest',\n            storageLimit: 10\n        },\n\n        /**\n         * @override\n         */\n        _create: function () {\n            var recentItems;\n\n            if (this.options.showRecent && window.localStorage) {\n                recentItems = JSON.parse(localStorage.getItem(this.options.storageKey));\n\n                /**\n                 * @type {Array} - list of recently searched items\n                 * @private\n                 */\n                this._recentItems = Array.isArray(recentItems) ? recentItems : [];\n            }\n            this._super();\n        },\n\n        /**\n         * @override\n         */\n        _bind: function () {\n            this._super();\n            this._on(this.dropdown, {\n                /**\n                 * @param {jQuery.Event} e\n                 */\n                showAll: function (e) {\n                    e.stopImmediatePropagation();\n                    e.preventDefault();\n                    this.element.trigger('showAll');\n                }\n            });\n\n            if (this.options.showRecent || this.options.showAll) {\n                this._on({\n                    /**\n                     * @param {jQuery.Event} e\n                     */\n                    focus: function (e) {\n                        if (!this.isDropdownShown()) {\n                            this.search(e);\n                        }\n                    },\n                    showAll: this._showAll\n                });\n            }\n        },\n\n        /**\n         * @private\n         * @param {Object} e - event object\n         */\n        _showAll: function (e) {\n            this._abortSearch();\n            this._search(e, '', {\n                _allShown: true\n            });\n        },\n\n        /**\n         * @override\n         */\n        search: function (e) {\n            if (!this._value()) {\n\n                if (this.options.showRecent) {\n\n                    if (this._recentItems.length) { //eslint-disable-line max-depth\n                        this._processResponse(e, this._recentItems, {});\n                    } else {\n                        this._showAll(e);\n                    }\n                } else if (this.options.showAll) {\n                    this._showAll(e);\n                }\n            }\n            this._superApply(arguments);\n        },\n\n        /**\n         * @override\n         */\n        _selectItem: function () {\n            this._superApply(arguments);\n\n            if (this._selectedItem && this._selectedItem.id && this.options.showRecent) {\n                this._addRecent(this._selectedItem);\n            }\n        },\n\n        /**\n         * @override\n         */\n        _prepareDropdownContext: function () {\n            var context = this._superApply(arguments);\n\n            return $.extend(context, {\n                recentShown: $.proxy(function () {\n                    return this.options.showRecent;\n                }, this),\n                recentTitle: $.mage.__('Recent items'),\n                showAllTitle: $.mage.__('Show all...'),\n\n                /**\n                 * @return {Boolean}\n                 */\n                allShown: function () {\n                    return !!context._allShown;\n                }\n            });\n        },\n\n        /**\n         * Add selected item of search result into storage of recents\n         * @param {Object} item - label+id object\n         * @private\n         */\n        _addRecent: function (item) {\n            this._recentItems = $.grep(this._recentItems, function (obj) {\n                return obj.id !== item.id;\n            });\n            this._recentItems.unshift(item);\n            this._recentItems = this._recentItems.slice(0, this.options.storageLimit);\n            localStorage.setItem(this.options.storageKey, JSON.stringify(this._recentItems));\n        }\n    });\n\n    /**\n     * Implement multi suggest functionality\n     */\n    $.widget('mage.suggest', $.mage.suggest, {\n        options: {\n            multiSuggestWrapper: '<ul class=\"mage-suggest-choices\">' +\n                '<li class=\"mage-suggest-search-field\" data-role=\"parent-choice-element\"><' +\n                'label class=\"mage-suggest-search-label\"></label></li></ul>',\n            choiceTemplate: '<li class=\"mage-suggest-choice button\"><div><%- text %></div>' +\n            '<span class=\"mage-suggest-choice-close\" tabindex=\"-1\" ' +\n            'data-mage-init=\\'{\"actionLink\":{\"event\":\"removeOption\"}}\\'></span></li>',\n            selectedClass: 'mage-suggest-selected'\n        },\n\n        /**\n         * @override\n         */\n        _create: function () {\n            this.choiceTmpl = mageTemplate(this.options.choiceTemplate);\n\n            this._super();\n\n            if (this.options.multiselect) {\n                this.valueField.hide();\n            }\n        },\n\n        /**\n         * @override\n         */\n        _render: function () {\n            this._super();\n\n            if (this.options.multiselect) {\n                this._renderMultiselect();\n            }\n        },\n\n        /**\n         * Render selected options\n         * @private\n         */\n        _renderMultiselect: function () {\n            var that = this;\n\n            this.element.wrap(this.options.multiSuggestWrapper);\n            this.elementWrapper = this.element.closest('[data-role=\"parent-choice-element\"]');\n            $(function () {\n                that._getOptions()\n                    .each(function (i, option) {\n                        option = $(option);\n                        that._createOption({\n                            id: option.val(),\n                            label: option.text()\n                        });\n                    });\n            });\n        },\n\n        /**\n         * @return {Array} array of DOM-elements\n         * @private\n         */\n        _getOptions: function () {\n            return this.valueField.find('option');\n        },\n\n        /**\n         * @override\n         */\n        _bind: function () {\n            this._super();\n\n            if (this.options.multiselect) {\n                this._on({\n                    /**\n                     * @param {jQuery.Event} event\n                     */\n                    keydown: function (event) {\n                        if (event.keyCode === $.ui.keyCode.BACKSPACE) {\n                            if (!this._value()) {\n                                this._removeLastAdded(event);\n                            }\n                        }\n                    },\n                    removeOption: this.removeOption\n                });\n            }\n        },\n\n        /**\n         * @param {Array} items\n         * @return {Array}\n         * @private\n         */\n        _filterSelected: function (items) {\n            var options = this._getOptions();\n\n            return $.grep(items, function (value) {\n                var itemSelected = false;\n\n                $.each(options, function () {\n                    if (value.id == $(this).val()) { //eslint-disable-line eqeqeq\n                        itemSelected = true;\n                    }\n                });\n\n                return !itemSelected;\n            });\n        },\n\n        /**\n         * @override\n         */\n        _processResponse: function (e, items, context) {\n            if (this.options.multiselect) {\n                items = this._filterSelected(items, context);\n            }\n            this._superApply([e, items, context]);\n        },\n\n        /**\n         * @override\n         */\n        _prepareValueField: function () {\n            this._super();\n\n            if (this.options.multiselect && !this.options.valueField && this.options.selectedItems) {\n                $.each(this.options.selectedItems, $.proxy(function (i, item) {\n                    this._addOption(item);\n                }, this));\n            }\n        },\n\n        /**\n         * If \"multiselect\" option is set, then do not need to clear value for hidden select, to avoid losing of\n         *      previously selected items\n         * @override\n         */\n        _resetSuggestValue: function () {\n            if (!this.options.multiselect) {\n                this._super();\n            }\n        },\n\n        /**\n         * @override\n         */\n        _createValueField: function () {\n            if (this.options.multiselect) {\n                return $('<select></select>', {\n                    type: 'hidden',\n                    multiple: 'multiple'\n                });\n            }\n\n            return this._super();\n        },\n\n        /**\n         * @override\n         */\n        _selectItem: function (e) {\n            if (this.options.multiselect) {\n                if (this._focused) {\n                    this._selectedItem = this._focused;\n\n                    /* eslint-disable max-depth */\n                    if (this._selectedItem !== this._nonSelectedItem) {\n                        this._term = '';\n                        this.element.val(this._term);\n\n                        if (this._isItemSelected(this._selectedItem)) {\n                            $(e.target).removeClass(this.options.selectedClass);\n                            this.removeOption(e, this._selectedItem);\n                            this._selectedItem = this._nonSelectedItem;\n                        } else {\n                            $(e.target).addClass(this.options.selectedClass);\n                            this._addOption(e, this._selectedItem);\n                        }\n                    }\n\n                    /* eslint-enable max-depth */\n                }\n                this.close(e);\n            } else {\n                this._superApply(arguments);\n            }\n        },\n\n        /**\n         * @override\n         */\n        _isItemSelected: function (item) {\n            if (this.options.multiselect) {\n                return this.valueField.find('option[value=' + item.id + ']').length > 0;\n            }\n\n            return this._superApply(arguments);\n        },\n\n        /**\n         *\n         * @param {Object} item\n         * @return {Element}\n         * @private\n         */\n        _createOption: function (item) {\n            var option = this._getOption(item);\n\n            if (!option.length) {\n                option = $('<option>', {\n                    value: item.id,\n                    selected: true\n                }).text(item.label);\n            }\n\n            return option.data('renderedOption', this._renderOption(item));\n        },\n\n        /**\n         * Add selected item in to select options\n         * @param {Object} e - event object\n         * @param {*} item\n         * @private\n         */\n        _addOption: function (e, item) {\n            this.valueField.append(this._createOption(item).data('selectTarget', $(e.target)));\n        },\n\n        /**\n         * @param {Object|Element} item\n         * @return {Element}\n         * @private\n         */\n        _getOption: function (item) {\n            return $(item).prop('tagName') ?\n                $(item) :\n                this.valueField.find('option[value=' + item.id + ']');\n        },\n\n        /**\n         * Remove last added option\n         * @private\n         * @param {Object} e - event object\n         */\n        _removeLastAdded: function (e) {\n            var lastAdded = this._getOptions().last();\n\n            if (lastAdded.length) {\n                this.removeOption(e, lastAdded);\n            }\n        },\n\n        /**\n         * Remove item from select options\n         * @param {Object} e - event object\n         * @param {Object} item\n         * @private\n         */\n        removeOption: function (e, item) {\n            var option = this._getOption(item),\n                selectTarget = option.data('selectTarget');\n\n            if (selectTarget && selectTarget.length) {\n                selectTarget.removeClass(this.options.selectedClass);\n            }\n\n            option.data('renderedOption').remove();\n            option.remove();\n        },\n\n        /**\n         * Render visual element of selected item\n         * @param {Object} item - selected item\n         * @private\n         */\n        _renderOption: function (item) {\n            var tmpl = this.choiceTmpl({\n                text: item.label\n            });\n\n            return $(tmpl)\n                .insertBefore(this.elementWrapper)\n                .trigger('contentUpdated')\n                .on('removeOption', $.proxy(function (e) {\n                    this.removeOption(e, item);\n                }, this));\n        }\n    });\n\n    return $.mage.suggest;\n});\n","mage/backend/button.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], function ($) {\n    'use strict';\n\n    $.widget('ui.button', $.ui.button, {\n        options: {\n            eventData: {},\n            waitTillResolved: true\n        },\n\n        /**\n         * Button creation.\n         * @protected\n         */\n        _create: function () {\n            if (this.options.event) {\n                this.options.target = this.options.target || this.element;\n                this._bind();\n            }\n\n            this._super();\n        },\n\n        /**\n         * Bind handler on button click.\n         * @protected\n         */\n        _bind: function () {\n            this.element\n                .off('click.button')\n                .on('click.button', $.proxy(this._click, this));\n        },\n\n        /**\n         * Button click handler.\n         * @protected\n         */\n        _click: function () {\n            var options = this.options;\n\n            $(options.target).trigger(options.event, [options.eventData]);\n        }\n    });\n\n    return $.ui.button;\n});\n","mage/backend/form.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'jquery',\n    'jquery/ui'\n], function ($) {\n    'use strict';\n\n    $.widget('mage.form', {\n        options: {\n            handlersData: {\n                save: {},\n                saveAndContinueEdit: {\n                    action: {\n                        args: {\n                            back: 'edit'\n                        }\n                    }\n                },\n                preview: {\n                    target: '_blank'\n                }\n            }\n        },\n\n        /**\n         * Form creation\n         * @protected\n         */\n        _create: function () {\n            this._bind();\n        },\n\n        /**\n         * Set form attributes to initial state\n         * @protected\n         */\n        _rollback: function () {\n            if (this.oldAttributes) {\n                this.element.prop(this.oldAttributes);\n            }\n        },\n\n        /**\n         * Check if field value is changed\n         * @protected\n         * @param {Object} e - event object\n         */\n        _changesObserver: function (e) {\n            var target = $(e.target),\n                changed;\n\n            if (e.type === 'focus' || e.type === 'focusin') {\n                this.currentField = {\n                    statuses: {\n                        checked: target.is(':checked'),\n                        selected: target.is(':selected')\n                    },\n                    val: target.val()\n                };\n\n            } else {\n                if (this.currentField) { //eslint-disable-line no-lonely-if\n                    changed = target.val() !== this.currentField.val ||\n                        target.is(':checked') !== this.currentField.statuses.checked ||\n                        target.is(':selected') !== this.currentField.statuses.selected;\n\n                    if (changed) { //eslint-disable-line max-depth\n                        target.trigger('changed');\n                    }\n                }\n            }\n        },\n\n        /**\n         * Get array with handler names\n         * @protected\n         * @return {Array} Array of handler names\n         */\n        _getHandlers: function () {\n            var handlers = [];\n\n            $.each(this.options.handlersData, function (key) {\n                handlers.push(key);\n            });\n\n            return handlers;\n        },\n\n        /**\n         * Store initial value of form attribute\n         * @param {String} attrName - name of attribute\n         * @protected\n         */\n        _storeAttribute: function (attrName) {\n            var prop;\n\n            this.oldAttributes = this.oldAttributes || {};\n\n            if (!this.oldAttributes[attrName]) {\n                prop = this.element.attr(attrName);\n                this.oldAttributes[attrName] = prop ? prop : '';\n            }\n        },\n\n        /**\n         * Bind handlers\n         * @protected\n         */\n        _bind: function () {\n            this.element\n                .on(this._getHandlers().join(' '), $.proxy(this._submit, this))\n                .on('focus blur focusin focusout', $.proxy(this._changesObserver, this));\n        },\n\n        /**\n         * Get action url for form\n         * @param {Object|String} data - object with parameters for action url or url string\n         * @return {String} action url\n         */\n        _getActionUrl: function (data) {\n            if (typeof data === 'object') {\n                return this._buildURL(this.oldAttributes.action, data.args);\n            }\n\n            return typeof data === 'string' ? data : this.oldAttributes.action;\n        },\n\n        /**\n         * Add additional parameters into URL\n         * @param {String} url - original url\n         * @param {Object} params - object with parameters for action url\n         * @return {String} action url\n         * @private\n         */\n        _buildURL: function (url, params) {\n            var concat = /\\?/.test(url) ? ['&', '='] : ['/', '/'];\n\n            url = url.replace(/[\\/&]+$/, '');\n            $.each(params, function (key, value) {\n                url += concat[0] + key + concat[1] + window.encodeURIComponent(value);\n            });\n\n            return url + (concat[0] === '/' ? '/' : '');\n        },\n\n        /**\n         * Prepare data for form attributes\n         * @protected\n         * @param {Object} data\n         * @return {Object}\n         */\n        _processData: function (data) {\n            $.each(data, $.proxy(function (attrName, attrValue) {\n                this._storeAttribute(attrName);\n\n                if (attrName === 'action') {\n                    data[attrName] = this._getActionUrl(attrValue);\n                }\n            }, this));\n\n            return data;\n        },\n\n        /**\n         * Get additional data before form submit\n         * @protected\n         * @param {String} handlerName\n         * @param {Object} data\n         */\n        _beforeSubmit: function (handlerName, data) {\n            var submitData = {},\n                event = new $.Event('beforeSubmit');\n\n            this.element.trigger(event, [submitData, handlerName]);\n            data = $.extend(\n                true, {},\n                this.options.handlersData[handlerName] || {},\n                submitData,\n                data\n            );\n            this.element.prop(this._processData(data));\n\n            return !event.isDefaultPrevented();\n        },\n\n        /**\n         * Submit the form\n         * @param {Object} e - event object\n         * @param {Object} data - event data object\n         */\n        _submit: function (e, data) {\n            this._rollback();\n\n            if (this._beforeSubmit(e.type, data) !== false) {\n                this.element.trigger('submit', e);\n            }\n        }\n    });\n\n    return $.mage.form;\n});\n","mage/backend/editablemultiselect.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated since version 2.2.0\n */\n/* global EditableMultiselect */\n/* eslint-disable strict */\ndefine([\n    'jquery',\n    'Magento_Ui/js/modal/alert',\n    'Magento_Ui/js/modal/confirm',\n    'jquery/editableMultiselect/js/jquery.editable',\n    'jquery/editableMultiselect/js/jquery.multiselect'\n], function ($, alert, confirm) {\n    /**\n     * Editable multiselect wrapper for multiselects\n     * This class is defined in global scope ('var' is not needed).\n     *\n     *  @param {Object} settings - settings object.\n     *  @param {String} settings.add_button_caption - caption of the 'Add New Value' button\n     *  @param {String} settings.new_url - URL to which new request has to be submitted\n     *  @param {String} settings.save_url - URL to which save request has to be submitted\n     *  @param {String} settings.delete_url - URL to which delete request has to be submitted\n     *  @param {String} settings.delete_confirm_message - confirmation message that is shown to user during\n     *      delete operation\n     *  @param {String} settings.target_select_id - HTML ID of target select element\n     *  @param {Hash} settings.submit_data - extra parameters to send with new/edit/delete requests\n     *  @param {String} settings.entity_value_name - name of the request parameter that represents select option text\n     *  @param {String} settings.entity_id_name - name of the request parameter that represents select option value\n     *  @param {Boolean} settings.is_entry_editable - flag that shows if user can add/edit/remove data\n     *\n     * @constructor\n     */\n    window.EditableMultiselect = function (settings) {\n\n        this.settings = settings || {};\n        this.addButtonCaption = this.settings['add_button_caption'] || 'Add new value';\n        this.newUrl = this.settings['new_url'];\n        this.saveUrl = this.settings['save_url'];\n        this.deleteUrl = this.settings['delete_url'];\n        this.deleteConfirmMessage = this.settings['delete_confirm_message'];\n        this.targetSelectId = this.settings['target_select_id'];\n        this.submitData = this.settings['submit_data'] || {};\n        this.entityIdName = this.settings['entity_id_name'] || 'entity_id';\n        this.entityValueName = this.settings['entity_value_name'] || 'entity_value';\n        this.isEntityEditable = this.settings['is_entity_editable'] || false;\n\n        /**\n         * Initialize editable multiselect (make it visible in UI)\n         */\n        EditableMultiselect.prototype.init = function () {\n            var self = this,\n                mselectOptions = {\n                    addText: this.addButtonCaption,\n\n                    /**\n                     * @param {*} value\n                     * @param {*} options\n                     */\n                    mselectInputSubmitCallback: function (value, options) {\n                        self.createEntity(value, options);\n                    }\n                },\n                mselectList;\n\n            if (!this.isEntityEditable) {\n                // Override default layout of editable multiselect\n                mselectOptions.layout = '<section class=\"block %mselectListClass%\">' +\n                    '<div class=\"block-content\"><div class=\"%mselectItemsWrapperClass%\">' +\n                    '%items%' +\n                    '</div></div>' +\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            }\n\n            $('#' + this.targetSelectId).multiselect(mselectOptions);\n\n            // Make multiselect editable if needed\n            if (this.isEntityEditable) {\n                this.makeMultiselectEditable();\n\n                // Root element of HTML markup that represents select element in UI\n                mselectList = $('#' + this.targetSelectId).next();\n                this.attachEventsToControls(mselectList);\n            }\n        };\n\n        /**\n         * Attach required event handlers to control elements of editable multiselect\n         *\n         * @param {Object} mselectList\n         */\n        EditableMultiselect.prototype.attachEventsToControls = function (mselectList) {\n            mselectList.on('click.mselect-delete', '.mselect-delete', {\n                container: this\n            }, function (event) {\n                // Pass the clicked button to container\n                event.data.container.deleteEntity({\n                    'delete_button': this\n                });\n            });\n\n            mselectList.on('click.mselect-checked', '.mselect-list-item input', {\n                container: this\n            }, function (event) {\n                var el = $(this),\n                    checkedClassName = 'mselect-checked';\n\n                el[el.is(':checked') ? 'addClass' : 'removeClass'](checkedClassName);\n                event.data.container.makeMultiselectEditable();\n            });\n\n            mselectList.on('click.mselect-edit', '.mselect-edit', {\n                container: this\n            }, function (event) {\n                event.data.container.makeMultiselectEditable();\n                $(this).parent().find('label span').trigger('dblclick');\n            });\n        };\n\n        /**\n         * Make multiselect editable\n         */\n        EditableMultiselect.prototype.makeMultiselectEditable = function () {\n            var entityIdName = this.entityIdName,\n                entityValueName = this.entityValueName,\n                selectList = $('#' + this.targetSelectId).next();\n\n            selectList.find('.mselect-list-item:not(.mselect-list-item-not-editable) label span').editable(this.saveUrl,\n            {\n                type: 'text',\n                submit: '<button class=\"mselect-save\" title=\"Save\" type=\"submit\"></button>',\n                cancel: '<span class=\"mselect-cancel\" title=\"Cancel\"></span>',\n                event: 'dblclick',\n                placeholder: '',\n\n                /**\n                 * Is checked.\n                 */\n                isChecked: function () {\n                    var that = $(this),\n                        checked;\n\n                    if (!that.closest('.mselect-list-item').hasClass('mselect-disabled')) {\n                        checked = that.parent().find('[type=checkbox]').prop('disabled');\n                        that.parent().find('[type=checkbox]').prop({\n                            disabled: !checked\n                        });\n                    }\n                },\n\n                /**\n                 * @param {*} value\n                 * @param {Object} sett\n                 * @return {*}\n                 */\n                data: function (value, sett) {\n                    var retval;\n\n                    sett.isChecked.apply(this, [sett]);\n\n                    if (typeof value === 'string') {\n                        retval = value.unescapeHTML();\n\n                        return retval;\n                    }\n\n                    return value;\n                },\n                submitdata: this.submitData,\n                onblur: 'cancel',\n                name: entityValueName,\n                ajaxoptions: {\n                    dataType: 'json'\n                },\n\n                /**\n                 * @param {Object} sett\n                 * @param {*} original\n                 */\n                onsubmit: function (sett, original) {\n                    var select = $(original).closest('.mselect-list').prev(),\n                        current = $(original).closest('.mselect-list-item').index(),\n                        entityId = select.find('option').eq(current).val(),\n                        entityInfo = {};\n\n                    entityInfo[entityIdName] = entityId;\n                    sett.submitdata = $.extend(sett.submitdata || {}, entityInfo);\n                },\n\n                /**\n                 * @param {Object} result\n                 * @param {Object} sett\n                 */\n                callback: function (result, sett) {\n                    var select, current;\n\n                    sett.isChecked.apply(this, [sett]);\n                    select = $(this).closest('.mselect-list').prev();\n                    current = $(this).closest('.mselect-list-item').index();\n\n                    if (result.success) {\n                        if (typeof result[entityValueName] === 'string') {\n                            select.find('option').eq(current).val(result[entityIdName]).text(result[entityValueName]);\n                            $(this).html(result[entityValueName].escapeHTML());\n                        }\n                    } else {\n                        alert({\n                            content: result['error_message']\n                        });\n                    }\n                }\n            });\n        };\n\n        /**\n         * Callback function that is called when admin adds new value to select\n         *\n         * @param {*} value\n         * @param {Object} options - list of settings of multiselect\n         */\n        EditableMultiselect.prototype.createEntity = function (value, options) {\n            var select, entityIdName, entityValueName, entityInfo, postData, ajaxOptions;\n\n            if (!value) {\n                return;\n            }\n\n            select = $('#' + this.targetSelectId),\n            entityIdName = this.entityIdName,\n            entityValueName = this.entityValueName,\n            entityInfo = {};\n            entityInfo[entityIdName] = null;\n            entityInfo[entityValueName] = value;\n\n            postData = $.extend(entityInfo, this.submitData);\n\n            ajaxOptions = {\n                type: 'POST',\n                data: postData,\n                dataType: 'json',\n                url: this.newUrl,\n\n                /**\n                 * @param {Object} result\n                 */\n                success: function (result) {\n                    var resultEntityValueName, mselectItemHtml, sectionBlock, itemsWrapper, inputSelector;\n\n                    if (result.success) {\n                        resultEntityValueName = '';\n\n                        if (typeof result[entityValueName] === 'string') {\n                            resultEntityValueName = result[entityValueName].escapeHTML();\n                        } else {\n                            resultEntityValueName = result[entityValueName];\n                        }\n                        // Add item to initial select element\n                        select.append('<option value=\"' + result[entityIdName] + '\" selected=\"selected\">' +\n                        resultEntityValueName + '</option>');\n                        // Add editable multiselect item\n                        mselectItemHtml = $(options.item.replace(/%value%|%label%/gi, resultEntityValueName)\n                            .replace(/%mselectDisabledClass%|%iseditable%|%isremovable%/gi, '')\n                            .replace(/%mselectListItemClass%/gi, options.mselectListItemClass))\n                            .find('[type=checkbox]')\n                            .addClass(options.mselectCheckedClass)\n                            .prop('checked', true)\n                            .end();\n                        sectionBlock = select.nextAll('section.block:first');\n                        itemsWrapper = sectionBlock.find('.' + options.mselectItemsWrapperClass + '');\n\n                        if (itemsWrapper.children('.' + options.mselectListItemClass + '').length) {\n                            itemsWrapper.children('.' + options.mselectListItemClass + ':last').after(mselectItemHtml);\n                        } else {\n                            itemsWrapper.prepend(mselectItemHtml);\n                        }\n                        // Trigger blur event on input field, that is used to add new value, to hide it\n                        inputSelector = '.' + options.mselectInputContainerClass + ' [type=text].' +\n                            options.mselectInputClass + '';\n                        sectionBlock.find(inputSelector).trigger('blur');\n                    } else {\n                        alert({\n                            content: result['error_message']\n                        });\n                    }\n                }\n            };\n            $.ajax(ajaxOptions);\n        };\n\n        /**\n         * Callback function that is called when user tries to delete value from select\n         *\n         * @param {Object} options\n         */\n        EditableMultiselect.prototype.deleteEntity = function (options) {\n            var self = this;\n\n            if (options['delete_button']) {\n                confirm({\n                    content: this.deleteConfirmMessage,\n                    actions: {\n                        /**\n                         * Confirm.\n                         */\n                        confirm: function () {\n                            // Button that has been clicked\n                            var deleteButton = $(options['delete_button']),\n                                index = deleteButton.parent().index(),\n                                select = deleteButton.closest('.mselect-list').prev(),\n                                entityId = select.find('option').eq(index).val(),\n                                entityInfo = {},\n                                postData, ajaxOptions;\n\n                            entityInfo[self.entityIdName] = entityId;\n                            postData = $.extend(entityInfo, self.submitData);\n\n                            ajaxOptions = {\n                                type: 'POST',\n                                data: postData,\n                                dataType: 'json',\n                                url: self.deleteUrl,\n\n                                /**\n                                 * @param {Object} result\n                                 */\n                                success: function (result) {\n                                    if (result.success) {\n                                        deleteButton.parent().remove();\n                                        select.find('option').eq(index).remove();\n                                    } else {\n                                        alert({\n                                            content: result['error_message']\n                                        });\n                                    }\n                                }\n                            };\n                            $.ajax(ajaxOptions);\n                        }\n                    }\n                });\n            }\n        };\n    };\n});\n","mage/backend/notification.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'mage/template',\n    'jquery/ui'\n], function ($, mageTemplate) {\n    'use strict';\n\n    $.widget('mage.notification', {\n        options: {\n            templates: {\n                global: '<div data-role=\"messages\" id=\"messages\">' +\n                    '<div class=\"message <% if (data.error) { %>error<% } %>\"><div><%- data.message %></div></div>' +\n                '</div>',\n                error: '<div data-role=\"messages\" id=\"messages\">' +\n                    '<div class=\"messages\"><div class=\"message message-error error\">' +\n                        '<div data-ui-id=\"messages-message-error\"><%- data.message %></div></div>' +\n                    '</div></div>'\n            }\n        },\n        placeholder: '[data-role=messages]',\n\n        /**\n         * Notification creation\n         * @protected\n         */\n        _create: function () {\n            $(document).on('ajaxComplete ajaxError', $.proxy(this._add, this));\n        },\n\n        /**\n         * Add new message\n         * @protected\n         * @param {Object} event - object\n         * @param {Object} jqXHR - The jQuery XMLHttpRequest object returned by $.ajax()\n         */\n        _add: function (event, jqXHR) {\n            var response;\n\n            try {\n                response = JSON.parse(jqXHR.responseText);\n\n                if (response && response.error && response['html_message']) {\n                    $(this.placeholder).html(response['html_message']);\n                }\n            } catch (e) {}\n        },\n\n        /**\n         * Adds new message.\n         *\n         * @param {Object} data - Data with a message to be displayed.\n         */\n        add: function (data) {\n            var template = data.error ? this.options.templates.error : this.options.templates.global,\n                message = mageTemplate(template, {\n                    data: data\n                }),\n                messageContainer;\n\n            if (typeof data.insertMethod === 'function') {\n                data.insertMethod(message);\n            } else {\n                messageContainer = data.messageContainer || this.placeholder;\n                $(messageContainer).prepend(message);\n            }\n\n            return this;\n        },\n\n        /**\n         * Removes error messages.\n         */\n        clear: function () {\n            $(this.placeholder).html('');\n        }\n    });\n\n    return $.mage.notification;\n});\n","mage/backend/bootstrap.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* global FORM_KEY */\ndefine([\n    'jquery',\n    'mage/apply/main',\n    'mage/backend/notification',\n    'Magento_Ui/js/lib/knockout/bootstrap',\n    'mage/mage',\n    'mage/translate'\n], function ($, mage, notification) {\n    'use strict';\n\n    var bootstrap;\n\n    $.ajaxSetup({\n        /*\n         * @type {string}\n         */\n        type: 'POST',\n\n        /**\n         * Ajax before send callback.\n         *\n         * @param {Object} jqXHR - The jQuery XMLHttpRequest object returned by $.ajax()\n         * @param {Object} settings\n         */\n        beforeSend: function (jqXHR, settings) {\n            var formKey = typeof FORM_KEY !== 'undefined' ? FORM_KEY : null;\n\n            if (!settings.url.match(new RegExp('[?&]isAjax=true',''))) {\n                settings.url = settings.url.match(\n                    new RegExp('\\\\?', 'g')) ?\n                    settings.url + '&isAjax=true' :\n                    settings.url + '?isAjax=true';\n            }\n\n            if (!settings.data) {\n                settings.data = {\n                    'form_key': formKey\n                };\n            } else if (typeof settings.data === 'string' &&\n                settings.data.indexOf('form_key=') === -1) {\n                settings.data += '&' + $.param({\n                    'form_key': formKey\n                });\n            } else if ($.isPlainObject(settings.data) && !settings.data['form_key']) {\n                settings.data['form_key'] = formKey;\n            }\n        },\n\n        /**\n         * Ajax complete callback.\n         *\n         * @param {Object} jqXHR - The jQuery XMLHttpRequest object returned by $.ajax()\n         */\n        complete: function (jqXHR) {\n            var jsonObject;\n\n            if (jqXHR.readyState === 4) {\n                try {\n                    jsonObject = JSON.parse(jqXHR.responseText);\n\n                    if (jsonObject.ajaxExpired && jsonObject.ajaxRedirect) { //eslint-disable-line max-depth\n                        window.location.replace(jsonObject.ajaxRedirect);\n                    }\n                } catch (e) {}\n            }\n        },\n\n        /**\n         * Error callback.\n         */\n        error: function () {\n            $('body').notification('clear')\n                .notification('add', {\n                    error: true,\n                    message: $.mage.__(\n                        'A technical problem with the server created an error. ' +\n                        'Try again to continue what you were doing. If the problem persists, try again later.'\n                    ),\n\n                    /**\n                     * @param {String} message\n                     */\n                    insertMethod: function (message) {\n                        var $wrapper = $('<div></div>').html(message);\n\n                        $('.page-main-actions').after($wrapper);\n                    }\n                });\n        }\n    });\n\n    /**\n     * Bootstrap application.\n     */\n    bootstrap = function () {\n        /**\n         * Init all components defined via data-mage-init attribute\n         * and subscribe init action on contentUpdated event\n         */\n        mage.apply();\n\n        /*\n         * Initialization of notification widget\n         */\n        notification({}, $('body'));\n    };\n\n    $(bootstrap);\n});\n","mage/backend/floating-header.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], function ($) {\n    'use strict';\n\n    $.widget('mage.floatingHeader', {\n        options: {\n            placeholderAttrs: {\n                'class': 'page-actions-placeholder'\n            },\n            fixedClass: '_fixed',\n            hiddenClass: '_hidden',\n            title: '.page-title-wrapper .page-title',\n            pageMainActions: '.page-main-actions',\n            contains: '[data-role=modal]'\n        },\n\n        /**\n         * Widget initialization\n         * @private\n         */\n        _create: function () {\n            var title = $(this.options.title).text(),\n                wrapped = this.element.find('.page-actions-buttons').children();\n\n            if (this.element.parents(this.options.contains).length) {\n                return this;\n            }\n\n            this._setVars();\n            this._bind();\n            this.element.find('script').remove();\n\n            if (wrapped.length) {\n                wrapped\n                    .unwrap()   // .page-actions-buttons\n                    .unwrap();  // .page-actions-inner\n            }\n            this.element.wrapInner($('<div></div>', {\n                'class': 'page-actions-buttons'\n            }));\n            this.element.wrapInner($('<div></div>', {\n                'class': 'page-actions-inner', 'data-title': title\n            }));\n            this.element.removeClass('floating-header');\n        },\n\n        /**\n         * Set privat variables on load, for performance purposes\n         * @private\n         */\n        _setVars: function () {\n            this._placeholder = this.element.before($('<div/>', this.options.placeholderAttrs)).prev();\n            this._offsetTop = this._placeholder.offset().top;\n            this._height = this.element\n                .parents(this.options.pageMainActions)\n                .outerHeight();\n        },\n\n        /**\n         * Event binding, will monitor scroll and resize events (resize events left for backward compat)\n         * @private\n         */\n        _bind: function () {\n            this._on(window, {\n                scroll: this._handlePageScroll,\n                resize: this._handlePageScroll\n            });\n        },\n\n        /**\n         * Event handler for setting fixed positioning\n         * @private\n         */\n        _handlePageScroll: function () {\n            var isActive = $(window).scrollTop() > this._offsetTop;\n\n            if (isActive) {\n                this.element\n                    .addClass(this.options.fixedClass)\n                    .parents(this.options.pageMainActions)\n                    .addClass(this.options.hiddenClass);\n            } else {\n                this.element\n                    .removeClass(this.options.fixedClass)\n                    .parents(this.options.pageMainActions)\n                    .removeClass(this.options.hiddenClass);\n            }\n\n            this._placeholder.height(isActive ? this._height : '');\n        },\n\n        /**\n         * Widget destroy functionality\n         * @private\n         */\n        _destroy: function () {\n            if (this._placeholder) {\n                this._placeholder.remove();\n            }\n            this._off($(window));\n        }\n    });\n\n    return $.mage.floatingHeader;\n});\n","mage/backend/validation.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* global BASE_URL, alertAlreadyDisplayed */\ndefine([\n    'jquery',\n    'underscore',\n    'Magento_Ui/js/modal/alert',\n    'jquery/ui',\n    'jquery/validate',\n    'mage/translate',\n    'mage/validation'\n], function ($, _, alert) {\n    'use strict';\n\n    $.extend(true, $.validator.prototype, {\n        /**\n         * Focus invalid fields\n         */\n        focusInvalid: function () {\n            if (this.settings.focusInvalid) {\n                try {\n                    $(this.errorList.length && this.errorList[0].element || [])\n                        .trigger('focus')\n                        .trigger('focusin');\n                } catch (e) {\n                    // ignore IE throwing errors when focusing hidden elements\n                }\n            }\n        },\n\n        /**\n         * Elements.\n         */\n        elements: function () {\n            var validator = this,\n                rulesCache = {};\n\n            // select all valid inputs inside the form (no submit or reset buttons)\n            return $(this.currentForm)\n                .find('input, select, textarea')\n                .not(this.settings.forceIgnore)\n                .not(':submit, :reset, :image, [disabled]')\n                .not(this.settings.ignore)\n                .filter(function () {\n                    if (!this.name && validator.settings.debug && window.console) {\n                        console.error('%o has no name assigned', this);\n                    }\n\n                    // select only the first element for each name, and only those with rules specified\n                    if (this.name in rulesCache || !validator.objectLength($(this).rules())) {\n                        return false;\n                    }\n\n                    rulesCache[this.name] = true;\n\n                    return true;\n                });\n        }\n    });\n\n    $.extend($.fn, {\n        /**\n         * ValidationDelegate overridden for those cases where the form is located in another form,\n         *     to avoid not correct working of validate plug-in\n         * @override\n         * @param {String} delegate - selector, if event target matched against this selector,\n         *     then event will be delegated\n         * @param {String} type - event type\n         * @param {Function} handler - event handler\n         * @return {Element}\n         */\n        validateDelegate: function (delegate, type, handler) {\n            return this.on(type, $.proxy(function (event) {\n                var target = $(event.target),\n                    form = target[0].form;\n\n                if (form && $(form).is(this) && $.data(form, 'validator') && target.is(delegate)) {\n                    return handler.apply(target, arguments);\n                }\n            }, this));\n        }\n    });\n\n    $.widget('mage.validation', $.mage.validation, {\n        options: {\n            messagesId: 'messages',\n            forceIgnore: '',\n            ignore: ':disabled, .ignore-validate, .no-display.template, ' +\n                ':disabled input, .ignore-validate input, .no-display.template input, ' +\n                ':disabled select, .ignore-validate select, .no-display.template select, ' +\n                ':disabled textarea, .ignore-validate textarea, .no-display.template textarea',\n            errorElement: 'label',\n            errorUrl: typeof BASE_URL !== 'undefined' ? BASE_URL : null,\n\n            /**\n             * @param {HTMLElement} element\n             */\n            highlight: function (element) {\n                if ($.validator.defaults.highlight && typeof $.validator.defaults.highlight === 'function') {\n                    $.validator.defaults.highlight.apply(this, arguments);\n                }\n                $(element).trigger('highlight.validate');\n            },\n\n            /**\n             * @param {HTMLElement} element\n             */\n            unhighlight: function (element) {\n                if ($.validator.defaults.unhighlight && typeof $.validator.defaults.unhighlight === 'function') {\n                    $.validator.defaults.unhighlight.apply(this, arguments);\n                }\n                $(element).trigger('unhighlight.validate');\n            }\n        },\n\n        /**\n         * Validation creation\n         * @protected\n         */\n        _create: function () {\n            if (!this.options.submitHandler && typeof this.options.submitHandler !== 'function') {\n                if (!this.options.frontendOnly && this.options.validationUrl) {\n                    this.options.submitHandler = $.proxy(this._ajaxValidate, this);\n                } else {\n                    this.options.submitHandler = $.proxy(this._submit, this);\n                }\n            }\n            this.element.on('resetElement', function (e) {\n                $(e.target).rules('remove');\n            });\n            this._super('_create');\n        },\n\n        /**\n         * ajax validation\n         * @protected\n         */\n        _ajaxValidate: function () {\n            $.ajax({\n                url: this.options.validationUrl,\n                type: 'POST',\n                dataType: 'json',\n                data: this.element.serialize(),\n                context: $('body'),\n                success: $.proxy(this._onSuccess, this),\n                error: $.proxy(this._onError, this),\n                showLoader: true,\n                dontHide: false\n            });\n        },\n\n        /**\n         * Process ajax success.\n         *\n         * @protected\n         * @param {Object} response\n         */\n        _onSuccess: function (response) {\n            if (!response.error) {\n                this._submit();\n            } else {\n                this._showErrors(response);\n                $(this.element[0]).trigger('afterValidate.error');\n                $('body').trigger('processStop');\n            }\n        },\n\n        /**\n         * Submitting a form.\n         * @private\n         */\n        _submit: function () {\n            $(this.element[0]).trigger('afterValidate.beforeSubmit');\n            this.element[0].submit();\n        },\n\n        /**\n         * Displays errors after backend validation.\n         *\n         * @param {Object} data - Data that came from backend.\n         */\n        _showErrors: function (data) {\n            $('body').notification('clear')\n                .notification('add', {\n                    error: data.error,\n                    message: data.message,\n\n                    /**\n                     * @param {*} message\n                     */\n                    insertMethod: function (message) {\n                        $('.messages:first').html(message);\n                    }\n                });\n        },\n\n        /**\n         * Tries to retrieve element either by id or by inputs' name property.\n         * @param {String} code - String to search by.\n         * @returns {jQuery} jQuery element.\n         */\n        _getByCode: function (code) {\n            var parent = this.element[0],\n                element;\n\n            element = parent.querySelector('#' + code) || parent.querySelector('input[name=' + code + ']');\n\n            return $(element);\n        },\n\n        /**\n         * Process ajax error\n         * @protected\n         */\n        _onError: function () {\n            $(this.element[0]).trigger('afterValidate.error');\n            $('body').trigger('processStop');\n\n            if (this.options.errorUrl) {\n                location.href = this.options.errorUrl;\n            }\n        }\n    });\n\n    _.each({\n        'validate-greater-zero-based-on-option': [\n            function (v, el) {\n                var optionType = $(el)\n                    .closest('.form-list')\n                    .prev('.fieldset-alt')\n                    .find('select.select-product-option-type'),\n                    optionTypeVal = optionType.val();\n\n                v = Number(v) || 0;\n\n                if (optionType && (optionTypeVal == 'checkbox' || optionTypeVal == 'multi') && v <= 0) { //eslint-disable-line\n                    return false;\n                }\n\n                return true;\n            },\n            $.mage.__('Please enter a number greater 0 in this field.')\n        ],\n        'validate-rating': [\n            function () {\n                var ratings = $('#detailed_rating').find('.field-rating'),\n                    noError = true;\n\n                ratings.each(function (index, rating) {\n                    noError = noError && $(rating).find('input:checked').length > 0;\n                });\n\n                return noError;\n            },\n            $.mage.__('Please select one of each ratings above.')\n        ],\n        'validate-downloadable-file': [\n            function (v, element) {\n                var elmParent = $(element).parent(),\n                    linkType = elmParent.find('input[value=\"file\"]'),\n                    newFileContainer;\n\n                if (linkType.is(':checked') && (v === '' || v === '[]')) {\n                    newFileContainer = elmParent.find('.new-file');\n\n                    if (!alertAlreadyDisplayed && (newFileContainer.empty() || newFileContainer.is(':visible'))) {\n                        window.alertAlreadyDisplayed = true;\n                        alert({\n                            content: $.mage.__('There are files that were selected but not uploaded yet. ' +\n                            'Please upload or remove them first')\n                        });\n                    }\n\n                    return false;\n                }\n\n                return true;\n            },\n            'Please upload a file.'\n        ],\n        'validate-downloadable-url': [\n            function (v, element) {\n                var linkType = $(element).parent().find('input[value=\"url\"]');\n\n                if (linkType.is(':checked') && v === '') {\n                    return false;\n                }\n\n                return true;\n            },\n            $.mage.__('Please specify Url.')\n        ]\n    }, function (rule, i) {\n        rule.unshift(i);\n        $.validator.addMethod.apply($.validator, rule);\n    });\n\n    return $.mage.validation;\n});\n","mage/backend/menu.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], function ($) {\n    'use strict';\n\n    $.widget('mage.menu', {\n        widgetEventPrefix: 'menu',\n        version: '1.10.1',\n        defaultElement: '<ul>',\n        delay: 300,\n        options: {\n            icons: {\n                submenu: 'ui-icon-carat-1-e'\n            },\n            menus: 'ul',\n            position: {\n                my: 'left top',\n                at: 'right top'\n            },\n            role: 'menu',\n\n            // callbacks\n            blur: null,\n            focus: null,\n            select: null\n        },\n\n        /**\n         * @private\n         */\n        _create: function () {\n            this.activeMenu = this.element;\n            // flag used to prevent firing of the click handler\n            // as the event bubbles up through nested menus\n            this.mouseHandled = false;\n            this.element\n                .uniqueId()\n                .addClass('ui-menu ui-widget ui-widget-content ui-corner-all')\n                .toggleClass('ui-menu-icons', !!this.element.find('.ui-icon').length)\n                .attr({\n                    role: this.options.role,\n                    tabIndex: 0\n                })\n                // need to catch all clicks on disabled menu\n                // not possible through _on\n                .on('click' + this.eventNamespace, $.proxy(function (event) {\n                    if (this.options.disabled) {\n                        event.preventDefault();\n                    }\n                }, this));\n\n            if (this.options.disabled) {\n                this.element\n                    .addClass('ui-state-disabled')\n                    .attr('aria-disabled', 'true');\n            }\n\n            this._on({\n                /**\n                 * Prevent focus from sticking to links inside menu after clicking\n                 * them (focus should always stay on UL during navigation).\n                 */\n                'mousedown .ui-menu-item > a': function (event) {\n                    event.preventDefault();\n                },\n\n                /**\n                 * Prevent focus from sticking to links inside menu after clicking\n                 * them (focus should always stay on UL during navigation).\n                 */\n                'click .ui-state-disabled > a': function (event) {\n                    event.preventDefault();\n                },\n\n                /**\n                 * @param {jQuery.Event} event\n                 */\n                'click .ui-menu-item:has(a)': function (event) {\n                    var target = $(event.target).closest('.ui-menu-item');\n\n                    if (!this.mouseHandled && target.not('.ui-state-disabled').length) {\n                        this.mouseHandled = true;\n\n                        this.select(event);\n                        // Open submenu on click\n                        if (target.has('.ui-menu').length) {\n                            this.expand(event);\n                        } else if (!this.element.is(':focus')) {\n                            // Redirect focus to the menu\n                            this.element.trigger('focus', [true]);\n\n                            // If the active item is on the top level, let it stay active.\n                            // Otherwise, blur the active item since it is no longer visible.\n                            if (this.active && this.active.parents('.ui-menu').length === 1) { //eslint-disable-line\n                                clearTimeout(this.timer);\n                            }\n                        }\n                    }\n                },\n\n                /**\n                 * @param {jQuery.Event} event\n                 */\n                'mouseenter .ui-menu-item': function (event) {\n                    var target = $(event.currentTarget);\n\n                    // Remove ui-state-active class from siblings of the newly focused menu item\n                    // to avoid a jump caused by adjacent elements both having a class with a border\n                    target.siblings().children('.ui-state-active').removeClass('ui-state-active');\n                    this.focus(event, target);\n                },\n                mouseleave: 'collapseAll',\n                'mouseleave .ui-menu': 'collapseAll',\n\n                /**\n                 * @param {jQuery.Event} event\n                 * @param {*} keepActiveItem\n                 */\n                focus: function (event, keepActiveItem) {\n                    // If there's already an active item, keep it active\n                    // If not, activate the first item\n                    var item = this.active || this.element.children('.ui-menu-item').eq(0);\n\n                    if (!keepActiveItem) {\n                        this.focus(event, item);\n                    }\n                },\n\n                /**\n                 * @param {jQuery.Event} event\n                 */\n                blur: function (event) {\n                    this._delay(function () {\n                        if (!$.contains(this.element[0], this.document[0].activeElement)) {\n                            this.collapseAll(event);\n                        }\n                    });\n                },\n                keydown: '_keydown'\n            });\n\n            this.refresh();\n\n            // Clicks outside of a menu collapse any open menus\n            this._on(this.document, {\n                /**\n                 * @param {jQuery.Event} event\n                 */\n                click: function (event) {\n                    if (!$(event.target).closest('.ui-menu').length) {\n                        this.collapseAll(event);\n                    }\n\n                    // Reset the mouseHandled flag\n                    this.mouseHandled = false;\n                }\n            });\n        },\n\n        /**\n         * @private\n         */\n        _destroy: function () {\n            // Destroy (sub)menus\n            this.element\n                .removeAttr('aria-activedescendant')\n                .find('.ui-menu').addBack()\n                .removeClass('ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons')\n                .removeAttr('role')\n                .removeAttr('tabIndex')\n                .removeAttr('aria-labelledby')\n                .removeAttr('aria-expanded')\n                .removeAttr('aria-hidden')\n                .removeAttr('aria-disabled')\n                .removeUniqueId()\n                .show();\n\n            // Destroy menu items\n            this.element.find('.ui-menu-item')\n                .removeClass('ui-menu-item')\n                .removeAttr('role')\n                .removeAttr('aria-disabled')\n                .children('a')\n                .removeUniqueId()\n                .removeClass('ui-corner-all ui-state-hover')\n                .removeAttr('tabIndex')\n                .removeAttr('role')\n                .removeAttr('aria-haspopup')\n                .children().each(function () {\n                    var elem = $(this);\n\n                    if (elem.data('ui-menu-submenu-carat')) {\n                        elem.remove();\n                    }\n                });\n\n            // Destroy menu dividers\n            this.element.find('.ui-menu-divider').removeClass('ui-menu-divider ui-widget-content');\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         * @private\n         */\n        _keydown: function (event) {\n            var match, prev, character, skip, regex,\n                preventDefault = true;\n\n            /**\n             * @param {String} value\n             */\n            function escape(value) {\n                return value.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#\\s]/g, '\\\\$&');\n            }\n\n            switch (event.keyCode) {\n                case $.ui.keyCode.PAGE_UP:\n                    this.previousPage(event);\n                    break;\n\n                case $.ui.keyCode.PAGE_DOWN:\n                    this.nextPage(event);\n                    break;\n\n                case $.ui.keyCode.HOME:\n                    this._move('first', 'first', event);\n                    break;\n\n                case $.ui.keyCode.END:\n                    this._move('last', 'last', event);\n                    break;\n\n                case $.ui.keyCode.UP:\n                    this.previous(event);\n                    break;\n\n                case $.ui.keyCode.DOWN:\n                    this.next(event);\n                    break;\n\n                case $.ui.keyCode.LEFT:\n                    this.collapse(event);\n                    break;\n\n                case $.ui.keyCode.RIGHT:\n                    if (this.active && !this.active.is('.ui-state-disabled')) {\n                        this.expand(event);\n                    }\n                    break;\n\n                case $.ui.keyCode.ENTER:\n                case $.ui.keyCode.SPACE:\n                    this._activate(event);\n                    break;\n\n                case $.ui.keyCode.ESCAPE:\n                    this.collapse(event);\n                    break;\n\n                default:\n                    preventDefault = false;\n                    prev = this.previousFilter || '';\n                    character = String.fromCharCode(event.keyCode);\n                    skip = false;\n\n                    clearTimeout(this.filterTimer);\n\n                    if (character === prev) {\n                        skip = true;\n                    } else {\n                        character = prev + character;\n                    }\n\n                    regex = new RegExp('^' + escape(character), 'i');\n                    match = this.activeMenu.children('.ui-menu-item').filter(function () {\n                        return regex.test($(this).children('a').text());\n                    });\n                    match = skip && match.index(this.active.next()) !== -1 ?\n                        this.active.nextAll('.ui-menu-item') :\n                        match;\n\n                    // If no matches on the current filter, reset to the last character pressed\n                    // to move down the menu to the first item that starts with that character\n                    if (!match.length) {\n                        character = String.fromCharCode(event.keyCode);\n                        regex = new RegExp('^' + escape(character), 'i');\n                        match = this.activeMenu.children('.ui-menu-item').filter(function () {\n                            return regex.test($(this).children('a').text());\n                        });\n                    }\n\n                    if (match.length) {\n                        this.focus(event, match);\n\n                        if (match.length > 1) { //eslint-disable-line max-depth\n                            this.previousFilter = character;\n                            this.filterTimer = this._delay(function () {\n                                delete this.previousFilter;\n                            }, 1000);\n                        } else {\n                            delete this.previousFilter;\n                        }\n                    } else {\n                        delete this.previousFilter;\n                    }\n            }\n\n            if (preventDefault) {\n                event.preventDefault();\n            }\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         * @private\n         */\n        _activate: function (event) {\n            if (!this.active.is('.ui-state-disabled')) {\n                if (this.active.children('a[aria-haspopup=\"true\"]').length) {\n                    this.expand(event);\n                } else {\n                    this.select(event);\n                }\n            }\n        },\n\n        /**\n         * Refresh.\n         */\n        refresh: function () {\n            var menus,\n                icon = this.options.icons.submenu,\n                submenus = this.element.find(this.options.menus);\n\n            // Initialize nested menus\n            submenus.filter(':not(.ui-menu)')\n                .addClass('ui-menu ui-widget ui-widget-content ui-corner-all')\n                .hide()\n                .attr({\n                    role: this.options.role,\n                    'aria-hidden': 'true',\n                    'aria-expanded': 'false'\n                })\n                .each(function () {\n                    var menu = $(this),\n                        item = menu.prev('a'),\n                        submenuCarat = $('<span>')\n                            .addClass('ui-menu-icon ui-icon ' + icon)\n                            .data('ui-menu-submenu-carat', true);\n\n                    item\n                        .attr('aria-haspopup', 'true')\n                        .prepend(submenuCarat);\n                    menu.attr('aria-labelledby', item.attr('id'));\n                });\n\n            menus = submenus.add(this.element);\n\n            // Don't refresh list items that are already adapted\n            menus.children(':not(.ui-menu-item):has(a)')\n                .addClass('ui-menu-item')\n                .attr('role', 'presentation')\n                .children('a')\n                .uniqueId()\n                .addClass('ui-corner-all')\n                .attr({\n                    tabIndex: -1,\n                    role: this._itemRole()\n                });\n\n            // Initialize unlinked menu-items containing spaces and/or dashes only as dividers\n            menus.children(':not(.ui-menu-item)').each(function () {\n                var item = $(this);\n\n                // hyphen, em dash, en dash\n                if (!/[^\\-\\u2014\\u2013\\s]/.test(item.text())) {\n                    item.addClass('ui-widget-content ui-menu-divider');\n                }\n            });\n\n            // Add aria-disabled attribute to any disabled menu item\n            menus.children('.ui-state-disabled').attr('aria-disabled', 'true');\n\n            // If the active item has been removed, blur the menu\n            if (this.active && !$.contains(this.element[0], this.active[0])) {\n                this.blur();\n            }\n        },\n\n        /**\n         * @return {*}\n         * @private\n         */\n        _itemRole: function () {\n            return {\n                menu: 'menuitem',\n                listbox: 'option'\n            }[this.options.role];\n        },\n\n        /**\n         * @param {String} key\n         * @param {*} value\n         * @private\n         */\n        _setOption: function (key, value) {\n            if (key === 'icons') {\n                this.element.find('.ui-menu-icon')\n                    .removeClass(this.options.icons.submenu)\n                    .addClass(value.submenu);\n            }\n            this._super(key, value);\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         * @param {Object} item\n         */\n        focus: function (event, item) {\n            var nested, focused;\n\n            this.blur(event, event && event.type === 'focus');\n\n            this._scrollIntoView(item);\n\n            this.active = item.first();\n            focused = this.active.children('a').addClass('ui-state-focus');\n            // Only update aria-activedescendant if there's a role\n            // otherwise we assume focus is managed elsewhere\n            if (this.options.role) {\n                this.element.attr('aria-activedescendant', focused.attr('id'));\n            }\n\n            // Highlight active parent menu item, if any\n            this.active\n                .parent()\n                .closest('.ui-menu-item')\n                .children('a:first')\n                .addClass('ui-state-active');\n\n            if (event && event.type === 'keydown') {\n                this._close();\n            } else {\n                this.timer = this._delay(function () {\n                    this._close();\n                }, this.delay);\n            }\n\n            nested = item.children('.ui-menu');\n\n            if (nested.length && /^mouse/.test(event.type)) {\n                this._startOpening(nested);\n            }\n            this.activeMenu = item.parent();\n\n            this._trigger('focus', event, {\n                item: item\n            });\n        },\n\n        /**\n         * @param {Object} item\n         * @private\n         */\n        _scrollIntoView: function (item) {\n            var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;\n\n            if (this._hasScroll()) {\n                borderTop = parseFloat($.css(this.activeMenu[0], 'borderTopWidth')) || 0;\n                paddingTop = parseFloat($.css(this.activeMenu[0], 'paddingTop')) || 0;\n                offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;\n                scroll = this.activeMenu.scrollTop();\n                elementHeight = this.activeMenu.height();\n                itemHeight = item.height();\n\n                if (offset < 0) {\n                    this.activeMenu.scrollTop(scroll + offset);\n                } else if (offset + itemHeight > elementHeight) {\n                    this.activeMenu.scrollTop(scroll + offset - elementHeight + itemHeight);\n                }\n            }\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         * @param {*} fromFocus\n         */\n        blur: function (event, fromFocus) {\n            if (!fromFocus) {\n                clearTimeout(this.timer);\n            }\n\n            if (!this.active) {\n                return;\n            }\n\n            this.active.children('a').removeClass('ui-state-focus');\n            this.active = null;\n\n            this._trigger('blur', event, {\n                item: this.active\n            });\n        },\n\n        /**\n         * @param {*} submenu\n         * @private\n         */\n        _startOpening: function (submenu) {\n            clearTimeout(this.timer);\n\n            // Don't open if already open fixes a Firefox bug that caused a .5 pixel\n            // shift in the submenu position when mousing over the carat icon\n            if (submenu.attr('aria-hidden') !== 'true') {\n                return;\n            }\n\n            this.timer = this._delay(function () {\n                this._close();\n                this._open(submenu);\n            }, this.delay);\n        },\n\n        /**\n         * @param {*} submenu\n         * @private\n         */\n        _open: function (submenu) {\n            var position = $.extend({\n                of: this.active\n            }, this.options.position);\n\n            clearTimeout(this.timer);\n            this.element.find('.ui-menu').not(submenu.parents('.ui-menu'))\n                .hide()\n                .attr('aria-hidden', 'true');\n\n            submenu\n                .show()\n                .removeAttr('aria-hidden')\n                .attr('aria-expanded', 'true')\n                .position(position);\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         * @param {*} all\n         */\n        collapseAll: function (event, all) {\n            clearTimeout(this.timer);\n            this.timer = this._delay(function () {\n                // If we were passed an event, look for the submenu that contains the event\n                var currentMenu = all ? this.element :\n                    $(event && event.target).closest(this.element.find('.ui-menu'));\n\n                // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway\n                if (!currentMenu.length) {\n                    currentMenu = this.element;\n                }\n\n                this._close(currentMenu);\n\n                this.blur(event);\n                this.activeMenu = currentMenu;\n            }, this.delay);\n        },\n\n        // With no arguments, closes the currently active menu - if nothing is active\n        // it closes all menus.  If passed an argument, it will search for menus BELOW\n        /**\n         * With no arguments, closes the currently active menu - if nothing is active\n         * it closes all menus.  If passed an argument, it will search for menus BELOW.\n         *\n         * @param {*} startMenu\n         * @private\n         */\n        _close: function (startMenu) {\n            if (!startMenu) {\n                startMenu = this.active ? this.active.parent() : this.element;\n            }\n\n            startMenu\n                .find('.ui-menu')\n                .hide()\n                .attr('aria-hidden', 'true')\n                .attr('aria-expanded', 'false')\n                .end()\n                .find('a.ui-state-active')\n                .removeClass('ui-state-active');\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         */\n        collapse: function (event) {\n            var newItem = this.active &&\n                this.active.parent().closest('.ui-menu-item', this.element);\n\n            if (newItem && newItem.length) {\n                this._close();\n                this.focus(event, newItem);\n            }\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         */\n        expand: function (event) {\n            var newItem = this.active &&\n                this.active\n                    .children('.ui-menu ')\n                    .children('.ui-menu-item')\n                    .first();\n\n            if (newItem && newItem.length) {\n                this._open(newItem.parent());\n\n                // Delay so Firefox will not hide activedescendant change in expanding submenu from AT\n                this._delay(function () {\n                    this.focus(event, newItem);\n                });\n            }\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         */\n        next: function (event) {\n            this._move('next', 'first', event);\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         */\n        previous: function (event) {\n            this._move('prev', 'last', event);\n        },\n\n        /**\n         * @return {null|Boolean}\n         */\n        isFirstItem: function () {\n            return this.active && !this.active.prevAll('.ui-menu-item').length;\n        },\n\n        /**\n         * @return {null|Boolean}\n         */\n        isLastItem: function () {\n            return this.active && !this.active.nextAll('.ui-menu-item').length;\n        },\n\n        /**\n         * @param {*} direction\n         * @param {*} filter\n         * @param {jQuery.Event} event\n         * @private\n         */\n        _move: function (direction, filter, event) {\n            var next;\n\n            if (this.active) {\n                if (direction === 'first' || direction === 'last') {\n                    next = this.active\n                        [direction === 'first' ? 'prevAll' : 'nextAll']('.ui-menu-item')\n                        .eq(-1);\n                } else {\n                    next = this.active\n                        [direction + 'All']('.ui-menu-item')\n                        .eq(0);\n                }\n            }\n\n            if (!next || !next.length || !this.active) {\n                next = this.activeMenu.children('.ui-menu-item')[filter]();\n            }\n\n            this.focus(event, next);\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         */\n        nextPage: function (event) {\n            var item, base, height;\n\n            if (!this.active) {\n                this.next(event);\n\n                return;\n            }\n\n            if (this.isLastItem()) {\n                return;\n            }\n\n            if (this._hasScroll()) {\n                base = this.active.offset().top;\n                height = this.element.height();\n                this.active.nextAll('.ui-menu-item').each(function () {\n                    item = $(this);\n\n                    return item.offset().top - base - height < 0;\n                });\n\n                this.focus(event, item);\n            } else {\n                this.focus(event, this.activeMenu.children('.ui-menu-item')\n                    [!this.active ? 'first' : 'last']());\n            }\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         */\n        previousPage: function (event) {\n            var item, base, height;\n\n            if (!this.active) {\n                this.next(event);\n\n                return;\n            }\n\n            if (this.isFirstItem()) {\n                return;\n            }\n\n            if (this._hasScroll()) {\n                base = this.active.offset().top;\n                height = this.element.height();\n                this.active.prevAll('.ui-menu-item').each(function () {\n                    item = $(this);\n\n                    return item.offset().top - base + height > 0;\n                });\n\n                this.focus(event, item);\n            } else {\n                this.focus(event, this.activeMenu.children('.ui-menu-item').first());\n            }\n        },\n\n        /**\n         * @return {Boolean}\n         * @private\n         */\n        _hasScroll: function () {\n            return this.element.outerHeight() < this.element.prop('scrollHeight');\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         */\n        select: function (event) {\n            // TODO: It should never be possible to not have an active item at this\n            // point, but the tests don't trigger mouseenter before click.\n            var ui;\n\n            this.active = this.active || $(event.target).closest('.ui-menu-item');\n            ui = {\n                item: this.active\n            };\n\n            if (!this.active.has('.ui-menu').length) {\n                this.collapseAll(event, true);\n            }\n            this._trigger('select', event, ui);\n        }\n    });\n\n    return $.mage.menu;\n});\n","mage/backend/action-link.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], function ($) {\n    'use strict';\n\n    $.widget('mage.actionLink', {\n        /**\n         * Button creation\n         * @protected\n         */\n        _create: function () {\n            this._bind();\n        },\n\n        /**\n         * Bind handler on button click\n         * @protected\n         */\n        _bind: function () {\n            var keyCode = $.ui.keyCode;\n\n            this._on({\n                /**\n                 * @param {jQuery.Event} e\n                 */\n                mousedown: function (e) {\n                    this._stopPropogation(e);\n                },\n\n                /**\n                 * @param {jQuery.Event} e\n                 */\n                mouseup: function (e) {\n                    this._stopPropogation(e);\n                },\n\n                /**\n                 * @param {jQuery.Event} e\n                 */\n                click: function (e) {\n                    this._stopPropogation(e);\n                    this._triggerEvent();\n                },\n\n                /**\n                 * @param {jQuery.Event} e\n                 */\n                keydown: function (e) {\n                    switch (e.keyCode) {\n                        case keyCode.ENTER:\n                        case keyCode.NUMPAD_ENTER:\n                            this._stopPropogation(e);\n                            this._triggerEvent();\n                            break;\n                    }\n                },\n\n                /**\n                 * @param {jQuery.Event} e\n                 */\n                keyup: function (e) {\n                    switch (e.keyCode) {\n                        case keyCode.ENTER:\n                        case keyCode.NUMPAD_ENTER:\n                            this._stopPropogation(e);\n                            break;\n                    }\n                }\n            });\n        },\n\n        /**\n         * @param {Object} e - event object\n         * @private\n         */\n        _stopPropogation: function (e) {\n            e.stopImmediatePropagation();\n            e.preventDefault();\n        },\n\n        /**\n         * @private\n         */\n        _triggerEvent: function () {\n            $(this.options.related || this.element)\n                .trigger(this.options.event, this.options.eventData ? [this.options.eventData] : [{}]);\n        }\n    });\n\n    return $.mage.actionLink;\n});\n","mage/utils/objects.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'ko',\n    'jquery',\n    'underscore',\n    'mage/utils/strings'\n], function (ko, $, _, stringUtils) {\n    'use strict';\n\n    var primitives = [\n        'undefined',\n        'boolean',\n        'number',\n        'string'\n    ];\n\n    /**\n     * Sets nested property of a specified object.\n     * @private\n     *\n     * @param {Object} parent - Object to look inside for the properties.\n     * @param {Array} path - Splitted path the property.\n     * @param {*} value - Value of the last property in 'path' array.\n     * returns {*} New value for the property.\n     */\n    function setNested(parent, path, value) {\n        var last = path.pop(),\n            len = path.length,\n            pi = 0,\n            part = path[pi];\n\n        for (; pi < len; part = path[++pi]) {\n            if (!_.isObject(parent[part])) {\n                parent[part] = {};\n            }\n\n            parent = parent[part];\n        }\n\n        if (typeof parent[last] === 'function') {\n            parent[last](value);\n        } else {\n            parent[last] = value;\n        }\n\n        return value;\n    }\n\n    /**\n     * Retrieves value of a nested property.\n     * @private\n     *\n     * @param {Object} parent - Object to look inside for the properties.\n     * @param {Array} path - Splitted path the property.\n     * @returns {*} Value of the property.\n     */\n    function getNested(parent, path) {\n        var exists = true,\n            len = path.length,\n            pi = 0;\n\n        for (; pi < len && exists; pi++) {\n            parent = parent[path[pi]];\n\n            if (typeof parent === 'undefined') {\n                exists = false;\n            }\n        }\n\n        if (exists) {\n            if (ko.isObservable(parent)) {\n                parent = parent();\n            }\n\n            return parent;\n        }\n    }\n\n    /**\n     * Removes property from a specified object.\n     * @private\n     *\n     * @param {Object} parent - Object from which to remove property.\n     * @param {Array} path - Splitted path to the property.\n     */\n    function removeNested(parent, path) {\n        var field = path.pop();\n\n        parent = getNested(parent, path);\n\n        if (_.isObject(parent)) {\n            delete parent[field];\n        }\n    }\n\n    return {\n\n        /**\n         * Retrieves or defines objects' property by a composite path.\n         *\n         * @param {Object} data - Container for the properties specified in path.\n         * @param {String} path - Objects' properties divided by dots.\n         * @param {*} [value] - New value for the last property.\n         * @returns {*} Returns value of the last property in chain.\n         *\n         * @example\n         *      utils.nested({}, 'one.two', 3);\n         *      => { one: {two: 3} }\n         */\n        nested: function (data, path, value) {\n            var action = arguments.length > 2 ? setNested : getNested;\n\n            path = path ? path.split('.') : [];\n\n            return action(data, path, value);\n        },\n\n        /**\n         * Removes nested property from an object.\n         *\n         * @param {Object} data - Data source.\n         * @param {String} path - Path to the property e.g. 'one.two.three'\n         */\n        nestedRemove: function (data, path) {\n            path = path.split('.');\n\n            removeNested(data, path);\n        },\n\n        /**\n         * Flattens objects' nested properties.\n         *\n         * @param {Object} data - Object to flatten.\n         * @param {String} [separator='.'] - Objects' keys separator.\n         * @returns {Object} Flattened object.\n         *\n         * @example Example with a default separator.\n         *      utils.flatten({one: { two: { three: 'value'} }});\n         *      => { 'one.two.three': 'value' };\n         *\n         * @example Example with a custom separator.\n         *      utils.flatten({one: { two: { three: 'value'} }}, '=>');\n         *      => {'one=>two=>three': 'value'};\n         */\n        flatten: function (data, separator, parent, result) {\n            separator = separator || '.';\n            result = result || {};\n\n            if (!data) {\n                return result;\n            }\n\n            // UnderscoreJS each breaks when an object has a length property so we use Object.keys\n            _.each(Object.keys(data), function (name) {\n                var node = data[name];\n\n                if ({}.toString.call(node) === '[object Function]') {\n                    return;\n                }\n\n                if (parent) {\n                    name = parent + separator + name;\n                }\n\n                typeof node === 'object' ?\n                    this.flatten(node, separator, name, result) :\n                    result[name] = node;\n\n            }, this);\n\n            return result;\n        },\n\n        /**\n         * Opposite operation of the 'flatten' method.\n         *\n         * @param {Object} data - Previously flattened object.\n         * @param {String} [separator='.'] - Keys separator.\n         * @returns {Object} Object with nested properties.\n         *\n         * @example Example using custom separator.\n         *      utils.unflatten({'one=>two': 'value'}, '=>');\n         *      => {\n         *          one: { two: 'value' }\n         *      };\n         */\n        unflatten: function (data, separator) {\n            var result = {};\n\n            separator = separator || '.';\n\n            _.each(data, function (value, nodes) {\n                nodes = nodes.split(separator);\n\n                setNested(result, nodes, value);\n            });\n\n            return result;\n        },\n\n        /**\n         * Same operation as 'flatten' method,\n         * but returns objects' keys wrapped in '[]'.\n         *\n         * @param {Object} data - Object that should be serialized.\n         * @returns {Object} Serialized data.\n         *\n         * @example\n         *      utils.serialize({one: { two: { three: 'value'} }});\n         *      => { 'one[two][three]': 'value' }\n         */\n        serialize: function (data) {\n            var result = {};\n\n            data = this.flatten(data);\n\n            _.each(data, function (value, keys) {\n                keys = stringUtils.serializeName(keys);\n                value = _.isUndefined(value) ? '' : value;\n\n                result[keys] = value;\n            }, this);\n\n            return result;\n        },\n\n        /**\n         * Performs deep extend of specified objects.\n         *\n         * @returns {Object|Array} Extended object.\n         */\n        extend: function () {\n            var args = _.toArray(arguments);\n\n            args.unshift(true);\n\n            return $.extend.apply($, args);\n        },\n\n        /**\n         * Performs a deep clone of a specified object.\n         *\n         * @param {(Object|Array)} data - Data that should be copied.\n         * @returns {Object|Array} Cloned object.\n         */\n        copy: function (data) {\n            var result = data,\n                isArray = Array.isArray(data),\n                placeholder;\n\n            if (this.isObject(data) || isArray) {\n                placeholder = isArray ? [] : {};\n                result = this.extend(placeholder, data);\n            }\n\n            return result;\n        },\n\n        /**\n         * Performs a deep clone of a specified object.\n         * Doesn't save links to original object.\n         *\n         * @param {*} original - Object to clone\n         * @returns {*}\n         */\n        hardCopy: function (original) {\n            if (original === null || typeof original !== 'object') {\n                return original;\n            }\n\n            return JSON.parse(JSON.stringify(original));\n        },\n\n        /**\n         * Removes specified nested properties from the target object.\n         *\n         * @param {Object} target - Object whose properties should be removed.\n         * @param {(...String|Array|Object)} list - List that specifies properties to be removed.\n         * @returns {Object} Modified object.\n         *\n         * @example Basic usage\n         *      var obj = {a: {b: 2}, c: 'a'};\n         *\n         *      omit(obj, 'a.b');\n         *      => {'a.b': 2};\n         *      obj => {a: {}, c: 'a'};\n         *\n         * @example Various syntaxes that would return same result\n         *      omit(obj, ['a.b', 'c']);\n         *      omit(obj, 'a.b', 'c');\n         *      omit(obj, {'a.b': true, 'c': true});\n         */\n        omit: function (target, list) {\n            var removed = {},\n                ignored = list;\n\n            if (this.isObject(list)) {\n                ignored = [];\n\n                _.each(list, function (value, key) {\n                    if (value) {\n                        ignored.push(key);\n                    }\n                });\n            } else if (_.isString(list)) {\n                ignored = _.toArray(arguments).slice(1);\n            }\n\n            _.each(ignored, function (path) {\n                var value = this.nested(target, path);\n\n                if (!_.isUndefined(value)) {\n                    removed[path] = value;\n\n                    this.nestedRemove(target, path);\n                }\n            }, this);\n\n            return removed;\n        },\n\n        /**\n         * Checks if provided value is a plain object.\n         *\n         * @param {*} value - Value to be checked.\n         * @returns {Boolean}\n         */\n        isObject: function (value) {\n            var objProto = Object.prototype;\n\n            return typeof value == 'object' ?\n            objProto.toString.call(value) === '[object Object]' :\n                false;\n        },\n\n        /**\n         *\n         * @param {*} value\n         * @returns {Boolean}\n         */\n        isPrimitive: function (value) {\n            return value === null || ~primitives.indexOf(typeof value);\n        },\n\n        /**\n         * Iterates over obj props/array elems recursively, applying action to each one\n         *\n         * @param {Object|Array} data - Data to be iterated.\n         * @param {Function} action - Callback to be called with each item as an argument.\n         * @param {Number} [maxDepth=7] - Max recursion depth.\n         */\n        forEachRecursive: function (data, action, maxDepth) {\n            maxDepth = typeof maxDepth === 'number' && !isNaN(maxDepth) ? maxDepth - 1 : 7;\n\n            if (!_.isFunction(action) || _.isFunction(data) || maxDepth < 0) {\n                return;\n            }\n\n            if (!_.isObject(data)) {\n                action(data);\n\n                return;\n            }\n\n            _.each(data, function (value) {\n                this.forEachRecursive(value, action, maxDepth);\n            }, this);\n\n            action(data);\n        },\n\n        /**\n         * Maps obj props/array elems recursively\n         *\n         * @param {Object|Array} data - Data to be iterated.\n         * @param {Function} action - Callback to transform each item.\n         * @param {Number} [maxDepth=7] - Max recursion depth.\n         *\n         * @returns {Object|Array}\n         */\n        mapRecursive: function (data, action, maxDepth) {\n            var newData;\n\n            maxDepth = typeof maxDepth === 'number' && !isNaN(maxDepth) ? maxDepth - 1 : 7;\n\n            if (!_.isFunction(action) || _.isFunction(data) || maxDepth < 0) {\n                return data;\n            }\n\n            if (!_.isObject(data)) {\n                return action(data);\n            }\n\n            if (_.isArray(data)) {\n                newData = _.map(data, function (item) {\n                    return this.mapRecursive(item, action, maxDepth);\n                }, this);\n\n                return action(newData);\n            }\n\n            newData = _.mapObject(data, function (val, key) {\n                if (data.hasOwnProperty(key)) {\n                    return this.mapRecursive(val, action, maxDepth);\n                }\n\n                return val;\n            }, this);\n\n            return action(newData);\n        },\n\n        /**\n         * Removes empty(in common sence) obj props/array elems\n         *\n         * @param {*} data - Data to be cleaned.\n         * @returns {*}\n         */\n        removeEmptyValues: function (data) {\n            if (!_.isObject(data)) {\n                return data;\n            }\n\n            if (_.isArray(data)) {\n                return data.filter(function (item) {\n                    return !this.isEmptyObj(item);\n                }, this);\n            }\n\n            return _.omit(data, this.isEmptyObj.bind(this));\n        },\n\n        /**\n         * Checks that argument of any type is empty in common sence:\n         * empty string, string with spaces only, object without own props, empty array, null or undefined\n         *\n         * @param {*} val - Value to be checked.\n         * @returns {Boolean}\n         */\n        isEmptyObj: function (val) {\n\n            return _.isObject(val) && _.isEmpty(val) ||\n            this.isEmpty(val) ||\n            val && val.trim && this.isEmpty(val.trim());\n        }\n    };\n});\n\n","mage/utils/misc.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'underscore',\n    'jquery',\n    'mage/utils/objects'\n], function (_, $, utils) {\n    'use strict';\n\n    var defaultAttributes,\n        ajaxSettings,\n        map;\n\n    defaultAttributes = {\n        method: 'post',\n        enctype: 'multipart/form-data'\n    };\n\n    ajaxSettings = {\n        default: {\n            method: 'POST',\n            cache: false,\n            processData: false,\n            contentType: false\n        },\n        simple: {\n            method: 'POST',\n            dataType: 'json'\n        }\n    };\n\n    map = {\n        'D': 'DDD',\n        'dd': 'DD',\n        'd': 'D',\n        'EEEE': 'dddd',\n        'EEE': 'ddd',\n        'e': 'd',\n        'yyyy': 'YYYY',\n        'yy': 'YY',\n        'y': 'YYYY',\n        'a': 'A'\n    };\n\n    return {\n\n        /**\n         * Generates a unique identifier.\n         *\n         * @param {Number} [size=7] - Length of a resulting identifier.\n         * @returns {String}\n         */\n        uniqueid: function (size) {\n            var code = Math.random() * 25 + 65 | 0,\n                idstr = String.fromCharCode(code);\n\n            size = size || 7;\n\n            while (idstr.length < size) {\n                code = Math.floor(Math.random() * 42 + 48);\n\n                if (code < 58 || code > 64) {\n                    idstr += String.fromCharCode(code);\n                }\n            }\n\n            return idstr;\n        },\n\n        /**\n         * Limits function call.\n         *\n         * @param {Object} owner\n         * @param {String} target\n         * @param {Number} limit\n         */\n        limit: function (owner, target, limit) {\n            var fn = owner[target];\n\n            owner[target] = _.debounce(fn.bind(owner), limit);\n        },\n\n        /**\n         * Converts mage date format to a moment.js format.\n         *\n         * @param {String} mageFormat\n         * @returns {String}\n         */\n        normalizeDate: function (mageFormat) {\n            var result = mageFormat;\n\n            _.each(map, function (moment, mage) {\n                result = result.replace(\n                    new RegExp(mage + '(?=([^\\u0027]*\\u0027[^\\u0027]*\\u0027)*[^\\u0027]*$)'),\n                    moment\n                );\n            });\n            result = result.replace(/'(.*?)'/g, '[$1]');\n            return result;\n        },\n\n        /**\n         * Puts provided value in range of min and max parameters.\n         *\n         * @param {Number} value - Value to be located.\n         * @param {Number} min - Min value.\n         * @param {Number} max - Max value.\n         * @returns {Number}\n         */\n        inRange: function (value, min, max) {\n            return Math.min(Math.max(min, value), max);\n        },\n\n        /**\n         * Serializes and sends data via POST request.\n         *\n         * @param {Object} options - Options object that consists of\n         *      a 'url' and 'data' properties.\n         * @param {Object} attrs - Attributes that will be added to virtual form.\n         */\n        submit: function (options, attrs) {\n            var form        = document.createElement('form'),\n                data        = utils.serialize(options.data),\n                attributes  = _.extend({}, defaultAttributes, attrs || {});\n\n            if (!attributes.action) {\n                attributes.action = options.url;\n            }\n\n            data['form_key'] = window.FORM_KEY;\n\n            _.each(attributes, function (value, name) {\n                form.setAttribute(name, value);\n            });\n\n            data = _.map(\n                data,\n                function (value, name) {\n                    return '<input type=\"hidden\" ' +\n                        'name=\"' + _.escape(name) + '\" ' +\n                        'value=\"' + _.escape(value) + '\"' +\n                        ' />';\n                }\n            ).join('');\n\n            form.insertAdjacentHTML('afterbegin', data);\n            document.body.appendChild(form);\n\n            form.submit();\n        },\n\n        /**\n         * Serializes and sends data via AJAX POST request.\n         *\n         * @param {Object} options - Options object that consists of\n         *      a 'url' and 'data' properties.\n         * @param {Object} config\n         */\n        ajaxSubmit: function (options, config) {\n            var t = new Date().getTime(),\n                settings;\n\n            options.data['form_key'] = window.FORM_KEY;\n            options.data = this.prepareFormData(options.data, config.ajaxSaveType);\n            settings = _.extend({}, ajaxSettings[config.ajaxSaveType], options || {});\n\n            if (!config.ignoreProcessEvents) {\n                $('body').trigger('processStart');\n            }\n\n            return $.ajax(settings)\n                .done(function (data) {\n                    if (config.response) {\n                        data.t = t;\n                        config.response.data(data);\n                        config.response.status(undefined);\n                        config.response.status(!data.error);\n                    }\n                })\n                .fail(function () {\n                    if (config.response) {\n                        config.response.status(undefined);\n                        config.response.status(false);\n                        config.response.data({\n                            error: true,\n                            messages: 'Something went wrong.',\n                            t: t\n                        });\n                    }\n                })\n                .always(function () {\n                    if (!config.ignoreProcessEvents) {\n                        $('body').trigger('processStop');\n                    }\n                });\n        },\n\n        /**\n         * Creates FormData object and append this data.\n         *\n         * @param {Object} data\n         * @param {String} type\n         * @returns {FormData}\n         */\n        prepareFormData: function (data, type) {\n            var formData;\n\n            if (type === 'default') {\n                formData = new FormData();\n                _.each(utils.serialize(data), function (val, name) {\n                    formData.append(name, val);\n                });\n            } else if (type === 'simple') {\n                formData = utils.serialize(data);\n            }\n\n            return formData;\n        },\n\n        /**\n         * Filters data object. Finds properties with suffix\n         * and sets their values to properties with the same name without suffix.\n         *\n         * @param {Object} data - The data object that should be filtered\n         * @param {String} suffix - The string by which data object should be filtered\n         * @param {String} separator - The string that is separator between property and suffix\n         *\n         * @returns {Object} Filtered data object\n         */\n        filterFormData: function (data, suffix, separator) {\n            data = data || {};\n            suffix = suffix || 'prepared-for-send';\n            separator = separator || '-';\n\n            _.each(data, function (value, key) {\n                if (_.isObject(value) && !Array.isArray(value)) {\n                    this.filterFormData(value, suffix, separator);\n                } else if (_.isString(key) && ~key.indexOf(suffix)) {\n                    data[key.split(separator)[0]] = value;\n                    delete data[key];\n                }\n            }, this);\n\n            return data;\n        },\n\n        /**\n         * Replaces special characters with their corresponding HTML entities.\n         *\n         * @param {String} string - Text to escape.\n         * @returns {String} Escaped text.\n         */\n        escape: function (string) {\n            return string ? $('<p></p>').text(string).html().replace(/\"/g, '&quot;') : string;\n        },\n\n        /**\n         * Replaces symbol codes with their unescaped counterparts.\n         *\n         * @param {String} data\n         *\n         * @returns {String}\n         */\n        unescape: function (data) {\n            var unescaped = _.unescape(data),\n                mapCharacters = {\n                    '&#039;': '\\''\n                };\n\n            _.each(mapCharacters, function (value, key) {\n                unescaped = unescaped.replace(key, value);\n            });\n\n            return unescaped;\n        },\n\n        /**\n         * Converts PHP IntlFormatter format to moment format.\n         *\n         * @param {String} format - PHP format\n         * @returns {String} - moment compatible formatting\n         */\n        convertToMomentFormat: function (format) {\n            var newFormat;\n\n            newFormat = format.replace(/yyyy|yy|y/, 'YYYY'); // replace the year\n            newFormat = newFormat.replace(/dd|d/g, 'DD'); // replace the date\n\n            return newFormat;\n        },\n\n        /**\n         * Get Url Parameters.\n         *\n         * @param {String} url - Url string\n         * @returns {Object}\n         */\n        getUrlParameters: function (url) {\n            var params = {},\n                queries = url.split('?'),\n                temp,\n                i,\n                l;\n\n            if (!queries[1]) {\n                return params;\n            }\n\n            queries = queries[1].split('&');\n\n            for (i = 0, l = queries.length; i < l; i++) {\n                temp = queries[i].split('=');\n\n                if (temp[1]) {\n                    params[temp[0]] = decodeURIComponent(temp[1].replace(/\\+/g, '%20'));\n                } else {\n                    params[temp[0]] = '';\n                }\n            }\n\n            return params;\n        }\n    };\n});\n","mage/utils/strings.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'underscore'\n], function (_) {\n    'use strict';\n\n    var jsonRe = /^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/;\n\n    return {\n\n        /**\n         * Attempts to convert string to one of the primitive values,\n         * or to parse it as a valid json object.\n         *\n         * @param {String} str - String to be processed.\n         * @returns {*}\n         */\n        castString: function (str) {\n            try {\n                str = str === 'true' ? true :\n                    str === 'false' ? false :\n                        str === 'null' ? null :\n                            +str + '' === str ? +str :\n                                jsonRe.test(str) ? JSON.parse(str) :\n                                    str;\n            } catch (e) {\n            }\n\n            return str;\n        },\n\n        /**\n         * Splits string by separator if it's possible,\n         * otherwise returns the incoming value.\n         *\n         * @param {(String|Array|*)} str - String to split.\n         * @param {String} [separator=' '] - Seperator based on which to split the string.\n         * @returns {Array|*} Splitted string or the incoming value.\n         */\n        stringToArray: function (str, separator) {\n            separator = separator || ' ';\n\n            return typeof str === 'string' ?\n                str.split(separator) :\n                str;\n        },\n\n        /**\n         * Converts the incoming string which consists\n         * of a specified delimiters into a format commonly used in form elements.\n         *\n         * @param {String} name - The incoming string.\n         * @param {String} [separator='.']\n         * @returns {String} Serialized string.\n         *\n         * @example\n         *      utils.serializeName('one.two.three');\n         *      => 'one[two][three]';\n         */\n        serializeName: function (name, separator) {\n            var result;\n\n            separator = separator || '.';\n            name = name.split(separator);\n\n            result = name.shift();\n\n            name.forEach(function (part) {\n                result += '[' + part + ']';\n            });\n\n            return result;\n        },\n\n        /**\n         * Checks wether the incoming value is not empty,\n         * e.g. not 'null' or 'undefined'\n         *\n         * @param {*} value - Value to check.\n         * @returns {Boolean}\n         */\n        isEmpty: function (value) {\n            return value === '' || _.isUndefined(value) || _.isNull(value);\n        },\n\n        /**\n         * Adds 'prefix' to the 'part' value if it was provided.\n         *\n         * @param {String} prefix\n         * @param {String} part\n         * @returns {String}\n         */\n        fullPath: function (prefix, part) {\n            return prefix ? prefix + '.' + part : part;\n        },\n\n        /**\n         * Splits incoming string and returns its' part specified by offset.\n         *\n         * @param {String} parts\n         * @param {Number} [offset]\n         * @param {String} [delimiter=.]\n         * @returns {String}\n         */\n        getPart: function (parts, offset, delimiter) {\n            delimiter = delimiter || '.';\n            parts = parts.split(delimiter);\n            offset = this.formatOffset(parts, offset);\n\n            parts.splice(offset, 1);\n\n            return parts.join(delimiter) || '';\n        },\n\n        /**\n         * Converts nameThroughCamelCase to name-through-minus\n         *\n         * @param {String} string\n         * @returns {String}\n         */\n        camelCaseToMinus: function camelCaseToMinus(string) {\n            return ('' + string)\n                .split('')\n                .map(function (symbol, index) {\n                    return index ?\n                        symbol.toUpperCase() === symbol ?\n                        '-' + symbol.toLowerCase() :\n                            symbol :\n                        symbol.toLowerCase();\n                })\n                .join('');\n        },\n\n        /**\n         * Converts name-through-minus to nameThroughCamelCase\n         *\n         * @param {String} string\n         * @returns {String}\n         */\n        minusToCamelCase: function minusToCamelCase(string) {\n            return ('' + string)\n                .split('-')\n                .map(function (part, index) {\n                    return index ? part.charAt(0).toUpperCase() + part.slice(1) : part;\n                })\n                .join('');\n        }\n    };\n});\n","mage/utils/arrays.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'underscore',\n    './strings'\n], function (_, utils) {\n    'use strict';\n\n    /**\n     * Defines index of an item in a specified container.\n     *\n     * @param {*} item - Item whose index should be defined.\n     * @param {Array} container - Container upon which to perform search.\n     * @returns {Number}\n     */\n    function getIndex(item, container) {\n        var index = container.indexOf(item);\n\n        if (~index) {\n            return index;\n        }\n\n        return _.findIndex(container, function (value) {\n            return value && value.name === item;\n        });\n    }\n\n    return {\n        /**\n         * Facade method to remove/add value from/to array\n         * without creating a new instance.\n         *\n         * @param {Array} arr - Array to be modified.\n         * @param {*} value - Value to add/remove.\n         * @param {Boolean} add - Flag that specfies operation.\n         * @returns {Utils} Chainable.\n         */\n        toggle: function (arr, value, add) {\n            return add ?\n                this.add(arr, value) :\n                this.remove(arr, value);\n        },\n\n        /**\n         * Removes the incoming value from array in case\n         * without creating a new instance of it.\n         *\n         * @param {Array} arr - Array to be modified.\n         * @param {*} value - Value to be removed.\n         * @returns {Utils} Chainable.\n         */\n        remove: function (arr, value) {\n            var index = arr.indexOf(value);\n\n            if (~index) {\n                arr.splice(index, 1);\n            }\n\n            return this;\n        },\n\n        /**\n         * Adds the incoming value to array if\n         * it's not alredy present in there.\n         *\n         * @param {Array} arr - Array to be modifed.\n         * @param {...*} arguments - Values to be added.\n         * @returns {Utils} Chainable.\n         */\n        add: function (arr) {\n            var values = _.toArray(arguments).slice(1);\n\n            values.forEach(function (value) {\n                if (!~arr.indexOf(value)) {\n                    arr.push(value);\n                }\n            });\n\n            return this;\n        },\n\n        /**\n         * Inserts specified item into container at a specified position.\n         *\n         * @param {*} item - Item to be inserted into container.\n         * @param {Array} container - Container of items.\n         * @param {*} [position=-1] - Position at which item should be inserted.\n         *      Position can represent:\n         *          - specific index in container\n         *          - item which might already be present in container\n         *          - structure with one of these properties: after, before\n         * @returns {Boolean|*}\n         *      - true if element has changed its' position\n         *      - false if nothing has changed\n         *      - inserted value if it wasn't present in container\n         */\n        insert: function (item, container, position) {\n            var currentIndex = getIndex(item, container),\n                newIndex,\n                target;\n\n            if (typeof position === 'undefined') {\n                position = -1;\n            } else if (typeof position === 'string') {\n                position = isNaN(+position) ? position : +position;\n            }\n\n            newIndex = position;\n\n            if (~currentIndex) {\n                target = container.splice(currentIndex, 1)[0];\n\n                if (typeof item === 'string') {\n                    item = target;\n                }\n            }\n\n            if (typeof position !== 'number') {\n                target = position.after || position.before || position;\n\n                newIndex = getIndex(target, container);\n\n                if (~newIndex && (position.after || newIndex >= currentIndex)) {\n                    newIndex++;\n                }\n            }\n\n            if (newIndex < 0) {\n                newIndex += container.length + 1;\n            }\n\n            container[newIndex] ?\n                container.splice(newIndex, 0, item) :\n                container[newIndex] = item;\n\n            return !~currentIndex ? item : currentIndex !== newIndex;\n        },\n\n        /**\n         * @param {Array} elems\n         * @param {Number} offset\n         * @return {Number|*}\n         */\n        formatOffset: function (elems, offset) {\n            if (utils.isEmpty(offset)) {\n                offset = -1;\n            }\n\n            offset = +offset;\n\n            if (offset < 0) {\n                offset += elems.length + 1;\n            }\n\n            return offset;\n        }\n    };\n});\n","mage/utils/wrapper.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * Utility methods used to wrap and extend functions.\n *\n * @example Usage of a 'wrap' method with arguments delegation.\n *      var multiply = function (a, b) {\n *          return a * b;\n *      };\n *\n *      multiply = module.wrap(multiply, function (orig) {\n *          return 'Result is: ' + orig();\n *      });\n *\n *      multiply(2, 2);\n *      => 'Result is: 4'\n *\n * @example Usage of 'wrapSuper' method.\n *      var multiply = function (a, b) {\n *         return a * b;\n *      };\n *\n *      var obj = {\n *          multiply: module.wrapSuper(multiply, function () {\n *              return 'Result is: ' + this._super();\n *          });\n *      };\n *\n *      obj.multiply(2, 2);\n *      => 'Result is: 4'\n */\ndefine([\n    'underscore'\n], function (_) {\n    'use strict';\n\n    /**\n     * Checks if string has a '_super' substring.\n     */\n    var superReg = /\\b_super\\b/;\n\n    return {\n\n        /**\n         * Wraps target function with a specified wrapper, which will receive\n         * reference to the original function as a first argument.\n         *\n         * @param {Function} target - Function to be wrapped.\n         * @param {Function} wrapper - Wrapper function.\n         * @returns {Function} Wrapper function.\n         */\n        wrap: function (target, wrapper) {\n            if (!_.isFunction(target) || !_.isFunction(wrapper)) {\n                return wrapper;\n            }\n\n            return function () {\n                var args    = _.toArray(arguments),\n                    ctx     = this,\n                    _super;\n\n                /**\n                 * Function that will be passed to the wrapper.\n                 * If no arguments will be passed to it, then the original\n                 * function will be called with an arguments of a wrapper function.\n                 */\n                _super = function () {\n                    var superArgs = arguments.length ? arguments : args.slice(1);\n\n                    return target.apply(ctx, superArgs);\n                };\n\n                args.unshift(_super);\n\n                return wrapper.apply(ctx, args);\n            };\n        },\n\n        /**\n         * Wraps the incoming function to implement support of the '_super' method.\n         *\n         * @param {Function} target - Function to be wrapped.\n         * @param {Function} wrapper - Wrapper function.\n         * @returns {Function} Wrapped function.\n         */\n        wrapSuper: function (target, wrapper) {\n            if (!this.hasSuper(wrapper) || !_.isFunction(target)) {\n                return wrapper;\n            }\n\n            return function () {\n                var _super  = this._super,\n                    args    = arguments,\n                    result;\n\n                /**\n                 * Temporary define '_super' method which\n                 * contains call to the original function.\n                 */\n                this._super = function () {\n                    var superArgs = arguments.length ? arguments : args;\n\n                    return target.apply(this, superArgs);\n                };\n\n                result = wrapper.apply(this, args);\n\n                this._super = _super;\n\n                return result;\n            };\n        },\n\n        /**\n         * Checks wether the incoming method contains calls of the '_super' method.\n         *\n         * @param {Function} fn - Function to be checked.\n         * @returns {Boolean}\n         */\n        hasSuper: function (fn) {\n            return _.isFunction(fn) && superReg.test(fn);\n        },\n\n        /**\n         * Extends target object with provided extenders.\n         * If property in target and extender objects is a function,\n         * then it will be wrapped using 'wrap' method.\n         *\n         * @param {Object} target - Object to be extended.\n         * @param {...Object} extenders - Multiple extenders objects.\n         * @returns {Object} Modified target object.\n         */\n        extend: function (target) {\n            var extenders = _.toArray(arguments).slice(1),\n                iterator = this._extend.bind(this, target);\n\n            extenders.forEach(iterator);\n\n            return target;\n        },\n\n        /**\n         * Same as the 'extend' method, but operates only on one extender object.\n         *\n         * @private\n         * @param {Object} target\n         * @param {Object} extender\n         */\n        _extend: function (target, extender) {\n            _.each(extender, function (value, key) {\n                target[key] = this.wrap(target[key], extender[key]);\n            }, this);\n        }\n    };\n});\n","mage/utils/main.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine(function (require) {\n    'use strict';\n\n    var utils = {},\n        _ = require('underscore'),\n        root = typeof self == 'object' && self.self === self && self ||\n            typeof global == 'object' && global.global === global && global ||\n            Function('return this')() || {};\n\n    root._ = _;\n\n    return _.extend(\n        utils,\n        require('./arrays'),\n        require('./compare'),\n        require('./misc'),\n        require('./objects'),\n        require('./strings'),\n        require('./template')\n    );\n});\n","mage/utils/template.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* eslint-disable no-shadow */\n\ndefine([\n    'jquery',\n    'underscore',\n    'mage/utils/objects',\n    'mage/utils/strings'\n], function ($, _, utils, stringUtils) {\n    'use strict';\n\n    var tmplSettings = _.templateSettings,\n        interpolate = /\\$\\{([\\s\\S]+?)\\}/g,\n        opener = '${',\n        template,\n        hasStringTmpls;\n\n    /**\n     * Identifies whether ES6 templates are supported.\n     */\n    hasStringTmpls = (function () {\n        var testString = 'var foo = \"bar\"; return `${ foo }` === foo';\n\n        try {\n            return Function(testString)();\n        } catch (e) {\n            return false;\n        }\n    })();\n\n    /**\n     * Objects can specify how to use templating for their properties - getting that configuration.\n     *\n     * To disable rendering for all properties of your object add __disableTmpl: true.\n     * To disable for specific property add __disableTmpl: {propertyName: true}.\n     * To limit recursion for a specific property add __disableTmpl: {propertyName: numberOfCycles}.\n     *\n     * @param {String} tmpl\n     * @param {Object | undefined} target\n     * @returns {Boolean|Object}\n     */\n    function isTmplIgnored(tmpl, target) {\n        var parsedTmpl;\n\n        try {\n            parsedTmpl = JSON.parse(tmpl);\n\n            if (typeof parsedTmpl === 'object') {\n                return tmpl.includes('__disableTmpl');\n            }\n        } catch (e) {\n        }\n\n        if (typeof target !== 'undefined') {\n            if (typeof target === 'object' && target.hasOwnProperty('__disableTmpl')) {\n                return target.__disableTmpl;\n            }\n        }\n\n        return false;\n\n    }\n\n    if (hasStringTmpls) {\n\n        /*eslint-disable no-unused-vars, no-eval*/\n        /**\n         * Evaluates template string using ES6 templates.\n         *\n         * @param {String} tmpl - Template string.\n         * @param {Object} $ - Data object used in a template.\n         * @returns {String} Compiled template.\n         */\n        template = function (tmpl, $) {\n            return eval('`' + tmpl + '`');\n        };\n\n        /*eslint-enable no-unused-vars, no-eval*/\n    } else {\n\n        /**\n         * Fallback function used when ES6 templates are not supported.\n         * Uses underscore templates renderer.\n         *\n         * @param {String} tmpl - Template string.\n         * @param {Object} data - Data object used in a template.\n         * @returns {String} Compiled template.\n         */\n        template = function (tmpl, data) {\n            var cached = tmplSettings.interpolate;\n\n            tmplSettings.interpolate = interpolate;\n\n            tmpl = _.template(tmpl, {\n                variable: '$'\n            })(data);\n\n            tmplSettings.interpolate = cached;\n\n            return tmpl;\n        };\n    }\n\n    /**\n     * Checks if provided value contains template syntax.\n     *\n     * @param {*} value - Value to be checked.\n     * @returns {Boolean}\n     */\n    function isTemplate(value) {\n        return typeof value === 'string' &&\n            value.indexOf(opener) !== -1 &&\n            // the below pattern almost always indicates an accident which should not cause template evaluation\n            // refuse to evaluate\n            value.indexOf('${{') === -1;\n    }\n\n    /**\n     * Iteratively processes provided string\n     * until no templates syntax will be found.\n     *\n     * @param {String} tmpl - Template string.\n     * @param {Object} data - Data object used in a template.\n     * @param {Boolean} [castString=false] - Flag that indicates whether template\n     *      should be casted after evaluation to a value of another type or\n     *      that it should be leaved as a string.\n     * @param {Number|undefined} maxCycles - Maximum number of rendering cycles, can be 0.\n     * @returns {*} Compiled template.\n     */\n    function render(tmpl, data, castString, maxCycles) {\n        var last = tmpl,\n            cycles = 0;\n\n        while (~tmpl.indexOf(opener) && (typeof maxCycles === 'undefined' || cycles < maxCycles)) {\n            if (!isTmplIgnored(tmpl)) {\n                tmpl = template(tmpl, data);\n            }\n\n            if (tmpl === last) {\n                break;\n            }\n\n            last = tmpl;\n            cycles++;\n        }\n\n        return castString ?\n            stringUtils.castString(tmpl) :\n            tmpl;\n    }\n\n    return {\n\n        /**\n         * Applies provided data to the template.\n         *\n         * @param {Object|String} tmpl\n         * @param {Object} [data] - Data object to match with template.\n         * @param {Boolean} [castString=false] - Flag that indicates whether template\n         *      should be casted after evaluation to a value of another type or\n         *      that it should be leaved as a string.\n         * @returns {*}\n         *\n         * @example Template defined as a string.\n         *      var source = { foo: 'Random Stuff', bar: 'Some' };\n         *\n         *      utils.template('${ $.bar } ${ $.foo }', source);\n         *      => 'Some Random Stuff';\n         *\n         * @example Template defined as an object.\n         *      var tmpl = {\n         *              key: {'${ $.$data.bar }': '${ $.$data.foo }'},\n         *              foo: 'bar',\n         *              x1: 2, x2: 5,\n         *              delta: '${ $.x2 - $.x1 }',\n         *              baz: 'Upper ${ $.foo.toUpperCase() }'\n         *      };\n         *\n         *      utils.template(tmpl, source);\n         *      => {\n         *          key: {'Some': 'Random Stuff'},\n         *          foo: 'bar',\n         *          x1: 2, x2: 5,\n         *          delta: 3,\n         *          baz: 'Upper BAR'\n         *      };\n         */\n        template: function (tmpl, data, castString, dontClone) {\n            if (typeof tmpl === 'string') {\n                return render(tmpl, data, castString);\n            }\n\n            if (!dontClone) {\n                tmpl = utils.copy(tmpl);\n            }\n\n            tmpl.$data = data || {};\n\n            /**\n             * Template iterator function.\n             */\n            _.each(tmpl, function iterate(value, key, list) {\n                var disabled,\n                    maxCycles;\n\n                if (key === '$data') {\n                    return;\n                }\n\n                if (isTemplate(key)) {\n                    delete list[key];\n\n                    key = render(key, tmpl);\n                    list[key] = value;\n                }\n\n                if (isTemplate(value)) {\n                    //Getting template disabling settings, can be true for all disabled and separate settings\n                    //for each property.\n                    disabled = isTmplIgnored(value, list);\n\n                    if (typeof disabled === 'object' && disabled.hasOwnProperty(key) && disabled[key] !== false) {\n                        //Checking if specific settings for a property provided.\n                        maxCycles = disabled[key];\n                    }\n\n                    if (disabled === true || maxCycles === true) {\n                        //Rendering for all properties is disabled.\n                        maxCycles = 0;\n                    }\n\n                    list[key] = render(value, tmpl, castString, maxCycles);\n                } else if ($.isPlainObject(value) || Array.isArray(value)) {\n                    _.each(value, iterate);\n                }\n            });\n\n            delete tmpl.$data;\n\n            return tmpl;\n        }\n    };\n});\n","mage/utils/compare.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'underscore',\n    'mage/utils/objects'\n], function (_, utils) {\n    'use strict';\n\n    var result = [];\n\n    /**\n     * Checks if all of the provided arrays contains equal values.\n     *\n     * @param {(Boolean|Array)} [keepOrder=false]\n     * @param {Array} target\n     * @returns {Boolean}\n     */\n    function equalArrays(keepOrder, target) {\n        var args = _.toArray(arguments),\n            arrays;\n\n        if (!Array.isArray(keepOrder)) {\n            arrays      = args.slice(2);\n        } else {\n            target      = keepOrder;\n            keepOrder   = false;\n            arrays      = args.slice(1);\n        }\n\n        if (!arrays.length) {\n            return true;\n        }\n\n        return arrays.every(function (array) {\n            if (array === target) {\n                return true;\n            } else if (array.length !== target.length) {\n                return false;\n            } else if (!keepOrder) {\n                return !_.difference(target, array).length;\n            }\n\n            return array.every(function (value, index) {\n                return target[index] === value;\n            });\n        });\n    }\n\n    /**\n     * Checks if two values are different.\n     *\n     * @param {*} a - First value.\n     * @param {*} b - Second value.\n     * @returns {Boolean}\n     */\n    function isDifferent(a, b) {\n        var oldIsPrimitive = utils.isPrimitive(a);\n\n        if (Array.isArray(a) && Array.isArray(b)) {\n            return !equalArrays(true, a, b);\n        }\n\n        return oldIsPrimitive ? a !== b : true;\n    }\n\n    /**\n     * @param {String} prefix\n     * @param {String} part\n     */\n    function getPath(prefix, part) {\n        return prefix ? prefix + '.' + part : part;\n    }\n\n    /**\n     * Checks if object has own specified property.\n     *\n     * @param {*} obj - Value to be checked.\n     * @param {String} key - Key of the property.\n     * @returns {Boolean}\n     */\n    function hasOwn(obj, key) {\n        return Object.prototype.hasOwnProperty.call(obj, key);\n    }\n\n    /**\n     * @param {Array} changes\n     */\n    function getContainers(changes) {\n        var containers  = {},\n            indexed     = _.indexBy(changes, 'path');\n\n        _.each(indexed, function (change, name) {\n            var path;\n\n            name.split('.').forEach(function (part) {\n                path = getPath(path, part);\n\n                if (path in indexed) {\n                    return;\n                }\n\n                (containers[path] = containers[path] || []).push(change);\n            });\n        });\n\n        return containers;\n    }\n\n    /**\n     * @param {String} path\n     * @param {String} name\n     * @param {String} type\n     * @param {String} newValue\n     * @param {String} oldValue\n     */\n    function addChange(path, name, type, newValue, oldValue) {\n        var data;\n\n        data = {\n            path: path,\n            name: name,\n            type: type\n        };\n\n        if (type !== 'remove') {\n            data.value = newValue;\n            data.oldValue = oldValue;\n        } else {\n            data.oldValue = newValue;\n        }\n\n        result.push(data);\n    }\n\n    /**\n     * @param {String} ns\n     * @param {String} name\n     * @param {String} type\n     * @param {String} iterator\n     * @param {String} placeholder\n     */\n    function setAll(ns, name, type, iterator, placeholder) {\n        var key;\n\n        if (arguments.length > 4) {\n            type === 'add' ?\n                addChange(ns, name, 'update', iterator, placeholder) :\n                addChange(ns, name, 'update', placeholder, iterator);\n        } else {\n            addChange(ns, name, type, iterator);\n        }\n\n        if (!utils.isObject(iterator)) {\n            return;\n        }\n\n        for (key in iterator) {\n            if (hasOwn(iterator, key)) {\n                setAll(getPath(ns, key), key, type, iterator[key]);\n            }\n        }\n    }\n\n    /*eslint-disable max-depth*/\n    /**\n     * @param {Object} old\n     * @param {Object} current\n     * @param {String} ns\n     * @param {String} name\n     */\n    function compare(old, current, ns, name) {\n        var key,\n            oldIsObj = utils.isObject(old),\n            newIsObj = utils.isObject(current);\n\n        if (oldIsObj && newIsObj) {\n            for (key in old) {\n                if (hasOwn(old, key) && !hasOwn(current, key)) {\n                    setAll(getPath(ns, key), key, 'remove', old[key]);\n                }\n            }\n\n            for (key in current) {\n                if (hasOwn(current, key)) {\n                    hasOwn(old, key) ?\n                        compare(old[key], current[key], getPath(ns, key), key) :\n                        setAll(getPath(ns, key), key, 'add', current[key]);\n                }\n            }\n        } else if (oldIsObj) {\n            setAll(ns, name, 'remove', old, current);\n        } else if (newIsObj) {\n            setAll(ns, name, 'add', current, old);\n        } else if (isDifferent(old, current)) {\n            addChange(ns, name, 'update', current, old);\n        }\n    }\n\n    /*eslint-enable max-depth*/\n\n    return {\n\n        /**\n         *\n         * @returns {Object}\n         */\n        compare: function () {\n            var changes;\n\n            compare.apply(null, arguments);\n\n            changes = result.splice(0);\n\n            return {\n                containers: getContainers(changes),\n                changes: changes,\n                equal: !changes.length\n            };\n        },\n\n        equalArrays: equalArrays\n    };\n});\n","Magento_Eav/js/input-types.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'jquery',\n    'mage/translate'\n], function ($) {\n    'use strict';\n\n    return function (config) {\n        $('select#frontend_input').each(function () {\n            var select = $(this),\n                currentValue = select.find('option:selected').val(),\n                compatibleTypes = config.inputTypes,\n                enabledTypes = [],\n                iterator,\n                warning = $('<label>')\n                    .hide()\n                    .text($.mage.__('These changes affect all related products.'))\n                    .addClass('mage-error')\n                    .attr({\n                        generated: true, for: select.attr('id')\n                    }),\n                hint = $('<p>')\n                    .hide()\n                    .addClass('note')\n                    .attr({\n                        generated: true\n                    }),\n                hints = config.hints,\n\n            /**\n             * Toggle hint about changes types\n             */\n            toggleWarning = function () {\n                if (select.find('option:selected').val() === currentValue) {\n                    warning.hide();\n                } else {\n                    warning.show();\n                }\n            },\n\n            /**\n             * Toggle hint\n             */\n            toggleHint = function () {\n                if (typeof hints[select.find('option:selected').val()] !== 'undefined') {\n                    select.after(hint.show().text(hints[select.find('option:selected').val()]));\n                } else {\n                    hint.hide();\n                }\n            },\n\n            /**\n             * Remove unsupported options\n             */\n            removeOption = function () {\n                if (!~enabledTypes.indexOf($(this).val())) {\n                    $(this).remove();\n                }\n            };\n\n            // find enabled types for switching dor current input type\n            for (iterator = 0; iterator < compatibleTypes.length; iterator++) {\n                if (compatibleTypes[iterator].indexOf(currentValue) >= 0) {\n                    enabledTypes = compatibleTypes[iterator];\n                }\n            }\n\n            // Check current type (allow only compatible types)\n            if (~enabledTypes.indexOf(currentValue)) {\n                // Enable select and keep only available options (all other will be removed)\n                select.prop('disabled', false).find('option').each(removeOption);\n                // Add warning on page and event for show/hide it\n                select.after(warning).on('change', toggleWarning);\n            }\n            //bind hint toggling on change event\n            select.on('change', toggleHint);\n            //show hint for currently selected value\n            toggleHint();\n        });\n    };\n});\n","Magento_AdobeIms/js/user.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine(['ko'], function (ko) {\n    'use strict';\n\n    return {\n        isAuthorized: ko.observable(false),\n        name: ko.observable(''),\n        email: ko.observable('')\n    };\n});\n","Magento_AdobeIms/js/config.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([], function () {\n    'use strict';\n\n    return {\n        loginUrl: 'https://ims-na1.adobelogin.com/ims/authorize',\n        profileUrl: 'adobe_ims/user/profile',\n        logoutUrl: 'adobe_ims/user/logout',\n        manageAccountLink: 'https://account.adobe.com/',\n        login: {\n            callbackParsingParams: {\n                regexpPattern: /auth\\[code=(success|error);message=(.+)\\]/,\n                codeIndex: 1,\n                messageIndex: 2,\n                nameIndex: 3,\n                successCode: 'success',\n                errorCode: 'error'\n            },\n            popupWindowParams: {\n                width: 500,\n                height: 600,\n                top: 100,\n                left: 300\n            },\n            popupWindowTimeout: 10000\n        }\n    };\n});\n\n","Magento_AdobeIms/js/signIn.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'uiComponent',\n    'jquery',\n    'Magento_AdobeIms/js/action/authorization'\n], function (Component, $, login) {\n    'use strict';\n\n    return Component.extend({\n\n        defaults: {\n            profileUrl: 'adobe_ims/user/profile',\n            logoutUrl: 'adobe_ims/user/logout',\n            user: {\n                isAuthorized: false,\n                name: '',\n                email: '',\n                image: ''\n            },\n            loginConfig: {\n                url: 'https://ims-na1.adobelogin.com/ims/authorize',\n                callbackParsingParams: {\n                    regexpPattern: /auth\\[code=(success|error);message=(.+)\\]/,\n                    codeIndex: 1,\n                    messageIndex: 2,\n                    nameIndex: 3,\n                    successCode: 'success',\n                    errorCode: 'error'\n                },\n                popupWindowParams: {\n                    width: 500,\n                    height: 600,\n                    top: 100,\n                    left: 300\n                },\n                popupWindowTimeout: 60000\n            }\n        },\n\n        /**\n         * @inheritdoc\n         */\n        initObservable: function () {\n            this._super().observe(['user']);\n\n            return this;\n        },\n\n        /**\n         * Login to Adobe\n         *\n         * @return {window.Promise}\n         */\n        login: function () {\n            var deferred = $.Deferred();\n\n            if (this.user().isAuthorized) {\n                deferred.resolve();\n            }\n            login(this.loginConfig)\n                .then(function (response) {\n                    this.loadUserProfile();\n                    deferred.resolve(response);\n                }.bind(this))\n                .fail(function (error) {\n                    deferred.reject(error);\n                });\n\n            return deferred.promise();\n        },\n\n        /**\n         * Retrieve data to authorized user.\n         *\n         * @return array\n         */\n        loadUserProfile: function () {\n            $.ajax({\n                type: 'GET',\n                url: this.profileUrl,\n                showLoader: true,\n                dataType: 'json',\n                context: this,\n\n                /**\n                 * @param {Object} response\n                 * @returns void\n                 */\n                success: function (response) {\n                    this.user({\n                        isAuthorized: true,\n                        name: response.result.name,\n                        email: response.result.email,\n                        image: response.result.image\n                    });\n                },\n\n                /**\n                 * @param {Object} response\n                 * @returns {String}\n                 */\n                error: function (response) {\n                    return response.message;\n                }\n            });\n        },\n\n        /**\n         * Logout from adobe account\n         */\n        logout: function () {\n            $.ajax({\n                type: 'POST',\n                url: this.logoutUrl,\n                data: {\n                    'form_key': window.FORM_KEY\n                },\n                dataType: 'json',\n                context: this,\n                showLoader: true,\n                success: function () {\n                    this.user({\n                        isAuthorized: false,\n                        name: '',\n                        email: '',\n                        image: ''\n                    });\n                }.bind(this),\n\n                /**\n                 * @param {Object} response\n                 * @returns {String}\n                 */\n                error: function (response) {\n                    return response.message;\n                }\n            });\n        }\n    });\n});\n","Magento_AdobeIms/js/action/authorization.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery'\n], function ($) {\n    'use strict';\n\n    /**\n     * Build window params\n     * @param {Object} windowParams\n     * @returns {String}\n     */\n    function buildWindowParams(windowParams) {\n        var output = '',\n            coma = '',\n            paramName,\n            paramValue;\n\n        for (paramName in windowParams) {\n            if (windowParams[paramName]) {\n                paramValue = windowParams[paramName];\n                output += coma + paramName + '=' + paramValue;\n                coma = ',';\n            }\n        }\n\n        return output;\n    }\n\n    return function (config) {\n        var authWindow,\n            deferred = $.Deferred(),\n            watcherId,\n            stopWatcherId;\n\n        /**\n         * Close authorization window if already opened\n         */\n        if (window.adobeIMSAuthWindow) {\n            window.adobeIMSAuthWindow.close();\n        }\n\n        /**\n         * Opens authorization window with special parameters\n         */\n        authWindow = window.adobeIMSAuthWindow = window.open(\n            config.url,\n            'authorization_widnow',\n            buildWindowParams(\n                config.popupWindowParams || {\n                    width: 500,\n                    height: 300\n                }\n            )\n        );\n\n        /**\n         * Stop handle\n         */\n        function stopHandle() {\n            // Clear timers\n            clearTimeout(stopWatcherId);\n            clearInterval(watcherId);\n\n            // Close window\n            authWindow.close();\n        }\n\n        /**\n         * Start handle\n         */\n        function startHandle() {\n            var responseData;\n\n            try {\n\n                if (authWindow.document.domain !== document.domain ||\n                    authWindow.document.readyState !== 'complete') {\n                    return;\n                }\n\n                /**\n                 * If within 10 seconds the result is not received, then reject the request\n                 */\n                stopWatcherId = setTimeout(function () {\n                    stopHandle();\n                    deferred.reject(new Error('Time\\'s up.'));\n                }, config.popupWindowTimeout || 60000);\n\n                responseData = authWindow.document.body.innerHTML.match(\n                    config.callbackParsingParams.regexpPattern\n                );\n\n                if (!responseData) {\n                    return;\n                }\n\n                stopHandle();\n\n                if (responseData[config.callbackParsingParams.codeIndex] ===\n                    config.callbackParsingParams.successCode) {\n                    deferred.resolve({\n                        isAuthorized: true,\n                        lastAuthSuccessMessage: responseData[config.callbackParsingParams.messageIndex]\n                    });\n                } else {\n                    deferred.reject(responseData[config.callbackParsingParams.messageIndex]);\n                }\n            } catch (e) {\n                if (authWindow.closed) {\n                    clearTimeout(stopWatcherId);\n                    clearInterval(watcherId);\n\n                    // eslint-disable-next-line max-depth\n                    if (window.adobeIMSAuthWindow && window.adobeIMSAuthWindow.closed) {\n                        deferred.reject(new Error('Authentication window was closed.'));\n                    }\n                }\n            }\n        }\n\n        /**\n         * Watch a result 1 time per second\n         */\n        watcherId = setInterval(startHandle, 1000);\n\n        return deferred.promise();\n    };\n});\n","Magento_Variable/variables.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* global Variables, updateElementAtCursor, MagentovariablePlugin, Base64 */\ndefine([\n    'jquery',\n    'mage/backend/notification',\n    'mage/translate',\n    'wysiwygAdapter',\n    'uiRegistry',\n    'mage/apply/main',\n    'mageUtils',\n    'Magento_Variable/js/config-directive-generator',\n    'Magento_Variable/js/custom-directive-generator',\n    'Magento_Ui/js/lib/spinner',\n    'jquery/ui',\n    'prototype',\n    'mage/adminhtml/tools'\n], function (jQuery, notification, $t, wysiwyg, registry, mageApply, utils, configGenerator, customGenerator, loader) {\n    'use strict';\n\n    window.Variables = {\n        textareaElementId: null,\n        variablesContent: null,\n        dialogWindow: null,\n        dialogWindowId: 'variables-chooser',\n        overlayShowEffectOptions: null,\n        overlayHideEffectOptions: null,\n        insertFunction: 'Variables.insertVariable',\n        selectedPlaceholder: null,\n        isEditMode: null,\n        editor: null,\n\n        /**\n         * Initialize Variables handler.\n         *\n         * @param {*} textareaElementId\n         * @param {Function} insertFunction\n         * @param {Object} editor\n         * @param {Object} selectedPlaceholder\n         */\n        init: function (textareaElementId, insertFunction, editor, selectedPlaceholder) {\n            if ($(textareaElementId)) {\n                this.textareaElementId = textareaElementId;\n            }\n\n            if (insertFunction) {\n                this.insertFunction = insertFunction;\n            }\n\n            if (selectedPlaceholder) {\n                this.selectedPlaceholder = selectedPlaceholder;\n            }\n\n            if (editor) {\n                this.editor = editor;\n            }\n        },\n\n        /**\n         * Reset data.\n         */\n        resetData: function () {\n            this.variablesContent = null;\n            this.dialogWindow = null;\n        },\n\n        /**\n         * Open variables chooser slideout.\n         *\n         * @param {Object} variables\n         */\n        openVariableChooser: function (variables) {\n            if (variables) {\n                this.openDialogWindow(variables);\n            }\n        },\n\n        /**\n         * Close variables chooser slideout dialog window.\n         */\n        closeDialogWindow: function () {\n            jQuery('#' + this.dialogWindowId).modal('closeModal');\n        },\n\n        /**\n         * Init ui component grid on the form\n         *\n         * @return void\n         */\n        initUiGrid: function () {\n            mageApply.apply(document.getElementById(this.dialogWindow));\n            jQuery('#' + this.dialogWindowId).applyBindings();\n            loader.get('variables_modal.variables_modal.variables').hide();\n        },\n\n        /**\n         * Open slideout dialog window.\n         *\n         * @param {*} variablesContent\n         * @param {Object} selectedElement\n         */\n        openDialogWindow: function (variablesContent, selectedElement) {\n\n            var html = utils.copy(variablesContent),\n                self = this;\n\n            jQuery('<div id=\"' + this.dialogWindowId + '\">' + html + '</div>').modal({\n                title: self.isEditMode ? $t('Edit Variable') : $t('Insert Variable'),\n                type: 'slide',\n                buttons: self.getButtonsConfig(self.isEditMode),\n\n                /**\n                 * @param {jQuery.Event} e\n                 * @param {Object} modal\n                 */\n                closed: function (e, modal) {\n                    modal.modal.remove();\n                    registry.get(\n                        'variables_modal.variables_modal.variables.variable_selector',\n                        function (radioSelect) {\n                            radioSelect.selectedVariableCode('');\n                        }\n                    );\n                }\n            });\n\n            this.selectedPlaceholder = selectedElement;\n\n            this.addNotAvailableMessage(selectedElement);\n\n            jQuery('#' + this.dialogWindowId).modal('openModal');\n\n            if (typeof selectedElement !== 'undefined') {\n                registry.get(\n                    'variables_modal.variables_modal.variables.variable_selector',\n                    function (radioSelect) {\n                        radioSelect.selectedVariableCode(MagentovariablePlugin.getElementVariablePath(selectedElement));\n                    }\n                );\n            }\n        },\n\n        /**\n         * Add message to slide out that variable is no longer available\n         *\n         * @param {Object} selectedElement\n         */\n        addNotAvailableMessage: function (selectedElement) {\n            var name,\n                msg,\n                variablePath,\n                $wrapper,\n                lostVariableClass = 'magento-placeholder-error';\n\n            if (\n                this.isEditMode &&\n                typeof selectedElement !== 'undefined' &&\n                jQuery(selectedElement).hasClass(lostVariableClass)\n            ) {\n\n                variablePath = MagentovariablePlugin.getElementVariablePath(selectedElement);\n                name = variablePath.split(':');\n                msg = $t('The variable %1 is no longer available. Select a different variable.')\n                    .replace('%1', name[1]);\n\n                jQuery('body').notification('clear')\n                    .notification('add', {\n                        error: true,\n                        message: msg,\n\n                        /**\n                         * @param {String} message\n                         */\n                        insertMethod: function (message) {\n                            $wrapper = jQuery('<div></div>').html(message);\n\n                            jQuery('.modal-header .page-main-actions').after($wrapper);\n                        }\n                    });\n            }\n        },\n\n        /**\n         * Get selected variable directive.\n         *\n         * @returns {*}\n         */\n        getVariableCode: function () {\n            var code = registry.get('variables_modal.variables_modal.variables.variable_selector')\n                    .selectedVariableCode(),\n                directive = code;\n\n            // processing switch here as content must contain only path/code without type\n            if (typeof code !== 'undefined') {\n                if (code.match('^default:')) {\n                    directive = configGenerator.processConfig(code.replace('default:', ''));\n                } else if (code.match('^custom:')) {\n                    directive = customGenerator.processConfig(code.replace('custom:', ''));\n                }\n\n                return directive;\n            }\n        },\n\n        /**\n         * Get buttons configuration for slideout dialog.\n         *\n         * @param {Boolean} isEditMode\n         *\n         * @returns {Array}\n         */\n        getButtonsConfig: function (isEditMode) {\n\n            var self = this,\n                buttonsData;\n\n            buttonsData = [\n                {\n\n                    text: $t('Cancel'),\n                    'class': 'action-scalable cancel',\n\n                    /**\n                     * @param {jQuery.Event} event\n                     */\n                    click: function (event) {\n                        this.closeModal(event);\n                    }\n                },\n                {\n\n                    text: isEditMode ? $t('Save') : $t('Insert Variable'),\n                    class: 'action-primary ' + (isEditMode ? '' : 'disabled'),\n                    attr: {\n                        'id': 'insert_variable'\n                    },\n\n                    /**\n                     * Insert Variable\n                     */\n                    click: function () {\n                        self.insertVariable(self.getVariableCode());\n                    }\n                }\n            ];\n\n            return buttonsData;\n        },\n\n        /**\n         * Prepare variables row.\n         *\n         * @param {String} varValue\n         * @param {*} varLabel\n         * @return {String}\n         * @deprecated This method isn't relevant after ui changes\n         */\n        prepareVariableRow: function (varValue, varLabel) {\n            var value = varValue.replace(/\"/g, '&quot;').replace(/'/g, '\\\\&#39;');\n\n            return '<a href=\"#\" onclick=\"' +\n                this.insertFunction +\n                '(\\'' +\n                value +\n                '\\');return false;\">' +\n                varLabel +\n                '</a>';\n        },\n\n        /**\n         * Insert variable into WYSIWYG editor.\n         *\n         * @param {*} value\n         * @return {Object}\n         */\n        insertVariable: function (value) {\n            var windowId = this.dialogWindowId,\n                textareaElm, scrollPos, wysiwygEditorFocused;\n\n            jQuery('#' + windowId).modal('closeModal');\n            textareaElm = $(this.textareaElementId);\n\n            //to support switching between wysiwyg editors\n            wysiwygEditorFocused = wysiwyg && wysiwyg.activeEditor();\n\n            if (wysiwygEditorFocused && wysiwyg.get(this.textareaElementId)) {\n                if (jQuery(this.selectedPlaceholder).hasClass('magento-placeholder')) {\n                    wysiwyg.setCaretOnElement(this.selectedPlaceholder, 1);\n                }\n                wysiwyg.insertContent(value, false);\n\n                if (this.selectedPlaceholder && jQuery(this.selectedPlaceholder).hasClass('magento-placeholder')) {\n                    this.selectedPlaceholder.remove();\n                }\n\n            } else if (textareaElm) {\n                scrollPos = textareaElm.scrollTop;\n                updateElementAtCursor(textareaElm, value);\n                textareaElm.focus();\n                textareaElm.scrollTop = scrollPos;\n                jQuery(textareaElm).trigger('change');\n                textareaElm = null;\n            }\n\n            return this;\n        }\n\n    };\n\n    window.MagentovariablePlugin = {\n        editor: null,\n        variables: null,\n        textareaId: null,\n\n        /**\n         * Bind editor.\n         *\n         * @param {*} editor\n         */\n        setEditor: function (editor) {\n            this.editor = editor;\n        },\n\n        /**\n         * Load variables chooser.\n         *\n         * @param {String} url\n         * @param {*} textareaId\n         * @param {Object} selectedElement\n         *\n         * @return {Object}\n         */\n        loadChooser: function (url, textareaId, selectedElement) {\n            this.textareaId = textareaId;\n\n            new Ajax.Request(url, {\n                parameters: {},\n                onComplete: function (transport) {\n                    Variables.init(this.textareaId, 'MagentovariablePlugin.insertVariable', this.editor);\n                    Variables.isEditMode = !!this.getElementVariablePath(selectedElement);\n                    this.variablesContent = transport.responseText;\n                    Variables.openDialogWindow(this.variablesContent, selectedElement);\n                    Variables.initUiGrid();\n                }.bind(this)\n            });\n\n            return this;\n        },\n\n        /**\n         * Open variables chooser window.\n         *\n         * @param {*} variables\n         * @deprecated This method isn't relevant after ui changes\n         */\n        openChooser: function (variables) {\n            Variables.openVariableChooser(variables);\n        },\n\n        /**\n         * Insert variable.\n         *\n         * @param {*} value\n         *\n         * @return {Object}\n         */\n        insertVariable: function (value) {\n            if (this.textareaId) {\n                Variables.init(this.textareaId);\n                Variables.insertVariable(value);\n            } else {\n                Variables.closeDialogWindow();\n                Variables.insertVariable(value);\n            }\n\n            return this;\n        },\n\n        /**\n         * Get element variable path.\n         *\n         * @param {Object} element\n         * @returns {String}\n         */\n        getElementVariablePath: function (element) {\n            var type, code;\n\n            if (!element || !jQuery(element).hasClass('magento-variable')) {\n                return '';\n            }\n            type = jQuery(element).hasClass('magento-custom-var') ? 'custom' : 'default';\n            code = Base64.idDecode(element.getAttribute('id'));\n\n            return type + ':' + code;\n        }\n    };\n});\n","Magento_Variable/js/config-directive-generator.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'underscore'\n], function (_) {\n    'use strict';\n\n    return _.extend({\n        directiveTemplate: '{{config path=\"%s\"}}',\n\n        /**\n         * @param {String} path\n         * @return {String}\n         */\n        processConfig: function (path) {\n            return this.directiveTemplate.replace('%s', path);\n\n        }\n\n    });\n\n});\n","Magento_Variable/js/custom-directive-generator.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'underscore'\n], function (_) {\n    'use strict';\n\n    return _.extend({\n        directiveTemplate: '{{customVar code=%s}}',\n\n        /**\n         * @param {String} path\n         * @return {String}\n         */\n        processConfig: function (path) {\n            return this.directiveTemplate.replace('%s', path);\n        }\n\n    });\n\n});\n","Magento_Variable/js/grid/columns/radioselect.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'underscore',\n    'mage/translate',\n    'Magento_Ui/js/grid/columns/column',\n    'jquery'\n], function (_, $t, Column, jQuery) {\n    'use strict';\n\n    return Column.extend({\n        defaults: {\n            bodyTmpl: 'Magento_Variable/grid/cells/radioselect',\n            draggable: false,\n            sortable: false,\n            selectedVariableCode: null,\n            selectedVariableType: null\n        },\n\n        /**\n         * Calls 'initObservable' of parent\n         *\n         * @returns {Object} Chainable.\n         */\n        initObservable: function () {\n            this._super().observe(['selectedVariableCode']);\n\n            return this;\n        },\n\n        /**\n         * Remove disable class from Insert Variable button after Variable has been chosen.\n         *\n         * @return {Boolean}\n         */\n        selectVariable: function () {\n            if (jQuery('#insert_variable').hasClass('disabled')) {\n                jQuery('#insert_variable').removeClass('disabled');\n            }\n\n            return true;\n        }\n    });\n});\n","js/theme.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine('globalNavigationScroll', [\n    'jquery'\n], function ($) {\n    'use strict';\n\n    var win = $(window),\n        subMenuClass = '.submenu',\n        fixedClassName = '_fixed',\n        menu = $('.menu-wrapper'),\n        content = $('.page-wrapper'),\n        menuItems = $('#nav').children('li'),\n        winHeight,\n        menuHeight = menu.height(),\n        menuScrollMax = 0,\n        submenuHeight = 0,\n        contentHeight,\n        winTop = 0,\n        winTopLast = 0,\n        scrollStep = 0,\n        nextTop = 0;\n\n    /**\n     * Check if menu is fixed\n     * @returns {Boolean}\n     */\n    function isMenuFixed() {\n        return menuHeight < contentHeight && contentHeight > winHeight;\n    }\n\n    /**\n     * Check if class exist than add or do nothing\n     * @param {jQuery} el\n     * @param {String} $class\n     */\n    function checkAddClass(el, $class) {\n        if (!el.hasClass($class)) {\n            el.addClass($class);\n        }\n    }\n\n    /**\n     * Check if class exist than remove or do nothing\n     * @param {jQuery} el\n     * @param {String} $class\n     */\n    function checkRemoveClass(el, $class) {\n        if (el.hasClass($class)) {\n            el.removeClass($class);\n        }\n    }\n\n    /**\n     * Calculate and apply menu position\n     */\n    function positionMenu() {\n\n        //  Spotting positions and heights\n        winHeight = win.height();\n        contentHeight = content.height();\n        winTop = win.scrollTop();\n        scrollStep = winTop - winTopLast;\n\n        if (isMenuFixed()) { // fixed menu cases\n\n            checkAddClass(menu, fixedClassName);\n\n            if (menuHeight > winHeight) { // smart scroll cases\n\n                if (winTop > winTopLast) { //eslint-disable-line max-depth\n\n                    menuScrollMax = menuHeight - winHeight;\n\n                    nextTop < menuScrollMax - scrollStep ?\n                        nextTop += scrollStep : nextTop = menuScrollMax;\n\n                    menu.css('top', -nextTop);\n\n                } else if (winTop <= winTopLast) { // scroll up\n\n                    nextTop > -scrollStep ?\n                        nextTop += scrollStep : nextTop = 0;\n\n                    menu.css('top', -nextTop);\n\n                }\n\n            }\n\n        } else { // static menu cases\n            checkRemoveClass(menu, fixedClassName);\n            menu.css('top', 'auto');\n        }\n\n        //  Save previous window scrollTop\n        winTopLast = winTop;\n\n    }\n\n    positionMenu(); // page start calculation\n\n    //  Change position on scroll\n    win.on('scroll', function () {\n        positionMenu();\n    });\n\n    win.on('resize', function () {\n\n        winHeight = win.height();\n\n        //  Reset position if fixed and out of smart scroll\n        if (menuHeight < contentHeight && menuHeight <= winHeight) {\n            menu.removeAttr('style');\n            menuItems.off();\n        }\n\n    });\n\n    //  Add event to menuItems to check submenu overlap\n    menuItems.on('click', function () {\n\n        var submenu = $(this).children(subMenuClass),\n            delta,\n            logo = $('.logo')[0].offsetHeight;\n\n        submenuHeight = submenu.height();\n\n        if (submenuHeight > menuHeight && menuHeight + logo > winHeight) {\n            menu.height(submenuHeight - logo);\n            delta = -menu.position().top;\n            window.scrollTo(0, 0);\n            positionMenu();\n            window.scrollTo(0, delta);\n            positionMenu();\n            menuHeight = submenuHeight;\n        }\n    });\n\n});\n\ndefine('globalNavigation', [\n    'jquery',\n    'jquery/ui',\n    'globalNavigationScroll'\n], function ($) {\n    'use strict';\n\n    $.widget('mage.globalNavigation', {\n        options: {\n            selectors: {\n                menu: '#nav',\n                currentItem: '._current',\n                topLevelItem: '.level-0',\n                topLevelHref: '> a',\n                subMenu: '> .submenu',\n                closeSubmenuBtn: '[data-role=\"close-submenu\"]'\n            },\n            overlayTmpl: '<div class=\"admin__menu-overlay\"></div>'\n        },\n\n        /** @inheritdoc */\n        _create: function () {\n            var selectors = this.options.selectors;\n\n            this.menu = this.element;\n            this.menuLinks = $(selectors.topLevelHref, selectors.topLevelItem);\n            this.closeActions = $(selectors.closeSubmenuBtn);\n\n            this._initOverlay()\n                ._bind();\n        },\n\n        /**\n         * @return {Object}\n         * @private\n         */\n        _initOverlay: function () {\n            this.overlay = $(this.options.overlayTmpl).appendTo('body').hide(0);\n\n            return this;\n        },\n\n        /**\n         * @private\n         */\n        _bind: function () {\n            var focus = this._focus.bind(this),\n                open = this._open.bind(this),\n                blur = this._blur.bind(this),\n                keyboard = this._keyboard.bind(this);\n\n            this.menuLinks\n                .on('focus', focus)\n                .on('click', open);\n\n            this.menuLinks.last().on('blur', blur);\n\n            this.closeActions.on('keydown', keyboard);\n        },\n\n        /**\n         * Remove active class from current menu item\n         * Turn back active class to current page menu item\n         */\n        _blur: function (e) {\n            var selectors = this.options.selectors,\n                menuItem = $(e.target).closest(selectors.topLevelItem),\n                currentItem = $(selectors.menu).find(selectors.currentItem);\n\n            menuItem.removeClass('_active');\n            currentItem.addClass('_active');\n        },\n\n        /**\n         * Add focus to active menu item\n         */\n        _keyboard: function (e) {\n            var selectors = this.options.selectors,\n                menuItem = $(e.target).closest(selectors.topLevelItem);\n\n            if (e.which === 13) {\n                this._close(e);\n                $(selectors.topLevelHref, menuItem).trigger('focus');\n            }\n        },\n\n        /**\n         * Toggle active state on focus\n         */\n        _focus: function (e) {\n            var selectors = this.options.selectors,\n                menuItem = $(e.target).closest(selectors.topLevelItem);\n\n            menuItem.addClass('_active')\n                .siblings(selectors.topLevelItem)\n                .removeClass('_active');\n        },\n\n        /**\n         * @param {jQuery.Event} e\n         * @private\n         */\n        _closeSubmenu: function (e) {\n            var selectors = this.options.selectors,\n                currentItem = $(selectors.menu).find(selectors.currentItem);\n\n            this._close(e);\n\n            currentItem.addClass('_active');\n        },\n\n        /**\n         * @param {jQuery.Event} e\n         * @private\n         */\n        _open: function (e) {\n            var selectors = this.options.selectors,\n                menuItemSelector = selectors.topLevelItem,\n                menuItem = $(e.target).closest(menuItemSelector),\n                subMenu = $(selectors.subMenu, menuItem),\n                close = this._closeSubmenu.bind(this),\n                closeBtn = subMenu.find(selectors.closeSubmenuBtn);\n\n            if (subMenu.length) {\n                e.preventDefault();\n            }\n            closeBtn.on('click', close);\n\n            if ($(menuItem).hasClass('_show')) {\n                closeBtn.trigger('click');\n            } else {\n                menuItem.addClass('_show')\n                    .siblings(menuItemSelector)\n                    .removeClass('_show');\n                subMenu.attr('aria-expanded', 'true');\n                this.overlay.show(0).on('click', close);\n                this.menuLinks.last().off('blur');\n            }\n        },\n\n        /**\n         * @param {jQuery.Event} e\n         * @private\n         */\n        _close: function (e) {\n            var selectors = this.options.selectors,\n                menuItem = this.menu.find(selectors.topLevelItem + '._show'),\n                subMenu = $(selectors.subMenu, menuItem),\n                closeBtn = subMenu.find(selectors.closeSubmenuBtn),\n                blur = this._blur.bind(this);\n\n            e.preventDefault();\n\n            this.overlay.hide(0).off('click');\n\n            this.menuLinks.last().on('blur', blur);\n\n            closeBtn.off('click');\n\n            subMenu.attr('aria-expanded', 'false');\n\n            menuItem.removeClass('_show _active');\n        }\n    });\n\n    return $.mage.globalNavigation;\n});\n\ndefine('globalSearch', [\n    'jquery',\n    'Magento_Ui/js/lib/key-codes',\n    'jquery-ui-modules/widget'\n], function ($, keyCodes) {\n    'use strict';\n\n    $.widget('mage.globalSearch', {\n        options: {\n            field: '.search-global-field',\n            fieldActiveClass: '_active',\n            input: '#search-global'\n        },\n\n        /** @inheritdoc */\n        _create: function () {\n            this.field = $(this.options.field);\n            this.input = $(this.options.input);\n            this._events();\n        },\n\n        /**\n         * @private\n         */\n        _events: function () {\n            var self = this;\n\n            this.input.on('blur.resetGlobalSearchForm', function () {\n                if (!self.input.val()) {\n                    self.field.removeClass(self.options.fieldActiveClass);\n                }\n            });\n\n            this.input.on('focus.activateGlobalSearchForm', function () {\n                self.field.addClass(self.options.fieldActiveClass);\n            });\n\n            $(document).on('keydown.activateGlobalSearchForm', function (event) {\n                var inputs = [\n                    'input',\n                    'select',\n                    'textarea'\n                ];\n\n                if (keyCodes[event.which] !== 'forwardSlashKey' ||\n                    inputs.indexOf(event.target.tagName.toLowerCase()) !== -1 ||\n                    event.target.isContentEditable\n                ) {\n                    return;\n                }\n\n                event.preventDefault();\n\n                self.input.focus();\n            });\n        }\n    });\n\n    return $.mage.globalSearch;\n});\n\ndefine('modalPopup', [\n    'jquery',\n    'jquery/ui'\n], function ($) {\n    'use strict';\n\n    $.widget('mage.modalPopup', {\n        options: {\n            popup: '.popup',\n            btnDismiss: '[data-dismiss=\"popup\"]',\n            btnHide: '[data-hide=\"popup\"]'\n        },\n\n        /** @inheritdoc */\n        _create: function () {\n            this.fade = this.element;\n            this.popup = $(this.options.popup, this.fade);\n            this.btnDismiss = $(this.options.btnDismiss, this.popup);\n            this.btnHide = $(this.options.btnHide, this.popup);\n\n            this._events();\n        },\n\n        /**\n         * @private\n         */\n        _events: function () {\n            var self = this;\n\n            this.btnDismiss\n                .on('click.dismissModalPopup', function () {\n                    self.fade.remove();\n                });\n\n            this.btnHide\n                .on('click.hideModalPopup', function () {\n                    self.fade.hide();\n                });\n        }\n    });\n\n    return $.mage.modalPopup;\n});\n\ndefine('useDefault', [\n    'jquery',\n    'jquery/ui'\n], function ($) {\n    'use strict';\n\n    $.widget('mage.useDefault', {\n        options: {\n            field: '.field',\n            useDefault: '.use-default',\n            checkbox: '.use-default-control',\n            label: '.use-default-label'\n        },\n\n        /** @inheritdoc */\n        _create: function () {\n            this.el = this.element;\n            this.field = $(this.el).closest(this.options.field);\n            this.useDefault = $(this.options.useDefault, this.field);\n            this.checkbox = $(this.options.checkbox, this.useDefault);\n            this.label = $(this.options.label, this.useDefault);\n            this.origValue = this.el.attr('data-store-label');\n\n            this._events();\n        },\n\n        /**\n         * @private\n         */\n        _events: function () {\n            var self = this;\n\n            this.el.on(\n                    'change.toggleUseDefaultVisibility keyup.toggleUseDefaultVisibility',\n                    $.proxy(this._toggleUseDefaultVisibility, this)\n                ).trigger('change.toggleUseDefaultVisibility');\n\n            this.checkboxon('change.setOrigValue', function () {\n                if ($(this).prop('checked')) {\n                    self.el\n                        .val(self.origValue)\n                        .trigger('change.toggleUseDefaultVisibility');\n\n                    $(this).prop('checked', false);\n                }\n            });\n        },\n\n        /**\n         * @private\n         */\n        _toggleUseDefaultVisibility: function () {\n            var curValue = this.el.val(),\n                origValue = this.origValue;\n\n            this[curValue != origValue ? '_show' : '_hide'](); //eslint-disable-line eqeqeq\n        },\n\n        /**\n         * @private\n         */\n        _show: function () {\n            this.useDefault.show();\n        },\n\n        /**\n         * @private\n         */\n        _hide: function () {\n            this.useDefault.hide();\n        }\n    });\n\n    return $.mage.useDefault;\n});\n\ndefine('loadingPopup', [\n    'jquery',\n    'jquery/ui'\n], function ($) {\n    'use strict';\n\n    $.widget('mage.loadingPopup', {\n        options: {\n            message: 'Please wait...',\n            timeout: 5000,\n            timeoutId: null,\n            callback: null,\n            template: null\n        },\n\n        /** @inheritdoc */\n        _create: function () {\n            this.template =\n                '<div class=\"popup popup-loading\">' +\n                '<div class=\"popup-inner\">' + this.options.message + '</div>' +\n                '</div>';\n\n            this.popup = $(this.template);\n\n            this._show();\n            this._events();\n        },\n\n        /**\n         * @private\n         */\n        _events: function () {\n            var self = this;\n\n            this.element\n                .on('showLoadingPopup', function () {\n                    self._show();\n                })\n                .on('hideLoadingPopup', function () {\n                    self._hide();\n                });\n        },\n\n        /**\n         * @private\n         */\n        _show: function () {\n            var options = this.options,\n                timeout = options.timeout;\n\n            $('body').trigger('processStart');\n\n            if (timeout) {\n                options.timeoutId = setTimeout(this._delayedHide.bind(this), timeout);\n            }\n        },\n\n        /**\n         * @private\n         */\n        _hide: function () {\n            $('body').trigger('processStop');\n        },\n\n        /**\n         * @private\n         */\n        _delayedHide: function () {\n            this._hide();\n\n            this.options.callback && this.options.callback();\n\n            this.options.timeoutId && clearTimeout(this.options.timeoutId);\n        }\n    });\n\n    return $.mage.loadingPopup;\n});\n\ndefine('collapsable', [\n    'jquery',\n    'jquery/ui',\n    'jquery/jquery.tabs'\n], function ($) {\n    'use strict';\n\n    $.widget('mage.collapsable', {\n        options: {\n            parent: null,\n            openedClass: 'opened',\n            wrapper: '.fieldset-wrapper'\n        },\n\n        /** @inheritdoc */\n        _create: function () {\n            this._events();\n        },\n\n        /** @inheritdoc */\n        _events: function () {\n            var self = this;\n\n            this.element\n                .on('show.bs.collapse', function (e) {\n                    var fieldsetWrapper = $(this).closest(self.options.wrapper);\n\n                    fieldsetWrapper.addClass(self.options.openedClass);\n                    e.stopPropagation();\n                })\n                .on('hide.bs.collapse', function (e) {\n                    var fieldsetWrapper = $(this).closest(self.options.wrapper);\n\n                    fieldsetWrapper.removeClass(self.options.openedClass);\n                    e.stopPropagation();\n                });\n        }\n    });\n\n    return $.mage.collapsable;\n});\n\ndefine('js/theme', [\n    'jquery',\n    'mage/smart-keyboard-handler',\n    'collapsable',\n    'domReady!'\n], function ($, keyboardHandler) {\n    'use strict';\n\n    /* @TODO refactor collapsible as widget and avoid logic binding with such a general selectors */\n    $('.collapse').collapsable();\n\n    $.each($('.entry-edit'), function (i, entry) {\n        $('.collapse:first', entry).filter(function () {\n            return $(this).data('collapsed') !== true;\n        }).collapse('show');\n    });\n\n    keyboardHandler.apply();\n});\n","Magento_MediaGalleryUi/js/container.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'uiElement',\n    'jquery'\n], function (Element, $) {\n    'use strict';\n\n    return Element.extend({\n        defaults: {\n            containerSelector: '.media-gallery-container',\n            masonryComponentPath: 'media_gallery_listing.media_gallery_listing.media_gallery_columns',\n            modules: {\n                masonry: '${ $.masonryComponentPath }'\n            }\n        },\n\n        /**\n         * Init component\n         *\n         * @return {exports}\n         */\n        initialize: function () {\n            this._super();\n\n            $(this.containerSelector).applyBindings();\n\n            return this;\n        }\n    });\n});\n","Magento_MediaGalleryUi/js/image-uploader.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'uiComponent',\n    'jquery',\n    'underscore',\n    'Magento_Ui/js/lib/validation/validator',\n    'mage/translate',\n    'jquery/file-uploader'\n], function (Component, $, _, validator, $t) {\n    'use strict';\n\n    return Component.extend({\n        defaults: {\n            imageUploadInputSelector: '#image-uploader-form',\n            directoriesPath: 'media_gallery_listing.media_gallery_listing.media_gallery_directories',\n            actionsPath: 'media_gallery_listing.media_gallery_listing.media_gallery_columns.thumbnail_url',\n            messagesPath: 'media_gallery_listing.media_gallery_listing.messages',\n            imageUploadUrl: '',\n            acceptFileTypes: '',\n            allowedExtensions: '',\n            maxFileSize: '',\n            maxFileNameLength: 90,\n            loader: false,\n            modules: {\n                directories: '${ $.directoriesPath }',\n                actions: '${ $.actionsPath }',\n                mediaGridMessages: '${ $.messagesPath }',\n                sortBy: '${ $.sortByName }',\n                listingPaging: '${ $.listingPagingName }'\n            }\n        },\n\n        /**\n         * Init component\n         *\n         * @return {exports}\n         */\n        initialize: function () {\n            this._super().observe(\n                [\n                    'loader',\n                    'count'\n                ]\n            );\n\n            return this;\n        },\n\n        /**\n         * Initializes file upload library\n         */\n        initializeFileUpload: function () {\n            $(this.imageUploadInputSelector).fileupload({\n                url: this.imageUploadUrl,\n                acceptFileTypes: this.acceptFileTypes,\n                allowedExtensions: this.allowedExtensions,\n                maxFileSize: this.maxFileSize,\n\n                /**\n                 * Extending the form data\n                 *\n                 * @param {Object} form\n                 * @returns {Array}\n                 */\n                formData: function (form) {\n                    return form.serializeArray().concat(\n                        [{\n                            name: 'isAjax',\n                            value: true\n                        },\n                        {\n                            name: 'form_key',\n                            value: window.FORM_KEY\n                        },\n                        {\n                            name: 'target_folder',\n                            value: this.getTargetFolder()\n                        }]\n                    );\n                }.bind(this),\n\n                add: function (e, data) {\n                    if (!this.isSizeExceeded(data.files[0]).passed) {\n                        this.addValidationErrorMessage(\n                            $t('Cannot upload \"%1\". File exceeds maximum file size limit.')\n                                .replace('%1', data.files[0].name)\n                        );\n\n                        return;\n                    } else if (!this.isFileNameLengthExceeded(data.files[0]).passed) {\n                        this.addValidationErrorMessage(\n                            $t('Cannot upload \"%1\". Filename is too long, must be 90 characters or less.')\n                                .replace('%1', data.files[0].name)\n                        );\n\n                        return;\n                    }\n\n                    this.showLoader();\n                    this.count(1);\n                    data.submit();\n                }.bind(this),\n\n                stop: function () {\n                    this.openNewestImages();\n                    this.mediaGridMessages().scheduleCleanup();\n                }.bind(this),\n\n                start: function () {\n                    this.mediaGridMessages().clear();\n                }.bind(this),\n\n                done: function (e, data) {\n                    var response = data.jqXHR.responseJSON;\n\n                    if (!response) {\n                        this.showErrorMessage(data, $t('Could not upload the asset.'));\n\n                        return;\n                    }\n\n                    if (!response.success) {\n                        this.showErrorMessage(data, response.message);\n\n                        return;\n                    }\n                    this.showSuccessMessage(data);\n                    this.hideLoader();\n                    this.actions().reloadGrid();\n                }.bind(this)\n            });\n        },\n\n        /**\n         * Add error message after validation error.\n         *\n         * @param {String} message\n         */\n        addValidationErrorMessage: function (message) {\n            this.mediaGridMessages().add('error', message);\n\n            this.count() < 2 || this.mediaGridMessages().scheduleCleanup();\n        },\n\n        /**\n         * Checks if size of provided file exceeds\n         * defined in configuration size limits.\n         *\n         * @param {Object} file - File to be checked.\n         * @returns {Boolean}\n         */\n        isSizeExceeded: function (file) {\n            return validator('validate-max-size', file.size, this.maxFileSize);\n        },\n\n        /**\n         * Checks if name length of provided file exceeds\n         * defined in configuration size limits.\n         *\n         * @param {Object} file - File to be checked.\n         * @returns {Boolean}\n         */\n        isFileNameLengthExceeded: function (file) {\n            return validator('max_text_length', file.name, this.maxFileNameLength);\n        },\n\n        /**\n         * Go to recently uploaded images if at least one uploaded successfully\n         */\n        openNewestImages: function () {\n            this.mediaGridMessages().get().each(function (message) {\n                if (message.code === 'success') {\n                    this.actions().deselectImage();\n                    this.sortBy().selectDefaultOption();\n                    this.listingPaging().goFirst();\n\n                    return false;\n                }\n            }.bind(this));\n        },\n\n        /**\n         * Show error meassages with file name.\n         *\n         * @param {Object} data\n         * @param {String} message\n         */\n        showErrorMessage: function (data, message) {\n            data.files.each(function (file) {\n                this.mediaGridMessages().add(\n                    'error',\n                    file.name + ': ' + $t(message)\n                );\n            }.bind(this));\n\n            this.hideLoader();\n        },\n\n        /**\n         * Show success message, and files counts\n         */\n        showSuccessMessage: function () {\n            this.mediaGridMessages().messages.remove(function (item) {\n                return item.code === 'success';\n            });\n            this.mediaGridMessages().add('success', $t('Assets have been successfully uploaded!'));\n            this.count(this.count() + 1);\n\n        },\n\n        /**\n         * Gets Media Gallery selected folder\n         *\n         * @returns {String}\n         */\n        getTargetFolder: function () {\n\n            if (_.isUndefined(this.directories().activeNode()) ||\n                _.isNull(this.directories().activeNode())) {\n                return '/';\n            }\n\n            return this.directories().activeNode();\n        },\n\n        /**\n         * Shows spinner loader\n         */\n        showLoader: function () {\n            this.loader(true);\n        },\n\n        /**\n         * Hides spinner loader\n         */\n        hideLoader: function () {\n            this.loader(false);\n        }\n    });\n});\n","Magento_MediaGalleryUi/js/validation/validate-image-title.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'jquery/validate',\n    'mage/translate'\n], function ($) {\n    'use strict';\n\n    $.validator.addMethod(\n        'validate-image-title', function (value) {\n            return /^[a-zA-Z0-9\\-\\_\\.\\,\\s]+$/i.test(value);\n\n        }, $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9), dots (.), commas(,), ' +\n            'underscores (_), dashes(-) and spaces on this field.'));\n});\n","Magento_MediaGalleryUi/js/validation/validate-image-keyword.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'jquery/validate',\n    'mage/translate'\n], function ($, validate, $t) {\n    'use strict';\n\n    $.validator.addMethod(\n        'validate-image-keyword', function (value) {\n            return /^[a-zA-Z0-9\\-\\_\\.\\,]+$|^$/i.test(value);\n\n        }, $t('Please use only letters (a-z or A-Z), numbers (0-9), dots (.), commas(,), ' +\n            'underscores (_) and dashes(-) on this field.'));\n});\n","Magento_MediaGalleryUi/js/validation/validate-image-description.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'jquery/validate',\n    'mage/translate'\n], function ($) {\n    'use strict';\n\n    $.validator.addMethod(\n        'validate-image-description', function (value) {\n            return /^[a-zA-Z0-9\\-\\_\\.\\,\\n\\s]+$|^$/i.test(value);\n\n        }, $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9), ' +\n            'dots (.), commas(,), underscores (_), dashes (-), and spaces on this field.'));\n});\n","Magento_MediaGalleryUi/js/directory/directories.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.g\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'uiComponent',\n    'Magento_Ui/js/modal/confirm',\n    'Magento_Ui/js/modal/alert',\n    'underscore',\n    'Magento_Ui/js/modal/prompt',\n    'Magento_MediaGalleryUi/js/directory/actions/createDirectory',\n    'Magento_MediaGalleryUi/js/directory/actions/deleteDirectory',\n    'mage/translate',\n    'validation'\n], function ($, Component, confirm, uiAlert, _, prompt, createDirectory, deleteDirectory, $t) {\n    'use strict';\n\n    return Component.extend({\n        defaults: {\n            allowedActions: [],\n            directoryTreeSelector: '#media-gallery-directory-tree',\n            deleteButtonSelector: '#delete_folder',\n            createFolderButtonSelector: '#create_folder',\n            messageDelay: 5,\n            selectedFolder: null,\n            messagesName: 'media_gallery_listing.media_gallery_listing.messages',\n            modules: {\n                directoryTree: '${ $.parentName }.media_gallery_directories',\n                messages: '${ $.messagesName }'\n            }\n        },\n\n        /**\n         * Initializes media gallery directories component.\n         *\n         * @returns {Sticky} Chainable.\n         */\n        initialize: function () {\n            this._super().observe(['selectedFolder']);\n            this._addValidation();\n            this.initEvents();\n\n            return this;\n        },\n\n        /**\n          * Initialize directories events\n          */\n        initEvents: function () {\n            $(this.deleteButtonSelector).on('delete_folder', function () {\n                this.deleteFolder();\n            }.bind(this));\n\n            $(this.createFolderButtonSelector).on('create_folder', function () {\n                this.createFolder();\n            }.bind(this));\n        },\n\n        /**\n         * Show confirmation popup and create folder based on user input\n         */\n        createFolder: function () {\n            this.getPrompt({\n                title: $t('New Folder Name:'),\n                content: '',\n                actions: {\n                    /**\n                     * Confirm action\n                     */\n                    confirm: function (folderName) {\n                        createDirectory(\n                            this.directoryTree().createDirectoryUrl,\n                            [this.getNewFolderPath(folderName)]\n                        ).then(function () {\n                            this.directoryTree().reloadJsTree().then(function () {\n                                this.directoryTree().locateNode(this.getNewFolderPath(folderName));\n                            }.bind(this));\n                        }.bind(this)).fail(function (error) {\n                            uiAlert({\n                                content: error\n                            });\n                        });\n                    }.bind(this)\n                },\n                buttons: [{\n                    text: $t('Cancel'),\n                    class: 'action-secondary action-dismiss',\n\n                    /**\n                     * Close modal\n                     */\n                    click: function () {\n                        this.closeModal();\n                    }\n                }, {\n                    text: $t('Confirm'),\n                    class: 'action-primary action-accept'\n                }]\n            });\n        },\n\n        /**\n         * Return configured path for folder creation.\n         *\n         * @param {String} folderName\n         * @returns {String}\n         */\n        getNewFolderPath: function (folderName) {\n            if (_.isUndefined(this.selectedFolder()) || _.isNull(this.selectedFolder())) {\n                return folderName;\n            }\n\n            return this.selectedFolder() + '/' + folderName;\n        },\n\n        /**\n          * Return configured prompt with input field\n          */\n        getPrompt: function (data) {\n                prompt({\n                    title: $t(data.title),\n                    content:  $t(data.content),\n                    modalClass: 'media-gallery-folder-prompt',\n                    validation: true,\n                    validationRules: ['required-entry', 'validate-filename'],\n                    attributesField: {\n                        name: 'folder_name',\n                        'data-validate': '{required:true, validate-filename}',\n                        maxlength: '128'\n                    },\n                    attributesForm: {\n                        novalidate: 'novalidate',\n                        action: ''\n                    },\n                    context: this,\n                    actions: data.actions,\n                    buttons: data.buttons\n                });\n            },\n\n        /**\n          * Confirmation popup for delete folder action.\n          */\n        deleteFolder: function () {\n            confirm({\n                title: $t('Are you sure you want to delete this folder?'),\n                modalClass: 'delete-folder-confirmation-popup',\n                content: $t('The following folder is going to be deleted: %1')\n                    .replace('%1', this.selectedFolder()),\n                actions: {\n\n                    /**\n                      * Delete folder on button click\n                      */\n                    confirm: function () {\n                        deleteDirectory(\n                            this.directoryTree().deleteDirectoryUrl,\n                            this.selectedFolder()\n                        ).then(function () {\n                            this.directoryTree().removeNode();\n                            this.directoryTree().selectStorageRoot();\n                            $(window).trigger('folderDeleted.enhancedMediaGallery');\n                        }.bind(this)).fail(function (error) {\n                            uiAlert({\n                                content: error\n                            });\n                        });\n                    }.bind(this)\n                }\n            });\n        },\n\n        /**\n         * Set inactive all nodes, adds disable state to Delete Folder Button\n         */\n        setInActive: function () {\n            this.selectedFolder(null);\n            $(this.deleteButtonSelector).attr('disabled', true).addClass('disabled');\n        },\n\n        /**\n         * Set active node, remove disable state from Delete Forlder button\n         *\n         * @param {String} folderId\n         */\n        setActive: function (folderId) {\n            if (!this.allowedActions.includes('delete_folder')) {\n                return;\n            }\n\n            this.selectedFolder(folderId);\n            $(this.deleteButtonSelector).prop('disabled', false).removeClass('disabled');\n        },\n\n        /**\n         * @private\n         */\n        _addValidation: function () {\n            $.validator.addMethod(\n                'validate-filename', function (value) {\n                    return $.mage.isEmptyNoTrim(value) || /^[a-z0-9\\-\\_]+$/si.test(value);\n                },\n                $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9), underscore (_) or hyphen (-) in this field. No spaces or other characters are allowed.')); //eslint-disable-line max-len\n        }\n    });\n});\n","Magento_MediaGalleryUi/js/directory/directoryTree.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* global Base64 */\ndefine([\n    'jquery',\n    'uiComponent',\n    'uiLayout',\n    'underscore',\n    'Magento_MediaGalleryUi/js/directory/actions/createDirectory',\n    'jquery/jstree/jquery.jstree',\n    'Magento_Ui/js/lib/view/utils/async',\n    'Magento_MediaGalleryUi/js/directory/directories'\n], function ($, Component, layout, _, createDirectory) {\n    'use strict';\n\n    return Component.extend({\n        defaults: {\n            allowedActions: [],\n            filterChipsProvider: 'componentType = filters, ns = ${ $.ns }',\n            bookmarkProvider: 'componentType = bookmark, ns = ${ $.ns }',\n            directoryTreeSelector: '#media-gallery-directory-tree',\n            getDirectoryTreeUrl: 'media_gallery/directories/gettree',\n            createDirectoryUrl: 'media_gallery/directories/create',\n            deleteDirectoryUrl: 'media_gallery/directories/delete',\n            jsTreeReloaded: null,\n            modules: {\n                bookmarks: '${ $.bookmarkProvider }',\n                directories: '${ $.name }_directories',\n                filterChips: '${ $.filterChipsProvider }'\n            },\n            listens: {\n                '${ $.provider }:params.filters.path': 'updateSelectedDirectory'\n            },\n            viewConfig: [{\n                component: 'Magento_MediaGalleryUi/js/directory/directories',\n                name: '${ $.name }_directories',\n                allowedActions: '${ $.allowedActions }'\n            }]\n        },\n\n        /**\n         * Initializes media gallery directories component.\n         *\n         * @returns {Sticky} Chainable.\n         */\n        initialize: function () {\n            this._super().observe(['activeNode']).initView();\n\n            $.async(\n                this.directoryTreeSelector,\n                this,\n                function () {\n                    this.initJsTreeEvents();\n                    this.renderDirectoryTree().then(function () {\n                        this.initEvents();\n                    }.bind(this));\n                }.bind(this)\n            );\n\n            return this;\n        },\n\n        /**\n         * Render directory tree component.\n         */\n        renderDirectoryTree: function () {\n            return this.getJsonTree().then(function (data) {\n                this.createFolderIfNotExists(data).then(function (isFolderCreated) {\n                    if (isFolderCreated) {\n                        this.getJsonTree().then(function (newData) {\n                            this.createTree(newData);\n                        }.bind(this));\n                    } else {\n                        this.createTree(data);\n                    }\n                }.bind(this));\n            }.bind(this));\n        },\n\n        /**\n         * Set jstree reloaded\n         *\n         * @param {Boolean} value\n         */\n        setJsTreeReloaded: function (value) {\n            this.jsTreeReloaded = value;\n        },\n\n        /**\n         * Create folder by provided current_tree_path param\n         *\n         * @param {Array} directories\n         */\n        createFolderIfNotExists: function (directories) {\n            var requestedDirectory = this.getRequestedDirectory(),\n                deferred = $.Deferred(),\n                pathArray;\n\n            if (_.isNull(requestedDirectory)) {\n                deferred.resolve(false);\n\n                return deferred.promise();\n            }\n\n            if (this.isDirectoryExist(directories, requestedDirectory)) {\n                deferred.resolve(false);\n\n                return deferred.promise();\n            }\n\n            pathArray = this.convertPathToPathsArray(requestedDirectory);\n\n            $.each(pathArray, function (i, val) {\n                if (this.isDirectoryExist(directories, val)) {\n                    pathArray.splice(i, 1);\n                }\n            }.bind(this));\n\n            createDirectory(\n                this.createDirectoryUrl,\n                pathArray\n            ).then(function () {\n                deferred.resolve(true);\n            });\n\n            return deferred.promise();\n        },\n\n        /**\n         * Verify if directory exists in array\n         *\n         * @param {Array} directories\n         * @param {String} directoryId\n         */\n        isDirectoryExist: function (directories, directoryId) {\n            var found = false;\n\n            /**\n             * Recursive search in array\n             *\n             * @param {Array} data\n             * @param {String} id\n             */\n            function recurse(data, id) {\n                var i;\n\n                for (i = 0; i < data.length; i++) {\n                    if (data[i].id === id) {\n                        found = data[i];\n                        break;\n                    } else if (data[i].children && data[i].children.length) {\n                        recurse(data[i].children, id);\n                    }\n                }\n            }\n\n            recurse(directories, directoryId);\n\n            return found;\n        },\n\n        /**\n         * Convert path string to path array e.g 'path1/path2' -> ['path1', 'path1/path2']\n         *\n         * @param {String} path\n         */\n        convertPathToPathsArray: function (path) {\n            var pathsArray = [],\n                pathString = '',\n                paths = path.split('/');\n\n            $.each(paths, function (i, val) {\n                pathString += i >= 1 ? val : val + '/';\n                pathsArray.push(i >= 1 ? pathString : val);\n            });\n\n            return pathsArray;\n        },\n\n        /**\n         * Initialize child components\n         *\n         * @returns {Object}\n         */\n        initView: function () {\n            layout(this.viewConfig);\n\n            return this;\n        },\n\n        /**\n         * Wait for condition then call provided callback\n         */\n        waitForCondition: function (condition, callback) {\n            if (condition()) {\n                setTimeout(function () {\n                    this.waitForCondition(condition, callback);\n                }.bind(this), 100);\n            } else {\n                callback();\n            }\n        },\n\n        /**\n         * Remove ability to multiple select on nodes\n         */\n        disableMultiselectBehavior: function () {\n            $.jstree.defaults.core.multiple = false;\n        },\n\n        /**\n         *  Handle jstree events\n         */\n        initEvents: function () {\n            this.disableMultiselectBehavior();\n\n            $(window).on('reload.MediaGallery', function () {\n                this.getJsonTree().then(function (data) {\n                    this.createFolderIfNotExists(data).then(function (isCreated) {\n                        if (isCreated) {\n                            this.renderDirectoryTree().then(function () {\n                                this.setJsTreeReloaded(true);\n                                this.initJsTreeEvents();\n                            }.bind(this));\n                        } else {\n                            this.updateSelectedDirectory();\n                        }\n                    }.bind(this));\n                }.bind(this));\n            }.bind(this));\n        },\n\n        /**\n         * Fire event for jstree component\n         */\n        initJsTreeEvents: function () {\n            $(this.directoryTreeSelector).on('select_node.jstree', function (element, data) {\n                this.setActiveNodeFilter(data.node.id);\n                this.setJsTreeReloaded(false);\n            }.bind(this));\n\n            $(this.directoryTreeSelector).on('loaded.jstree', function () {\n                this.updateSelectedDirectory();\n            }.bind(this));\n        },\n\n        /**\n         * Verify directory filter on init event, select folder per directory filter state\n         */\n        updateSelectedDirectory: function () {\n            var currentFilterPath = this.filterChips().filters.path,\n                requestedDirectory = this.getRequestedDirectory(),\n                currentTreePath;\n\n            if (_.isUndefined(currentFilterPath)) {\n                this.clearFiltersHandle();\n\n                return;\n            }\n\n            if (!_.isUndefined(this.bookmarks())) {\n                if (!_.size(this.bookmarks().getViewData(this.bookmarks().defaultIndex))) {\n                    setTimeout(function () {\n                        this.updateSelectedDirectory();\n                    }.bind(this), 500);\n\n                    return;\n                }\n            }\n            currentTreePath = this.isFilterApplied(currentFilterPath) || _.isNull(requestedDirectory) ?\n                currentFilterPath : requestedDirectory;\n\n            if (this.folderExistsInTree(currentTreePath)) {\n                this.locateNode(currentTreePath);\n            } else {\n                this.selectStorageRoot();\n            }\n        },\n\n        /**\n         * Verify if directory exists in folder tree\n         *\n         * @param {String} path\n         */\n        folderExistsInTree: function (path) {\n            if (!_.isUndefined(path)) {\n                return $(this.directoryTreeSelector).jstree('get_node', path);\n            }\n\n            return false;\n        },\n\n        /**\n         * Get requested directory from MediabrowserUtility\n         *\n         * @returns {String|null}\n         */\n        getRequestedDirectory: function () {\n            return !_.isUndefined(window.MediabrowserUtility) && window.MediabrowserUtility.pathId !== '' ?\n                Base64.idDecode(window.MediabrowserUtility.pathId) : null;\n        },\n\n        /**\n         * Check if need to select directory by filters state\n         *\n         * @param {String} currentFilterPath\n         */\n        isFilterApplied: function (currentFilterPath) {\n            return !_.isUndefined(currentFilterPath) && currentFilterPath !== '';\n        },\n\n        /**\n         * Locate and higlight node in jstree by path id.\n         *\n         * @param {String} path\n         */\n        locateNode: function (path) {\n            if ($(this.directoryTreeSelector).jstree('is_selected', path)) {\n                return;\n            }\n            $(this.directoryTreeSelector).jstree('deselect_node',\n                $(this.directoryTreeSelector).jstree('get_selected')\n            );\n            $(this.directoryTreeSelector).jstree('open_node', path);\n            $(this.directoryTreeSelector).jstree('select_node', path, true);\n\n        },\n\n        /**\n         * Clear filters\n         */\n        clearFiltersHandle: function () {\n            $(this.directoryTreeSelector).jstree('deselect_all');\n            this.activeNode(null);\n            this.directories().setInActive();\n        },\n\n        /**\n         * Set active node filter, or deselect if the same node clicked\n         *\n         * @param {String} nodePath\n         */\n        setActiveNodeFilter: function (nodePath) {\n            if (this.activeNode() === nodePath && !this.jsTreeReloaded) {\n                this.selectStorageRoot();\n            } else {\n                this.selectFolder(nodePath);\n            }\n        },\n\n        /**\n         * Remove folders selection -> select storage root\n         */\n        selectStorageRoot: function () {\n            var filters = {},\n                applied = this.filterChips().get('applied');\n\n            $(this.directoryTreeSelector).jstree('deselect_all');\n\n            filters = $.extend(true, filters, applied);\n            delete filters.path;\n            this.filterChips().set('applied', filters);\n            this.activeNode(null);\n            this.waitForCondition(\n                function () {\n                    return _.isUndefined(this.directories());\n                }.bind(this),\n                function () {\n                    this.directories().setInActive();\n                }.bind(this)\n            );\n        },\n\n        /**\n         * Set selected folder\n         *\n         * @param {String} path\n         */\n        selectFolder: function (path) {\n            this.activeNode(path);\n\n            this.waitForCondition(\n                function () {\n                    return _.isUndefined(this.directories());\n                }.bind(this),\n                function () {\n                    this.directories().setActive(path);\n                }.bind(this)\n            );\n\n            this.applyFilter(path);\n        },\n\n        /**\n         * Remove active node from directory tree, and select next\n         */\n        removeNode: function () {\n            $(this.directoryTreeSelector).jstree('delete_node',\n                $(this.directoryTreeSelector).jstree('get_selected')\n            );\n        },\n\n        /**\n         * Apply folder filter by path\n         *\n         * @param {String} path\n         */\n        applyFilter: function (path) {\n            var filters = {},\n                applied = this.filterChips().get('applied');\n\n            filters = $.extend(true, filters, applied);\n            filters.path = path;\n            this.filterChips().set('applied', filters);\n        },\n\n        /**\n         * Reload jstree and update jstree events\n         */\n        reloadJsTree: function () {\n            var deferred = $.Deferred();\n\n            this.getJsonTree().then(function (data) {\n                $(this.directoryTreeSelector).jstree(true).settings.core.data = data;\n                $(this.directoryTreeSelector).jstree(true).refresh(false, true);\n                this.setJsTreeReloaded(true);\n                deferred.resolve();\n            }.bind(this));\n\n            return deferred.promise();\n        },\n\n        /**\n         * Get json data for jstree\n         */\n        getJsonTree: function () {\n            var deferred = $.Deferred();\n\n            $.ajax({\n                url: this.getDirectoryTreeUrl,\n                type: 'GET',\n                dataType: 'json',\n\n                /**\n                 * Success handler for request\n                 *\n                 * @param {Object} data\n                 */\n                success: function (data) {\n                    deferred.resolve(data);\n                },\n\n                /**\n                 * Error handler for request\n                 *\n                 * @param {Object} jqXHR\n                 * @param {String} textStatus\n                 */\n                error: function (jqXHR, textStatus) {\n                    deferred.reject();\n                    throw textStatus;\n                }\n            });\n\n            return deferred.promise();\n        },\n\n        /**\n         * Initialize directory tree\n         *\n         * @param {Array} data\n         */\n        createTree: function (data) {\n            // jscs:disable requireCamelCaseOrUpperCaseIdentifiers\n            $(this.directoryTreeSelector).jstree({\n                plugins: [],\n                checkbox: {\n                    three_state: false,\n                    cascade: ''\n                },\n                core: {\n                    data: data,\n                    check_callback: true,\n                    themes: {\n                        dots: false\n                    }\n                }\n            });\n            // jscs:enable requireCamelCaseOrUpperCaseIdentifiers\n        }\n    });\n});\n","Magento_MediaGalleryUi/js/directory/actions/deleteDirectory.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'jquery',\n    'mage/translate'\n], function ($, $t) {\n    'use strict';\n\n    return function (deleteFolderUrl, path) {\n        var deferred = $.Deferred(),\n            message;\n\n        $.ajax({\n            type: 'POST',\n            url: deleteFolderUrl,\n            dataType: 'json',\n            showLoader: true,\n            data: {\n                path: path\n            },\n            context: this,\n\n            /**\n             * Resolve  if delete folder success, reject with response message othervise\n             *\n             * @param {Object} response\n             */\n            success: function (response) {\n                if (response.success) {\n                    deferred.resolve(response.message);\n\n                    return;\n                }\n\n                deferred.reject(response.message);\n            },\n\n            /**\n             * Extract the message and reject\n             *\n             * @param {Object} response\n             */\n            error: function (response) {\n\n                if (typeof response.responseJSON === 'undefined' ||\n                    typeof response.responseJSON.message === 'undefined'\n                ) {\n                    message = $t('Could not delete the directory.');\n                } else {\n                    message = response.responseJSON.message;\n                }\n                deferred.reject(message);\n            }\n        });\n\n        return deferred.promise();\n    };\n});\n","Magento_MediaGalleryUi/js/directory/actions/createDirectory.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'jquery',\n    'mage/translate'\n], function ($, $t) {\n    'use strict';\n\n    return function (createFolderUrl, paths) {\n        var deferred = $.Deferred(),\n            message,\n            data = {\n                paths: paths\n            };\n\n        $.ajax({\n            type: 'POST',\n            url: createFolderUrl,\n            dataType: 'json',\n            showLoader: true,\n            data: data,\n            context: this,\n\n            /**\n             * Resolve  if success, reject with response message othervise\n             *\n             * @param {Object} response\n             */\n            success: function (response) {\n                if (response.success) {\n                    deferred.resolve(response.message);\n\n                    return;\n                }\n\n                deferred.reject(response.message);\n            },\n\n            /**\n             * Extract the message and reject\n             *\n             * @param {Object} response\n             */\n            error: function (response) {\n\n                if (typeof response.responseJSON === 'undefined' ||\n                    typeof response.responseJSON.message === 'undefined'\n                ) {\n                    message = $t('Could not create the directory.');\n                } else {\n                    message = response.responseJSON.message;\n                }\n                deferred.reject(message);\n            }\n        });\n\n        return deferred.promise();\n    };\n});\n","Magento_MediaGalleryUi/js/image/image-details.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'underscore',\n    'uiComponent',\n    'Magento_MediaGalleryUi/js/action/getDetails'\n], function ($, _, Component, getDetails) {\n    'use strict';\n\n    return Component.extend({\n        defaults: {\n            template: 'Magento_MediaGalleryUi/image/image-details',\n            modalSelector: '',\n            modalWindowSelector: '',\n            imageDetailsUrl: '/media_gallery/image/details',\n            images: [],\n            tagListLimit: 7,\n            showAllTags: false,\n            image: null,\n            modules: {\n                mediaGridMessages: '${ $.mediaGridMessages }'\n            }\n        },\n\n        /**\n         * Init observable variables\n         *\n         * @return {Object}\n         */\n        initObservable: function () {\n            this._super()\n                .observe([\n                    'image',\n                    'showAllTags'\n                ]);\n\n            return this;\n        },\n\n        /**\n         * Show image details by ID\n         *\n         * @param {String} imageId\n         */\n        showImageDetailsById: function (imageId) {\n            if (_.isUndefined(this.images[imageId])) {\n                getDetails(this.imageDetailsUrl, [imageId]).then(function (imageDetails) {\n                    this.images[imageId] = imageDetails[imageId];\n                    this.image(this.images[imageId]);\n                    this.openImageDetailsModal();\n                }.bind(this)).fail(function (error) {\n                    this.addMediaGridMessage('error', error);\n                }.bind(this));\n\n                return;\n            }\n\n            if (this.image() && this.image().id === imageId) {\n                this.openImageDetailsModal();\n\n                return;\n            }\n\n            this.image(this.images[imageId]);\n            this.openImageDetailsModal();\n        },\n\n        /**\n         * Open image details popup\n         */\n        openImageDetailsModal: function () {\n            var modalElement = $(this.modalSelector);\n\n            if (!modalElement.length || _.isUndefined(modalElement.modal)) {\n                return;\n            }\n\n            this.showAllTags(false);\n            modalElement.modal('openModal');\n        },\n\n        /**\n         * Close image details popup\n         */\n        closeImageDetailsModal: function () {\n            var modalElement = $(this.modalSelector);\n\n            if (!modalElement.length || _.isUndefined(modalElement.modal)) {\n                return;\n            }\n\n            modalElement.modal('closeModal');\n        },\n\n        /**\n         * Add media grid message\n         *\n         * @param {String} code\n         * @param {String} message\n         */\n        addMediaGridMessage: function (code, message) {\n            this.mediaGridMessages().add(code, message);\n            this.mediaGridMessages().scheduleCleanup();\n        },\n\n        /**\n         * Get tag text\n         *\n         * @param {String} tagText\n         * @param {Number} tagIndex\n         * @return {String}\n         */\n        getTagText: function (tagText, tagIndex) {\n            return tagText + (this.image().tags.length - 1 === tagIndex ? '' : ',');\n        },\n\n        /**\n         * Show all image tags\n         */\n        showMoreImageTags: function () {\n            this.showAllTags(true);\n        },\n\n        /**\n         * Is value an object\n         *\n         * @param {*} value\n         * @returns {Boolean}\n         */\n        isArray: function (value) {\n            return _.isArray(value);\n        },\n\n        /**\n         * Is value not empty\n         *\n         * @param {*} value\n         * @returns {Boolean}\n         */\n        notEmpty: function (value) {\n            return value.length > 0;\n        },\n\n        /**\n         * Get name and number text for used in link\n         *\n         * @param {Object} item\n         * @returns {String}\n         */\n        getUsedInText: function (item) {\n            return item.name +  '(' + item.number + ')';\n        },\n\n        /**\n         * Get filter url\n         *\n         * @param {String} link\n         */\n        getFilterUrl: function (link) {\n            return link + '?filters[asset_id]=[' + this.image().id + ']';\n        },\n\n        /**\n         * Check if details modal is active\n         * @return {Boolean}\n         */\n        isActive: function () {\n            return $(this.modalWindowSelector).hasClass('_show');\n        },\n\n        /**\n         * Remove image details\n         *\n         * @param {String} id\n         */\n        removeCached: function (id) {\n            delete this.images[id];\n        }\n    });\n});\n","Magento_MediaGalleryUi/js/image/image-edit.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'underscore',\n    'uiComponent',\n    'uiLayout',\n    'Magento_Ui/js/lib/key-codes',\n    'Magento_MediaGalleryUi/js/action/getDetails',\n    'mage/validation'\n], function ($, _, Component, layout, keyCodes, getDetails) {\n    'use strict';\n\n    return Component.extend({\n        defaults: {\n            template: 'Magento_MediaGalleryUi/image/image-edit',\n            modalSelector: '.media-gallery-edit-image-details-modal',\n            imageEditDetailsUrl: '/media_gallery/image/details',\n            saveDetailsUrl: '/media_gallery/image/saveDetails',\n            images: [],\n            image: null,\n            keywordOptions: [],\n            selectedKeywords: [],\n            newKeyword: '',\n            newKeywordSelector: '#keyword',\n            modules: {\n                mediaGridMessages: '${ $.mediaGridMessages }',\n                keywordsSelect: '${ $.name }_keywords'\n            },\n            viewConfig: [\n                {\n                    component: 'Magento_Ui/js/form/element/ui-select',\n                    name: '${ $.name }_keywords',\n                    template: 'ui/grid/filters/elements/ui-select',\n                    disableLabel: true\n                }\n            ],\n            exports: {\n                keywordOptions: '${ $.name }_keywords:options'\n            },\n            links: {\n                selectedKeywords: '${ $.name }_keywords:value'\n            }\n        },\n\n        /**\n         * Initialize the component\n         *\n         * @returns {Object}\n         */\n        initialize: function () {\n            this._super().initView();\n\n            return this;\n        },\n\n        /**\n         * Add a new keyword to select\n         */\n        addKeyword: function () {\n            var options = this.keywordOptions(),\n                selected = this.selectedKeywords(),\n                newKeywordField = $(this.newKeywordSelector);\n\n            newKeywordField.validation();\n\n            if (!newKeywordField.validation('isValid') || this.newKeyword() === '') {\n                return;\n            }\n\n            options.push(this.getOptionForKeyword(this.newKeyword()));\n            selected.push(this.newKeyword());\n            this.newKeyword('');\n\n            this.keywordOptions(options);\n            this.selectedKeywords(selected);\n        },\n\n        /**\n         * Create an option object based on keyword string\n         *\n         * @param {String} keyword\n         * @returns {Object}\n         */\n        getOptionForKeyword: function (keyword) {\n            return {\n                'is_active': 1,\n                level: 1,\n                value: keyword,\n                label: keyword\n            };\n        },\n\n        /**\n         * Convert array of keywords to options format\n         *\n         * @param {Array} tags\n         */\n        setKeywordOptions: function (tags) {\n            var options = [];\n\n            tags.forEach(function (tag) {\n                options.push(this.getOptionForKeyword(tag));\n            }.bind(this));\n\n            this.keywordOptions(options);\n            this.selectedKeywords(tags);\n        },\n\n        /**\n         * Initialize child components\n         *\n         * @returns {Object}\n         */\n        initView: function () {\n            layout(this.viewConfig);\n\n            return this;\n        },\n\n        /**\n         * Init observable variables\n         *\n         * @return {Object}\n         */\n        initObservable: function () {\n            this._super()\n                .observe([\n                    'image',\n                    'keywordOptions',\n                    'selectedKeywords',\n                    'newKeyword'\n                ]);\n\n            return this;\n        },\n\n        /**\n         * Get image details by ID\n         *\n         * @param {String} imageId\n         */\n        showEditDetailsPanel: function (imageId) {\n            if (_.isUndefined(this.images[imageId])) {\n                getDetails(this.imageEditDetailsUrl, [imageId]).then(function (imageDetails) {\n                    this.images[imageId] = imageDetails[imageId];\n                    this.image(this.images[imageId]);\n                    this.openEditImageDetailsModal();\n                }.bind(this)).fail(function (error) {\n                    this.addMediaGridMessage('error', error);\n                }.bind(this));\n\n                return;\n            }\n\n            if (this.image() && this.image().id === imageId) {\n                this.openEditImageDetailsModal();\n\n                return;\n            }\n\n            this.image(this.images[imageId]);\n            this.openEditImageDetailsModal();\n        },\n\n        /**\n         * Open edit image details popup\n         */\n        openEditImageDetailsModal: function () {\n            var modalElement = $(this.modalSelector);\n\n            if (!modalElement.length || _.isUndefined(modalElement.modal)) {\n                return;\n            }\n\n            this.setKeywordOptions(this.image().tags);\n            this.newKeyword('');\n\n            modalElement.modal('openModal');\n        },\n\n        /**\n         * Close image details popup\n         */\n        closeImageDetailsModal: function () {\n            var modalElement = $(this.modalSelector);\n\n            if (!modalElement.length || _.isUndefined(modalElement.modal)) {\n                return;\n            }\n\n            modalElement.modal('closeModal');\n        },\n\n        /**\n         * Add media grid message\n         *\n         * @param {String} code\n         * @param {String} message\n         */\n        addMediaGridMessage: function (code, message) {\n            this.mediaGridMessages().add(code, message);\n            this.mediaGridMessages().scheduleCleanup();\n        },\n\n        /**\n         * Handle Enter key event to save image details\n         *\n         * @param {Object} data\n         * @param {jQuery.Event} event\n         * @returns {Boolean}\n         */\n        handleEnterKey: function (data, event) {\n            var modalElement = $(this.modalSelector),\n                key = keyCodes[event.keyCode];\n\n            if (key === 'enterKey') {\n                event.preventDefault();\n                modalElement.find('.page-action-buttons button.save').trigger('click');\n            }\n\n            return true;\n        },\n\n        /**\n         * Remove cached image details in edit form\n         *\n         * @param {String} id\n         */\n        removeCached: function (id) {\n            delete this.images[id];\n        }\n    });\n});\n","Magento_MediaGalleryUi/js/image/image-actions.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'underscore',\n    'uiElement',\n    'Magento_MediaGalleryUi/js/action/deleteImageWithDetailConfirmation',\n    'Magento_MediaGalleryUi/js/grid/columns/image/insertImageAction',\n    'Magento_MediaGalleryUi/js/action/saveDetails',\n    'mage/validation'\n], function ($, _, Element, deleteImageWithDetailConfirmation, addSelected, saveDetails) {\n    'use strict';\n\n    return Element.extend({\n        defaults: {\n            modalSelector: '',\n            modalWindowSelector: '',\n            mediaGalleryImageDetailsName: 'mediaGalleryImageDetails',\n            mediaGalleryEditDetailsName: 'mediaGalleryEditDetails',\n            template: 'Magento_MediaGalleryUi/image/actions',\n            modules: {\n                imageModel: '${ $.imageModelName }',\n                mediaGalleryImageDetails: '${ $.mediaGalleryImageDetailsName }',\n                mediaGalleryEditDetails: '${ $.mediaGalleryEditDetailsName }'\n            }\n        },\n\n        /**\n         * Initialize the component\n         *\n         * @returns {Object}\n         */\n        initialize: function () {\n            this._super();\n            $(window).on('fileDeleted.enhancedMediaGallery', this.closeViewDetailsModal.bind(this));\n\n            return this;\n        },\n\n        /**\n         * Close the images details modal\n         */\n        closeModal: function () {\n            var modalElement = $(this.modalSelector),\n                modalWindow = $(this.modalWindowSelector);\n\n            if (!modalWindow.hasClass('_show') || !modalElement.length || _.isUndefined(modalElement.modal)) {\n                return;\n            }\n\n            this.mediaGalleryEditDetails().keywordsSelect().cacheOptions.plain = [];\n            modalElement.modal('closeModal');\n        },\n\n        /**\n         * Opens the image edit panel\n         */\n        editImageAction: function () {\n            var record = this.imageModel().getSelected().id;\n\n            this.mediaGalleryEditDetails().showEditDetailsPanel(record);\n        },\n\n        /**\n         * Delete image action\n         */\n        deleteImageAction: function () {\n            var imageDetailsUrl = this.mediaGalleryImageDetails().imageDetailsUrl,\n                deleteImageUrl = this.imageModel().deleteImageUrl;\n\n            deleteImageWithDetailConfirmation.deleteImageAction(\n                [this.imageModel().getSelected().id],\n                imageDetailsUrl,\n                deleteImageUrl\n            );\n        },\n\n        /**\n         * Save image details action\n         */\n        saveImageDetailsAction: function () {\n            var saveDetailsUrl = this.mediaGalleryEditDetails().saveDetailsUrl,\n                modalElement = $(this.modalSelector),\n                form = modalElement.find('#image-edit-details-form'),\n                imageId = this.imageModel().getSelected().id,\n                keywords = this.mediaGalleryEditDetails().selectedKeywords(),\n                imageDetails = this.mediaGalleryImageDetails(),\n                imageEditDetails = this.mediaGalleryEditDetails();\n\n            if (form.validation('isValid')) {\n                saveDetails(\n                    saveDetailsUrl,\n                    [form.serialize(), $.param({\n                        'keywords': keywords\n                    })].join('&')\n                ).then(function () {\n                    this.closeModal();\n                    this.imageModel().reloadGrid();\n                    imageDetails.removeCached(imageId);\n                    imageEditDetails.removeCached(imageId);\n\n                    if (imageDetails.isActive()) {\n                        imageDetails.showImageDetailsById(imageId);\n                    }\n                }.bind(this));\n            }\n        },\n\n        /**\n         * Add Image\n         */\n        addImage: function () {\n            addSelected.insertImage(\n                this.imageModel().getSelected(),\n                {\n                    onInsertUrl: this.imageModel().onInsertUrl,\n                    storeId: this.imageModel().storeId\n                }\n            );\n            this.closeModal();\n        },\n\n        /**\n         * Close view details modal after confirm deleting image\n         */\n        closeViewDetailsModal: function () {\n            this.closeModal();\n        }\n    });\n});\n","Magento_MediaGalleryUi/js/action/getDetails.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'jquery',\n    'mage/translate'\n], function ($, $t) {\n    'use strict';\n\n    return function (imageDetailsUrl, imageIds) {\n        var deferred = $.Deferred(),\n            message;\n\n        $.ajax({\n            type: 'GET',\n            url: imageDetailsUrl,\n            dataType: 'json',\n            showLoader: true,\n            data: {\n                'ids': imageIds\n            },\n            context: this,\n\n            /**\n             * Resolve with image details if success, reject with response message othervise\n             *\n             * @param {Object} response\n             */\n            success: function (response) {\n                if (response.success) {\n                    deferred.resolve(response.imageDetails);\n\n                    return;\n                }\n\n                deferred.reject(response.message);\n            },\n\n            /**\n             * Extract the message and reject\n             *\n             * @param {Object} response\n             */\n            error: function (response) {\n\n                if (typeof response.responseJSON === 'undefined' ||\n                    typeof response.responseJSON.message === 'undefined'\n                ) {\n                    message = $t('Could not retrieve image details.');\n                } else {\n                    message = response.responseJSON.message;\n                }\n                deferred.reject(message);\n            }\n        });\n\n        return deferred.promise();\n    };\n});\n","Magento_MediaGalleryUi/js/action/deleteImages.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'jquery',\n    'underscore',\n    'mage/url',\n    'Magento_MediaGalleryUi/js/grid/messages',\n    'Magento_Ui/js/modal/confirm',\n    'mage/translate'\n], function ($, _, urlBuilder, messages, confirmation, $t) {\n    'use strict';\n\n    return function (ids, deleteUrl, confirmationContent) {\n        var deferred = $.Deferred(),\n               title = $t('Delete assets'),\n               cancelText = $t('Cancel'),\n               deleteImageText = $t('Delete');\n\n        /**\n         * Send deletion request with redords ids\n         *\n         * @param {Array} recordIds\n         * @param {String} serviceUrl\n         */\n        function sendRequest(recordIds, serviceUrl) {\n\n            $.ajax({\n                type: 'POST',\n                url: serviceUrl,\n                dataType: 'json',\n                showLoader: true,\n                data: {\n                    'form_key': window.FORM_KEY,\n                    'ids': recordIds\n                },\n                context: this,\n\n                /**\n                 * Success handler for deleting image\n                 *\n                 * @param {Object} response\n                 */\n                success: function (response) {\n                    var message = !_.isUndefined(response.message) ? response.message : null;\n\n                    if (!response.success) {\n                        message = message || $t('There was an error on attempt to delete the images.');\n                        $(window).trigger('fileDeleted.enhancedMediaGallery', {\n                            reload: false,\n                            message: message,\n                            code: 'error'\n                        });\n\n                        deferred.reject(message);\n                    }\n\n                    message = message || $t('You have successfully removed the images.');\n                    $(window).trigger('fileDeleted.enhancedMediaGallery', {\n                        reload: true,\n                        message: message,\n                        code: 'success'\n                    });\n                    deferred.resolve(message);\n                },\n\n                /**\n                 * Error handler for deleting image\n                 *\n                 * @param {Object} response\n                 */\n                error: function (response) {\n                    var message;\n\n                    if (typeof response.responseJSON === 'undefined' ||\n                        typeof response.responseJSON.message === 'undefined'\n                    ) {\n                        message = $t('There was an error on attempt to delete the image.');\n                    } else {\n                        message = response.responseJSON.message;\n                    }\n\n                    $(window).trigger('fileDeleted.enhancedMediaGallery', {\n                        reload: false,\n                        message: message,\n                        code: 'error'\n                    });\n                    deferred.reject(message);\n                }\n            });\n        }\n\n        confirmation({\n            title: title,\n            modalClass: 'media-gallery-delete-image-action',\n            content: confirmationContent,\n            buttons: [\n                {\n                    text: cancelText,\n                    class: 'action-secondary action-dismiss',\n\n                    /**\n                     * Close modal\n                     */\n                    click: function () {\n                        this.closeModal();\n                        deferred.resolve({\n                            status: 'canceled'\n                        });\n                    }\n                },\n                {\n                    text: deleteImageText,\n                    class: 'action-primary action-accept',\n\n                    /**\n                     * Delete Image and close modal\n                     */\n                    click: function () {\n                        sendRequest(ids, deleteUrl);\n                        this.closeModal();\n                    }\n                }\n            ]\n        });\n\n        return deferred.promise();\n    };\n});\n","Magento_MediaGalleryUi/js/action/saveDetails.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'jquery',\n    'mage/translate'\n], function ($, $t) {\n    'use strict';\n\n    return function (saveImageDetailsUrl, data) {\n        var deferred = $.Deferred(),\n            message;\n\n        $.ajax({\n            type: 'POST',\n            url: saveImageDetailsUrl,\n            dataType: 'json',\n            showLoader: true,\n            data: data,\n\n            /**\n             * Resolve with image details if success, reject with response message otherwise\n             *\n             * @param {Object} response\n             */\n            success: function (response) {\n                if (response.success) {\n                    deferred.resolve(response.message);\n\n                    return;\n                }\n\n                deferred.reject(response.message);\n            },\n\n            /**\n             * Extract the message and reject\n             *\n             * @param {Object} response\n             */\n            error: function (response) {\n                if (typeof response.responseJSON === 'undefined' ||\n                    typeof response.responseJSON.message === 'undefined'\n                ) {\n                    message = $t('Could not save image details.');\n                } else {\n                    message = response.responseJSON.message;\n                }\n                deferred.reject(message);\n            }\n        });\n\n        return deferred.promise();\n    };\n});\n","Magento_MediaGalleryUi/js/action/deleteImageWithDetailConfirmation.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'jquery',\n    'underscore',\n    'Magento_MediaGalleryUi/js/action/getDetails',\n    'Magento_MediaGalleryUi/js/action/deleteImages',\n    'mage/translate'\n], function ($, _, getDetails, deleteImages, $t) {\n    'use strict';\n\n    return {\n\n        /**\n         * Get information about image use\n         *\n         * @param {Array} recordsIds\n         * @param {String} imageDetailsUrl\n         * @param {String} deleteImageUrl\n         */\n        deleteImageAction: function (recordsIds, imageDetailsUrl, deleteImageUrl) {\n            var confirmationContent = $t('%1Are you sure you want to delete \"%2\" image(s)?')\n                .replace('%2', Object.keys(recordsIds).length),\n                deferred = $.Deferred();\n\n            getDetails(imageDetailsUrl, recordsIds).then(function (images) {\n                        confirmationContent = confirmationContent.replace(\n                            '%1',\n                            this.getRecordRelatedContentMessage(images) + ' '\n                        );\n                    }.bind(this)).fail(function () {\n                        confirmationContent = confirmationContent.replace('%1', '');\n                    }).always(function () {\n                        deleteImages(recordsIds, deleteImageUrl, confirmationContent).then(function (status) {\n                            deferred.resolve(status);\n                        }).fail(function (error) {\n                            deferred.reject(error);\n                        });\n                    });\n\n            return deferred.promise();\n        },\n\n        /**\n         * Get information about image use\n         *\n         * @param {Object|String} images\n         * @return {String}\n         */\n        getRecordRelatedContentMessage: function (images) {\n            var usedInMessage = $t('The selected assets are used in the content of the following entities: '),\n                usedIn = {};\n\n            $.each(images, function (key, image) {\n                $.each(image.details, function (sectionIndex, section) {\n                    if (section.title === 'Used In' && _.isObject(section) && !_.isEmpty(section.value)) {\n                        $.each(section.value, function (entityTypeIndex, entityTypeData) {\n                            usedIn[entityTypeData.name] = entityTypeData.name in usedIn ?\n                                usedIn[entityTypeData.name] + entityTypeData.number :\n                                entityTypeData.number;\n                        });\n                    }\n                });\n            });\n\n            if (_.isEmpty(usedIn)) {\n                return '';\n            }\n\n            return usedInMessage + this.usedInObjectToString(usedIn);\n        },\n\n        /**\n         * Fromats usedIn object to string\n         *\n         * @param {Object} usedIn\n         * @return {String}\n         */\n        usedInObjectToString: function (usedIn) {\n            var entities = [];\n\n            $.each(usedIn, function (entityName, number) {\n                entities.push(entityName + '(' + number + ')');\n            });\n\n            return entities.join(', ') + '.';\n        }\n    };\n});\n","Magento_MediaGalleryUi/js/grid/sortBy.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'Magento_Ui/js/grid/sortBy'\n], function (Element) {\n    'use strict';\n\n    return Element.extend({\n        defaults: {\n            columnIndexMap: {}\n        },\n\n        /**\n         * Prepared sort order options\n         */\n        preparedOptions: function (columns) {\n            var index = 0,\n                sortBy;\n\n            if (columns && columns.length > 0) {\n                columns.map(function (column) {\n                    if (column.sortable === true) {\n                        sortBy = column['sort_by'] || {};\n\n                        if (sortBy.excluded) {\n                            return;\n                        }\n\n                        this.options.push({\n                            value: column.index,\n                            label: column.label,\n                            sortByField: sortBy.field,\n                            sortDirection: sortBy.direction\n                        });\n\n                        this.columnIndexMap[column.index] = index++;\n\n                        this.isVisible(true);\n                    } else {\n                        this.isVisible(false);\n                    }\n                }.bind(this));\n            }\n        },\n\n        /**\n         * Apply changes\n         */\n        applyChanges: function () {\n            var column = this.getColumn(this.selectedOption());\n\n            this.applied({\n                field: column.sortByField || this.selectedOption(),\n                direction: column.sortDirection || this.sorting\n            });\n        },\n\n        /**\n         * Get column by index\n         *\n         * @param {String} optionIndex\n         * @returns {Object}\n         */\n        getColumn: function (optionIndex) {\n            return this.options[this.columnIndexMap[optionIndex]];\n        },\n\n        /**\n         * Select default option\n         */\n        selectDefaultOption: function () {\n            this.selectedOption(this.options[0].value);\n        }\n    });\n});\n","Magento_MediaGalleryUi/js/grid/messages.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'uiElement',\n    'escaper'\n], function (Element, escaper) {\n    'use strict';\n\n    return Element.extend({\n        defaults: {\n            template: 'Magento_MediaGalleryUi/grid/messages',\n            messageDelay: 5,\n            messages: [],\n            allowedTags: ['div', 'span', 'b', 'strong', 'i', 'em', 'u', 'a']\n        },\n\n        /**\n         * Init observable variables\n         * @return {Object}\n         */\n        initObservable: function () {\n            this._super()\n                .observe([\n                    'messages'\n                ]);\n\n            return this;\n        },\n\n        /**\n         * Get messages\n         *\n         * @returns {Array}\n         */\n        get: function () {\n            return this.messages();\n        },\n\n        /**\n         * Add message\n         *\n         * @param {String} type\n         * @param {String} message\n         */\n        add: function (type, message) {\n            this.messages.push({\n                code: type,\n                message: message\n            });\n        },\n\n        /**\n         * Clear messages\n         */\n        clear: function () {\n            this.messages.removeAll();\n        },\n\n        /**\n         * Schedule message cleanup\n         *\n         * @param {Number} delay\n         */\n        scheduleCleanup: function (delay) {\n            // eslint-disable-next-line no-unused-vars\n            var timerId;\n\n            delay = delay || this.messageDelay;\n\n            // eslint-disable-next-line no-unused-vars\n            timerId = setTimeout(function () {\n                clearTimeout(timerId);\n                this.clear();\n            }.bind(this), Number(delay) * 1000);\n        },\n\n        /**\n         * Prepare the given message to be rendered as HTML\n         *\n         * @param {String} message\n         * @return {String}\n         */\n        prepareMessageUnsanitizedHtml: function (message) {\n            return escaper.escapeHtml(message, this.allowedTags);\n        }\n    });\n});\n","Magento_MediaGalleryUi/js/grid/masonry.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'Magento_Ui/js/grid/masonry',\n    'jquery'\n], function (Masonry, $) {\n    'use strict';\n\n    return Masonry.extend({\n        defaults: {\n            modules: {\n                provider: '${ $.provider }'\n            }\n        },\n\n        /**\n         * Init component\n         *\n         * @return {Object}\n         */\n        initialize: function () {\n            this._super();\n            this.initEvents();\n\n            return this;\n        },\n\n        /**\n         * Initialize events\n         */\n        initEvents: function () {\n            $(window).on('folderDeleted.enhancedMediaGallery', this.reloadGrid.bind(this));\n        },\n\n        /**\n         * Reload grid\n         */\n        reloadGrid: function () {\n            var provider = this.provider(),\n                dataStorage = provider.storage();\n\n            dataStorage.clearRequests();\n            provider.reload();\n        }\n    });\n});\n","Magento_MediaGalleryUi/js/grid/columns/image.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'jquery',\n    'Magento_Ui/js/grid/columns/column',\n    'uiLayout',\n    'underscore'\n], function ($, Column, layout, _) {\n    'use strict';\n\n    return Column.extend({\n        defaults: {\n            bodyTmpl: 'Magento_MediaGalleryUi/grid/columns/image',\n            messageContentSelector: 'ul.messages',\n            mediaGalleryContainerSelector: '.media-gallery-container',\n            deleteImageUrl: 'media_gallery/image/delete',\n            addSelectedBtnSelector: '#add_selected',\n            deleteSelectedBtnSelector: '#delete_selected',\n            gridSelector: '[data-id=\"media-gallery-masonry-grid\"]',\n            selected: null,\n            allowedActions: [],\n            fields: {\n                id: 'id',\n                url: 'url',\n                alt: 'name'\n            },\n            modules: {\n                actions: '${ $.name }_actions',\n                provider: '${ $.provider }',\n                messages: '${ $.messagesName }',\n                massaction: '${ $.massactionComponentName }'\n            },\n            imports: {\n                activeDirectory: '${ $.mediaGalleryDirectoryComponent }:activeNode'\n            },\n            listens: {\n                activeDirectory: 'selectDirectoryHandle',\n                '${ $.massactionComponentName }:massActionMode': 'updateSelected'\n            },\n            viewConfig: [\n                {\n                    component: 'Magento_MediaGalleryUi/js/grid/columns/image/actions',\n                    name: '${ $.name }_actions',\n                    imageModelName: '${ $.name }',\n                    allowedActions: '${ $.allowedActions }'\n                }\n            ]\n        },\n\n        /**\n         * Initialize the component\n         *\n         * @returns {Object}\n         */\n        initialize: function () {\n            this._super();\n            this.initView();\n            $(window).on('fileDeleted.enhancedMediaGallery', this.reloadMediaGrid.bind(this));\n            $(window).on('reload.MediaGallery', this.reloadGrid.bind(this));\n\n            return this;\n        },\n\n        /**\n         * Init observable variables\n         * @return {Object}\n         */\n        initObservable: function () {\n            this._super()\n                .observe([\n                    'selected'\n                ]);\n\n            return this;\n        },\n\n        /**\n         * Is massaction mode active.\n         */\n        isMassActionMode: function () {\n            return this.massaction().massActionMode();\n        },\n\n        /**\n         * Returns url to given record.\n         *\n         * @param {Object} record - Data to be preprocessed.\n         * @returns {String}\n         */\n        getUrl: function (record) {\n            return record[this.fields.url];\n        },\n\n        /**\n         * Returns id to given record.\n         *\n         * @param {Object} record - Data to be preprocessed.\n         * @returns {Number}\n         */\n        getId: function (record) {\n            return record[this.fields.id];\n        },\n\n        /**\n         * Update selected items per massaction mode.\n         */\n        updateSelected: function () {\n            this.selected({});\n            this.hideAddSelectedAndDeleteButon();\n        },\n\n        /**\n         * Returns name to given record.\n         *\n         * @param {Object} record - Data to be preprocessed.\n         * @returns {String}\n         */\n        getImageAlt: function (record) {\n            return record[this.fields.alt];\n        },\n\n        /**\n         * Check if the record is currently selected\n         *\n         * @param {Object} record - Data to be preprocessed.\n         * @returns {Boolean}\n         */\n        isSelected: function (record) {\n            if (_.isNull(this.selected())) {\n                return false;\n            }\n\n            if (this.massaction().massActionMode()) {\n                return this.selected()[record.id];\n            }\n\n            return this.getId(this.selected()) === this.getId(record);\n        },\n\n        /**\n         * Click on image\n         *\n         * @param {Object} record\n         * @param {Boolean} collapsibleOpened\n         */\n        clickOnImage: function (record, collapsibleOpened) {\n            if (!collapsibleOpened) {\n                this.select(record);\n            }\n        },\n\n        /**\n         * Click on three-dots\n         *\n         * @param {Object} record\n         * @param {Boolean} collapsibleOpened\n         */\n        clickOnThreeDots: function (record, collapsibleOpened) {\n            if (!this.isSelected(record) || collapsibleOpened) {\n                this.select(record);\n            }\n        },\n\n        /**\n         * Handle checkbox click.\n         */\n        checkboxClick: function (record) {\n            var items = this.selected();\n\n            if (this.selected()[record.id])  {\n                delete items[record.id];\n                this.selected(items);\n            } else {\n                items[record.id] = record.id;\n                this.selected(items);\n            }\n\n            return true;\n        },\n\n        /**\n         * Set the record as selected\n         */\n        select: function (record) {\n            if (this.massaction().massActionMode()) {\n                return this.checkboxClick(record);\n            }\n\n            this.isSelected(record) ? this.selected(null) : this.selected(record);\n            this.toggleAddSelectedButton();\n\n            return true;\n        },\n\n        /**\n         * Deselect the record\n         */\n        deselectImage: function () {\n            this.selected(null);\n            this.toggleAddSelectedButton();\n        },\n\n        /**\n         * Get the selected record\n         * @returns {Object}\n         */\n        getSelected: function () {\n            return this.selected();\n        },\n\n        /**\n         * Initialize child components\n         *\n         * @returns {Object}\n         */\n        initView: function () {\n            layout(this.viewConfig);\n\n            return this;\n        },\n\n        /**\n         * Toggle add selected button\n         */\n        toggleAddSelectedButton: function () {\n            if (this.selected() === null) {\n                this.hideAddSelectedAndDeleteButon();\n\n                return;\n            }\n\n            if (this.allowedActions.includes('insert')) {\n                $(this.addSelectedBtnSelector).removeClass('no-display');\n            }\n\n            if (this.allowedActions.includes('delete')) {\n                $(this.deleteSelectedBtnSelector).removeClass('no-display');\n            }\n        },\n\n        /**\n         * Hide add selected and Delete button\n         */\n        hideAddSelectedAndDeleteButon: function () {\n            $(this.addSelectedBtnSelector).addClass('no-display');\n            $(this.deleteSelectedBtnSelector).addClass('no-display');\n        },\n\n        /**\n         * @param {jQuery.event} e\n         * @param {Object} data\n         */\n        reloadMediaGrid: function (e, data) {\n            if (data.reload) {\n                this.reloadGrid();\n            }\n\n            if (data.message && data.code) {\n                this.addMessage(data.code, data.message);\n            }\n            this.hideAddSelectedAndDeleteButon();\n        },\n\n        /**\n         * Reload grid\n         */\n        reloadGrid: function () {\n            var provider = this.provider(),\n                dataStorage = provider.storage();\n\n            dataStorage.clearRequests();\n            provider.reload();\n        },\n\n        /**\n         * Add message\n         *\n         * @param {String} code\n         * @param {String} message\n         */\n        addMessage: function (code, message) {\n            this.messages().add(code, message);\n            this.closeContextMenu();\n            this.scrollToMessageContent();\n            this.messages().scheduleCleanup();\n        },\n\n        /**\n         * Listener to select directory event\n         *\n         * @param {String} path\n         */\n        selectDirectoryHandle: function (path) {\n            if (this.selected() &&\n                this.selected().directory !== path  &&\n                !this.massaction().massActionMode()) {\n                this.deselectImage();\n            }\n        },\n\n        /**\n         * Action to close the context menu in media gallery.\n         */\n        closeContextMenu: function () {\n            $(this.gridSelector).trigger('click');\n        },\n\n        /**\n         * Scroll to the top of media gallery page\n         */\n        scrollToMessageContent: function () {\n            var scrollTargetElement = $(this.messageContentSelector),\n                scrollTargetContainer = $(this.mediaGalleryContainerSelector);\n\n            scrollTargetContainer.find(scrollTargetElement).get(0).scrollIntoView({\n                behavior: 'smooth',\n                block: 'center',\n                inline: 'nearest'\n            });\n        }\n    });\n});\n","Magento_MediaGalleryUi/js/grid/columns/image/actions.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'jquery',\n    'underscore',\n    'uiComponent',\n    'Magento_MediaGalleryUi/js/action/deleteImageWithDetailConfirmation',\n    'Magento_MediaGalleryUi/js/grid/columns/image/insertImageAction',\n    'mage/translate',\n    'Magento_Ui/js/lib/view/utils/async'\n], function ($, _, Component, deleteImageWithDetailConfirmation, image, $t) {\n    'use strict';\n\n    return Component.extend({\n        defaults: {\n            template: 'Magento_MediaGalleryUi/grid/columns/image/actions',\n            mediaGalleryImageDetailsName: 'mediaGalleryImageDetails',\n            mediaGalleryEditDetailsName: 'mediaGalleryEditDetails',\n            allowedActions: [],\n            actionsList: [\n                {\n                    name: 'image-details',\n                    title: $t('View Details'),\n                    classes: 'action-menu-item',\n                    handler: 'viewImageDetails'\n                },\n                {\n                    name: 'edit',\n                    title: $t('Edit'),\n                    classes: 'action-menu-item',\n                    handler: 'editImageDetails'\n                },\n                {\n                    name: 'delete',\n                    title: $t('Delete'),\n                    classes: 'action-menu-item media-gallery-delete-assets',\n                    handler: 'deleteImageAction'\n                }\n            ],\n            modules: {\n                imageModel: '${ $.imageModelName }',\n                mediaGalleryImageDetails: '${ $.mediaGalleryImageDetailsName }',\n                mediaGalleryEditDetails: '${ $.mediaGalleryEditDetailsName }'\n            }\n        },\n\n        /**\n         * Initialize the component\n         *\n         * @returns {Object}\n         */\n        initialize: function () {\n            this._super();\n            this.initEvents();\n\n            this.actionsList = this.actionsList.filter(function (item) {\n                return this.allowedActions.includes(item.name);\n            }.bind(this));\n\n            if (!this.allowedActions.includes('delete')) {\n                $.async('.media-gallery-delete-assets', function () {\n                    $('.media-gallery-delete-assets').off('click').addClass('action-disabled');\n                });\n            }\n\n            return this;\n        },\n\n        /**\n         * Initialize image action events\n         */\n        initEvents: function () {\n            $(this.imageModel().addSelectedBtnSelector).on('click', function () {\n                image.insertImage(\n                    this.imageModel().getSelected(),\n                    {\n                        onInsertUrl: this.imageModel().onInsertUrl,\n                        storeId: this.imageModel().storeId\n                    }\n                );\n            }.bind(this));\n            $(this.imageModel().deleteSelectedBtnSelector).on('click', function () {\n                this.deleteImageAction(this.imageModel().selected());\n            }.bind(this));\n\n        },\n\n        /**\n         * Delete image action\n         *\n         * @param {Object} record\n         */\n        deleteImageAction: function (record) {\n            var imageDetailsUrl = this.mediaGalleryImageDetails().imageDetailsUrl,\n                deleteImageUrl = this.imageModel().deleteImageUrl;\n\n            deleteImageWithDetailConfirmation.deleteImageAction([record.id], imageDetailsUrl, deleteImageUrl);\n        },\n\n        /**\n         * View image details\n         *\n         * @param {Object} record\n         */\n        viewImageDetails: function (record) {\n            var recordId = this.imageModel().getId(record);\n\n            this.mediaGalleryImageDetails().showImageDetailsById(recordId);\n        },\n\n        /**\n         * Edit image details\n         *\n         * @param {Object} record\n         */\n        editImageDetails: function (record) {\n            var recordId = this.imageModel().getId(record);\n\n            this.mediaGalleryEditDetails().showEditDetailsPanel(recordId);\n        }\n    });\n});\n","Magento_MediaGalleryUi/js/grid/columns/image/insertImageAction.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* global FORM_KEY, tinyMceEditors */\ndefine([\n    'jquery',\n    'wysiwygAdapter',\n    'underscore',\n    'mage/translate'\n], function ($, wysiwyg, _, $t) {\n    'use strict';\n\n    return {\n\n        /**\n         * Insert provided image in wysiwyg if enabled, or widget\n         *\n         * @param {Object} record\n         * @param {Object} config\n         * @returns {Boolean}\n         */\n        insertImage: function (record, config) {\n            var targetElement, forceStaticPath;\n\n            if (record === null) {\n                return false;\n            }\n            targetElement = this.getTargetElement(window.MediabrowserUtility.targetElementId);\n\n            if (!targetElement.length) {\n                window.MediabrowserUtility.closeDialog();\n                throw $t('Target element not found for content update');\n            }\n\n            forceStaticPath = typeof targetElement !== 'function' && targetElement.data('force_static_path') ? 1 : 0;\n\n            $.ajax({\n                url: config.onInsertUrl,\n                data: {\n                    filename: record['encoded_id'],\n                    'store_id': config.storeId,\n                    'as_is': typeof targetElement !== 'function' && targetElement.is('textarea') ? 1 : 0,\n                    'force_static_path': forceStaticPath,\n                    'form_key': FORM_KEY\n                },\n                context: this,\n                showLoader: true\n            }).done($.proxy(function (data) {\n                if (typeof targetElement === 'function') {\n                    targetElement(data.content, {text: record['title']});\n                } else if (targetElement.is('textarea')) {\n                    this.insertAtCursor(targetElement.get(0), data.content);\n                    targetElement.focus();\n                    $(targetElement).trigger('change');\n                } else {\n                    targetElement.val(data.content)\n                        .data('size', data.size)\n                        .data('mime-type', data.type)\n                        .trigger('change');\n                }\n            }, this));\n            window.MediabrowserUtility.closeDialog();\n\n            if (typeof targetElement !== 'function') {\n                targetElement.focus();\n                $(targetElement).trigger('change');\n            }\n        },\n\n        /**\n         * Insert image to target instance.\n         *\n         * @param {Object} element\n         * @param {*} value\n         */\n        insertAtCursor: function (element, value) {\n            var sel, startPos, endPos, scrollTop;\n\n            if ('selection' in document) {\n                //For browsers like Internet Explorer\n                element.focus();\n                sel = document.selection.createRange();\n                sel.text = value;\n                element.focus();\n            } else if (element.selectionStart || element.selectionStart == '0') { //eslint-disable-line eqeqeq\n                //For browsers like Firefox and Webkit based\n                startPos = element.selectionStart;\n                endPos = element.selectionEnd;\n                scrollTop = element.scrollTop;\n                element.value = element.value.substring(0, startPos) + value +\n                    element.value.substring(startPos, endPos) + element.value.substring(endPos, element.value.length);\n                element.focus();\n                element.selectionStart = startPos + value.length;\n                element.selectionEnd = startPos + value.length + element.value.substring(startPos, endPos).length;\n                element.scrollTop = scrollTop;\n            } else {\n                element.value += value;\n                element.focus();\n            }\n        },\n\n        /**\n         * Return opener Window object if it exists, not closed and editor is active\n         *\n         * @param {String} targetElementId\n         * return {Object|null}\n         */\n        getMediaBrowserOpener: function (targetElementId) {\n            if (!_.isUndefined(wysiwyg) && wysiwyg.get(targetElementId) && !_.isUndefined(tinyMceEditors)) {\n                return tinyMceEditors.get(targetElementId).getMediaBrowserOpener();\n            }\n\n            return null;\n        },\n\n        /**\n         * Get target element\n         *\n         * @param {String} targetElementId\n         * @returns {*|n.fn.init|jQuery|HTMLElement}\n         */\n        getTargetElement: function (targetElementId) {\n\n            if (!_.isUndefined(wysiwyg) && wysiwyg.get(targetElementId)) {\n                return this.getMediaBrowserOpener(targetElementId) || window;\n            }\n\n            return $('#' + targetElementId);\n        }\n    };\n});\n","Magento_MediaGalleryUi/js/grid/massaction/massactions.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'uiComponent',\n    'Magento_MediaGalleryUi/js/action/deleteImageWithDetailConfirmation',\n    'uiLayout',\n    'underscore',\n    'Magento_Ui/js/modal/alert',\n    'mage/translate'\n], function ($, Component, DeleteImages, Layout, _, uiAlert, $t) {\n    'use strict';\n\n    return Component.extend({\n        defaults: {\n            allowedActions: [],\n            deleteButtonSelector: '#delete_selected_massaction',\n            deleteImagesSelector: '#delete_massaction',\n            mediaGalleryImageDetailsName: 'mediaGalleryImageDetails',\n            modules: {\n                massactionView: '${ $.name }_view',\n                imageModel: '${ $.imageModelName }',\n                mediaGalleryImageDetails: '${ $.mediaGalleryImageDetailsName }'\n            },\n            viewConfig: [\n                {\n                    component: 'Magento_MediaGalleryUi/js/grid/massaction/massactionView',\n                    name: '${ $.name }_view'\n                }\n            ],\n            imports: {\n                imageItems: '${ $.mediaGalleryProvider }:data.items'\n            },\n            listens: {\n                imageItems: 'checkButtonVisibility'\n            },\n            exports: {\n                massActionMode: '${ $.name }_view:massActionMode'\n            }\n        },\n\n        /**\n         * Initializes media gallery massaction component.\n         *\n         * @returns {Sticky} Chainable.\n         */\n        initialize: function () {\n            this._super().observe([\n                'massActionMode'\n            ]);\n            this.initView();\n            this.initEvents();\n\n            return this;\n        },\n\n        /**\n         * Initialize child components\n         *\n         * @returns {Object}\n         */\n        initView: function () {\n            Layout(this.viewConfig);\n\n            return this;\n        },\n\n        /**\n         * Initilize massactions events for media gallery grid.\n         */\n        initEvents: function () {\n            $(window).on('massAction.MediaGallery', function () {\n                if (this.massActionMode()) {\n                    return;\n                }\n                this.imageModel().selected(null);\n                this.massActionMode(true);\n                this.switchMode();\n            }.bind(this));\n\n            $(window).on('terminateMassAction.MediaGallery', function () {\n                if (!this.massActionMode()) {\n                    return;\n                }\n\n                this.massActionMode(false);\n                this.switchMode();\n                this.imageModel().updateSelected();\n            }.bind(this));\n        },\n\n        /**\n         * Return total selected items.\n         */\n        getSelectedCount: function () {\n            if (this.massActionMode() && !_.isNull(this.imageModel().selected())) {\n                return Object.keys(this.imageModel().selected()).length;\n            }\n\n            return 0;\n        },\n\n        /**\n         * If images records less than one, disable \"delete images\" button\n         */\n        checkButtonVisibility: function () {\n            if (!this.allowedActions.includes('delete_assets')) {\n                return;\n            }\n\n            if (this.imageItems.length < 1) {\n                $(this.deleteImagesSelector).addClass('disabled');\n            } else {\n                $(this.deleteImagesSelector).removeClass('disabled');\n            }\n        },\n\n        /**\n         * Switch massaction per current event.\n         */\n        switchMode: function () {\n            this.massactionView().switchView();\n            this.handleDeleteAction();\n        },\n\n        /**\n         * Change Default  behavior of delete image to bulk deletion.\n         */\n        handleDeleteAction: function () {\n            if (this.massActionMode()) {\n                $(this.deleteButtonSelector).on('massDelete.MediaGallery', function () {\n                    if (this.getSelectedCount() < 1) {\n                        uiAlert({\n                            content: $t('You need to select at least one image')\n                        });\n\n                    } else {\n                        DeleteImages.deleteImageAction(\n                            this.imageModel().selected(),\n                            this.mediaGalleryImageDetails().imageDetailsUrl,\n                            this.imageModel().deleteImageUrl\n                        ).then(function (response) {\n                            if (response.status === 'canceled') {\n                                return;\n                            }\n                            $(window).trigger('terminateMassAction.MediaGallery');\n                        });\n                    }\n                }.bind(this));\n            }\n        }\n    });\n});\n","Magento_MediaGalleryUi/js/grid/massaction/massactionView.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'uiComponent',\n    'mage/translate',\n    'text!Magento_MediaGalleryUi/template/grid/massactions/massactionButtons.html'\n], function ($, Component, $t, massactionButtons) {\n    'use strict';\n\n    return Component.extend({\n        defaults: {\n            gridSelector: '[data-id=\"media-gallery-masonry-grid\"]',\n            standAloneTitle: 'Manage Gallery',\n            slidePanelTitle: 'Media Gallery',\n            defaultTitle: null,\n            are: null,\n            standAloneArea: 'standalone',\n            slidepanelArea: 'slidepanel',\n            massactionButtonsSelector: '.massaction-buttons',\n            buttonsSelectorStandalone: '.page-actions-buttons',\n            buttonsSelectorSlidePanel: '.page-actions.floating-header',\n            buttons: '.page-main-actions :button',\n            massactionModeTitle: $t('Select Images to Delete')\n        },\n\n        /**\n         * Initializes media gallery massaction component.\n         *\n         * @returns {Sticky} Chainable.\n         */\n        initialize: function () {\n            this._super().observe([\n                'massActionMode'\n            ]);\n\n            return this;\n        },\n\n        /**\n         * Switch massaction view state per active mode.\n         */\n        switchView: function () {\n            this.changePageTitle();\n            this.switchButtons();\n        },\n\n        /**\n         * Hide or show buttons per active mode.\n         */\n        switchButtons: function () {\n            if (this.massActionMode()) {\n                this.activateMassactionButtonView();\n            } else {\n                this.revertButtonsToDefaultView();\n            }\n        },\n\n        /**\n         * Sets buttons to default regular -mode view.\n         */\n        revertButtonsToDefaultView: function () {\n            $(this.buttons).removeClass('no-display');\n            $(this.massactionButtonsSelector).remove();\n        },\n\n        /**\n          * Activate mass action buttons view\n          */\n        activateMassactionButtonView: function () {\n            var buttonsContainer;\n\n            $(this.buttons).addClass('no-display');\n\n            buttonsContainer =  this.area === this.standAloneArea ?\n                this.buttonsSelectorStandalone :\n                this.buttonsSelectorSlidePanel;\n\n            $(buttonsContainer).append(massactionButtons);\n            $(this.massactionButtonsSelector).applyBindings();\n        },\n\n        /**\n         * Change page title per active mode.\n         */\n        changePageTitle: function () {\n            var title = $('h1:contains(' + this.standAloneTitle + ')'),\n                titleSelector;\n\n            if (title.length === 1)  {\n                titleSelector = title;\n                this.area = this.standAloneArea;\n            } else {\n                titleSelector = $('h1:contains(' + this.slidePanelTitle + ')');\n                this.area = this.slidepanelArea;\n            }\n\n            if (this.massActionMode()) {\n                this.defaultTitle = titleSelector.text();\n                titleSelector.text(this.massactionModeTitle);\n            } else {\n                titleSelector = $('h1:contains(' + this.massactionModeTitle + ')');\n                titleSelector.text(this.defaultTitle);\n            }\n        }\n    });\n});\n","Magento_Weee/js/regions-tax-select.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/select'\n], function (Select) {\n    'use strict';\n\n    return Select.extend({\n        defaults: {\n            filterBy: {\n                field: 'country',\n                target: '${ $.parentName }.country:value'\n            }\n        },\n\n        /** @inheritdoc */\n        filter: function () {\n            this._super();\n            this.disableSelect();\n        },\n\n        /**\n         * Disables select if there's no regions/states\n         *\n         * @returns {*} instance - Chainable\n         */\n        disableSelect: function () {\n            var empty = !this.options().length;\n\n            this.disabled(empty);\n\n            if (empty) {\n                this.error('');\n            }\n\n            return this;\n        }\n    });\n});\n","Magento_Weee/js/fpt-group.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'Magento_Ui/js/form/components/group',\n    'uiRegistry',\n    'Magento_Ui/js/lib/validation/validator',\n    'mage/translate',\n    'underscore'\n], function (Group, uiRegistry, validation, $t, _) {\n    'use strict';\n\n    return Group.extend({\n        defaults: {\n            visible: true,\n            label: '',\n            showLabel: true,\n            required: false,\n            template: 'ui/group/group',\n            fieldTemplate: 'ui/form/field',\n            breakLine: true,\n            validateWholeGroup: false,\n            additionalClasses: {}\n        },\n\n        /** @inheritdoc */\n        initialize: function () {\n            validation.addRule('validate-fpt-group', function (value) {\n                if (value.indexOf('?') !== -1) {\n\n                    return false;\n                }\n\n                return true;\n            }, $t(\n                'Set unique country-state combinations within the same fixed product tax. ' +\n                'Verify the combinations and try again.'\n            ));\n\n            this._super();\n        },\n\n        /**\n         *\n         * @private\n         */\n        _handleOptionsAvailability: function () {\n            var parent,\n                dup;\n\n            dup = {};\n            parent = uiRegistry.get(uiRegistry.get(this.parentName).parentName);\n            _.each(parent.elems(), function (elem) {\n                var country,\n                    state,\n                    val,\n                    key;\n\n                country = uiRegistry.get(elem.name + '.countryState.country');\n                state = uiRegistry.get(elem.name + '.countryState.state');\n                val = uiRegistry.get(elem.name + '.countryState.val');\n\n                key = country.value() + (state.value() > 0 ? state.value() : 0);\n                dup[key]++;\n\n                if (!dup[key]) {\n                    dup[key] = 1;\n                    val.value('');\n                } else {\n                    dup[key]++;\n                    val.value(country.value() + '?' + country.name);\n                }\n            });\n        },\n\n        /** @inheritdoc */\n        initElement: function (elem) {\n            var obj;\n\n            obj = this;\n            this._super();\n            elem.on('value', function () {\n                obj._handleOptionsAvailability();\n            });\n\n            return this;\n        }\n    });\n});\n","Magento_Weee/js/fpt-attribute.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* global RegionUpdater */\ndefine([\n    'jquery',\n    'mage/template',\n    'jquery/ui',\n    'mage/adminhtml/form'\n], function ($, mageTemplate) {\n    'use strict';\n\n    $.widget('mage.fptAttribute', {\n        /** @inheritdoc */\n        _create: function () {\n            var widget = this;\n\n            this.rowTmpl = mageTemplate(this.element.find('[data-role=\"row-template\"]').html());\n\n            this._initOptionItem();\n\n            if ($(this.options.bundlePriceType).val() === '0') {\n                this.element.hide();\n            }\n\n            $.each(this.options.itemsData, function () {\n                widget.addItem(this);\n            });\n        },\n\n        /**\n         * @private\n         */\n        _initOptionItem: function () {\n            var widget = this,\n                isOriginalRequired = $(widget.element).hasClass('required');\n\n            this._on({\n                /**\n                 * Add new tax item.\n                 *\n                 * @param {jQuery.Event} event\n                 */\n                'click [data-action=add-fpt-item]': function (event) {\n                    this.addItem(event);\n                },\n\n                /**\n                 * Delete tax item.\n                 *\n                 * @param {jQuery.Event} event\n                 */\n                'click [data-action=delete-fpt-item]': function (event) {\n                    var parent = $(event.target).closest('[data-role=\"fpt-item-row\"]');\n\n                    parent.find('[data-role=\"delete-fpt-item\"]').val(1);\n                    parent.addClass('ignore-validate').hide();\n                },\n\n                /**\n                 * Change tax item country/state.\n                 *\n                 * @param {jQuery.Event} event\n                 * @param {Object} data\n                 */\n                'change [data-role=\"select-country\"]': function (event, data) {\n                    var currentElement = event.target || event.srcElement || event.currentTarget,\n                        parentElement = $(currentElement).closest('[data-role=\"fpt-item-row\"]'),\n                        updater;\n\n                    data = data || {};\n                    updater = new RegionUpdater(\n                        parentElement.find('[data-role=\"select-country\"]').attr('id'), null,\n                        parentElement.find('[data-role=\"select-state\"]').attr('id'),\n                        widget.options.region, 'disable', true\n                    );\n                    updater.update();\n                    //set selected state value if set\n                    if (data.state) {\n                        parentElement.find('[data-role=\"select-state\"]').val(data.state);\n                    }\n\n                    if (!isOriginalRequired && $(widget.element).hasClass('required')) {\n                        $(widget.element).removeClass('required');\n                    }\n                }\n            });\n\n            $(this.options.bundlePriceType).on('change', function (event) {\n                var attributeItems = widget.element.find('[data-role=\"delete-fpt-item\"]');\n\n                if ($(event.target).val() === '0') {\n                    widget.element.hide();\n                    attributeItems.each(function () {\n                        $(this).val(1);\n                    });\n                } else {\n                    widget.element.show();\n                    attributeItems.each(function () {\n                        if ($(this).closest('[data-role=\"fpt-item-row\"]').is(':visible')) {\n                            $(this).val(0);\n                        }\n                    });\n                }\n            });\n        },\n\n        /**\n         * Add custom option.\n         *\n         * @param {jQuery.Event} event\n         */\n        addItem: function (event) {\n            var data = {},\n                currentElement = event.target || event.srcElement || event.currentTarget,\n                tmpl;\n\n            if (typeof currentElement !== 'undefined') {\n                data['website_id'] = 0;\n            } else {\n                data = event;\n            }\n\n            data.index = this.element.find('[data-role=\"fpt-item-row\"]').length;\n\n            tmpl = this.rowTmpl({\n                data: data\n            });\n\n            $(tmpl).appendTo(this.element.find('[data-role=\"fpt-item-container\"]'));\n\n            //set selected website_id value if set\n            if (data['website_id']) {\n                this.element.find('[data-role=\"select-website\"][id$=\"_' + data.index + '_website\"]')\n                    .val(data['website_id']);\n            }\n\n            //set selected country value if set\n            if (data.country) {\n                this.element.find('[data-role=\"select-country\"][id$=\"_' + data.index + '_country\"]')\n                    .val(data.country).trigger('change', data);\n            }\n        }\n    });\n\n    return $.mage.fptAttribute;\n});\n","Magento_Weee/js/price/adjustment.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'Magento_Ui/js/grid/columns/column'\n], function (Element) {\n    'use strict';\n\n    return Element.extend({\n        defaults: {\n            bodyTmpl: 'Magento_Weee/price/adjustment',\n            dataSource: '${ $.parentName }.provider',\n            //Weee configuration constants can be configured from backend\n            inclFptWithDesc: 1,//show FPT and description\n            inclFpt: 0, //show FPT attribute\n            exclFpt: 2, //do not show FPT\n            bothFptPrices: 3 //show price without FPT and with FPT and with description\n        },\n\n        /**\n         * Get Weee attributes.\n         *\n         * @param {Object} row\n         * @return {HTMLElement} Weee html\n         */\n        getWeeeAttributes: function (row) {\n            return row['price_info']['extension_attributes']['weee_attributes'];\n        },\n\n        /**\n         * Get Weee without Tax attributes.\n         *\n         * @param {Object} taxAmount\n         * @return {HTMLElement} Weee html\n         */\n        getWeeeTaxWithoutTax: function (taxAmount) {\n            return taxAmount['amount_excl_tax'];\n        },\n\n        /**\n         * UnsanitizedHtml version of getWeeeTaxWithoutTax.\n         *\n         * @param {Object} taxAmount\n         * @return {HTMLElement} Weee html\n         */\n        getWeeeTaxWithoutTaxUnsanitizedHtml: function (taxAmount) {\n            return this.getWeeeTaxWithoutTax(taxAmount);\n        },\n\n        /**\n         * Get Weee with Tax attributes.\n         *\n         * @param {Object} taxAmount\n         * @return {HTMLElement} Weee html\n         */\n        getWeeeTaxWithTax: function (taxAmount) {\n            return taxAmount['tax_amount_incl_tax'];\n        },\n\n        /**\n         * UnsanitizedHtml version of getWeeeTaxWithTax.\n         *\n         * @param {Object} taxAmount\n         * @return {HTMLElement} Weee html\n         */\n        getWeeeTaxWithTaxUnsanitizedHtml: function (taxAmount) {\n            return this.getWeeeTaxWithTax(taxAmount);\n        },\n\n        /**\n         * Get Weee Tax name.\n         *\n         * @param {String} taxAmount\n         * @return {String} Weee name\n         */\n        getWeeTaxAttributeName: function (taxAmount) {\n            return taxAmount['attribute_code'];\n        },\n\n        /**\n         * Set price type.\n         *\n         * @param {String} priceType\n         * @return {Object}\n         */\n        setPriceType: function (priceType) {\n            this.taxPriceType = priceType;\n\n            return this;\n        },\n\n        /**\n         * Check if Weee Tax must be shown.\n         *\n         * @param {Object} row\n         * @return {Boolean}\n         */\n        isShown: function (row) {\n            return row['price_info']['extension_attributes']['weee_attributes'].length;\n        },\n\n        /**\n         * Get Weee final price.\n         *\n         * @param {Object} row\n         * @return {HTMLElement} Weee final price html\n         */\n        getWeeeAdjustment: function (row) {\n            return row['price_info']['extension_attributes']['weee_adjustment'];\n        },\n\n        /**\n         * UnsanitizedHtml version of getWeeeAdjustment.\n         *\n         * @param {Object} row\n         * @return {HTMLElement} Weee final price html\n         */\n        getWeeeAdjustmentUnsanitizedHtml: function (row) {\n            return this.getWeeeAdjustment(row);\n        },\n\n        /**\n         * Return whether display setting is to display price including FPT only.\n         *\n         * @return {Boolean}\n         */\n        displayPriceInclFpt: function () {\n            return +this.source.data.displayWeee === this.inclFpt;\n        },\n\n        /**\n         * Return whether display setting is to display\n         * price including FPT and FPT description.\n         *\n         * @return {Boolean}\n         */\n        displayPriceInclFptDescr: function () {\n            return +this.source.data.displayWeee === this.inclFptWithDesc;\n        },\n\n        /**\n         * Return whether display setting is to display price\n         * excluding FPT but including FPT description and final price.\n         *\n         * @return {Boolean}\n         */\n        displayPriceExclFptDescr: function () {\n            return +this.source.data.displayWeee === this.exclFpt;\n        },\n\n        /**\n         * Return whether display setting is to display price excluding FPT.\n         *\n         * @return {Boolean}\n         */\n        displayPriceExclFpt: function () {\n            return +this.source.data.displayWeee === this.bothFptPrices;\n        },\n\n        /**\n         * Return whether display setting is to display price excluding tax.\n         *\n         * @return {Boolean}\n         */\n        displayPriceExclTax: function () {\n            return +this.source.data.displayTaxes === this.inclFptWithDesc;\n        },\n\n        /**\n         * Return whether display setting is to display price including tax.\n         *\n         * @return {Boolean}\n         */\n        displayPriceInclTax: function () {\n            return +this.source.data.displayTaxes === this.exclFpt;\n        },\n\n        /**\n         * Return whether display setting is to display\n         * both price including tax and price excluding tax.\n         *\n         * @return {Boolean}\n         */\n        displayBothPricesTax: function () {\n            return +this.source.data.displayTaxes === this.bothFptPrices;\n        }\n    });\n});\n","knockoutjs/knockout-repeat.js":"// REPEAT binding for Knockout http://knockoutjs.com/\n// (c) Michael Best\n// License: MIT (http://www.opensource.org/licenses/mit-license.php)\n// Version 2.1.0\n\n(function(factory) {\n    if (typeof define === 'function' && define.amd) {\n        // [1] AMD anonymous module\n        define(['knockout'], factory);\n    } else if (typeof exports === 'object') {\n        // [2] commonJS\n        factory(require('knockout'));\n    } else {\n        // [3] No module loader (plain <script> tag) - put directly in global namespace\n        factory(window.ko);\n    }\n})(function(ko) {\n\nif (!ko.virtualElements)\n    throw Error('Repeat requires at least Knockout 2.1');\n\nvar ko_bindingFlags = ko.bindingFlags || {};\nvar ko_unwrap = ko.utils.unwrapObservable;\n\nvar koProtoName = '__ko_proto__';\n\nif (ko.version >= \"3.0.0\") {\n    // In Knockout 3.0.0, use the node preprocessor to replace a node with a repeat binding with a virtual element\n    var provider = ko.bindingProvider.instance, previousPreprocessFn = provider.preprocessNode;\n    provider.preprocessNode = function(node) {\n        var newNodes, nodeBinding;\n        if (!previousPreprocessFn || !(newNodes = previousPreprocessFn.call(this, node))) {\n            if (node.nodeType === 1 && (nodeBinding = node.getAttribute('data-bind'))) {\n                if (/^\\s*repeat\\s*:/.test(nodeBinding)) {\n                    var leadingComment = node.ownerDocument.createComment('ko ' + nodeBinding),\n                        trailingComment = node.ownerDocument.createComment('/ko');\n                    node.parentNode.insertBefore(leadingComment, node);\n                    node.parentNode.insertBefore(trailingComment, node.nextSibling);\n                    node.removeAttribute('data-bind');\n                    newNodes = [leadingComment, node, trailingComment];\n                }\n            }\n        }\n        return newNodes;\n    };\n}\n\nko.virtualElements.allowedBindings.repeat = true;\nko.bindingHandlers.repeat = {\n    flags: ko_bindingFlags.contentBind | ko_bindingFlags.canUseVirtual,\n    init: function(element, valueAccessor, allBindingsAccessor, xxx, bindingContext) {\n\n        // Read and set fixed options--these options cannot be changed\n        var repeatParam = ko_unwrap(valueAccessor());\n        if (repeatParam && typeof repeatParam == 'object' && !('length' in repeatParam)) {\n            var repeatIndex = repeatParam.index,\n                repeatData = repeatParam.item,\n                repeatStep = repeatParam.step,\n                repeatReversed = repeatParam.reverse,\n                repeatBind = repeatParam.bind,\n                repeatInit = repeatParam.init,\n                repeatUpdate = repeatParam.update;\n        }\n        // Set default values for options that need it\n        repeatIndex = repeatIndex || '$index';\n        repeatData = repeatData || ko.bindingHandlers.repeat.itemName || '$item';\n        repeatStep = repeatStep || 1;\n        repeatReversed = repeatReversed || false;\n\n        var parent = element.parentNode, placeholder;\n        if (element.nodeType == 8) {    // virtual element\n            // Extract the \"children\" and find the single element node\n            var childNodes = ko.utils.arrayFilter(ko.virtualElements.childNodes(element), function(node) { return node.nodeType == 1;});\n            if (childNodes.length !== 1) {\n                throw Error(\"Repeat binding requires a single element to repeat\");\n            }\n            ko.virtualElements.emptyNode(element);\n\n            // The placeholder is the closing comment normally, or the opening comment if reversed\n            placeholder = repeatReversed ? element : element.nextSibling;\n            // The element to repeat is the contained element\n            element = childNodes[0];\n        } else {    // regular element\n            // First clean the element node and remove node's binding\n            var origBindString = element.getAttribute('data-bind');\n            ko.cleanNode(element);\n            element.removeAttribute('data-bind');\n\n            // Original element is no longer needed: delete it and create a placeholder comment\n            placeholder = element.ownerDocument.createComment('ko_repeatplaceholder ' + origBindString);\n            parent.replaceChild(placeholder, element);\n        }\n\n        // extract and remove a data-repeat-bind attribute, if present\n        if (!repeatBind) {\n            repeatBind = element.getAttribute('data-repeat-bind');\n            if (repeatBind) {\n                element.removeAttribute('data-repeat-bind');\n            }\n        }\n\n        // Make a copy of the element node to be copied for each repetition\n        var cleanNode = element.cloneNode(true);\n        if (typeof repeatBind == \"string\") {\n            cleanNode.setAttribute('data-bind', repeatBind);\n            repeatBind = null;\n        }\n\n        // Set up persistent data\n        var lastRepeatCount = 0,\n            notificationObservable = ko.observable(),\n            repeatArray, arrayObservable;\n\n        if (repeatInit) {\n            repeatInit(parent);\n        }\n\n        var subscribable = ko.computed(function() {\n            function makeArrayItemAccessor(index) {\n                var f = function(newValue) {\n                    var item = repeatArray[index];\n                    // Reading the value of the item\n                    if (!arguments.length) {\n                        notificationObservable();   // for dependency tracking\n                        return ko_unwrap(item);\n                    }\n                    // Writing a value to the item\n                    if (ko.isObservable(item)) {\n                        item(newValue);\n                    } else if (arrayObservable && arrayObservable.splice) {\n                        arrayObservable.splice(index, 1, newValue);\n                    } else {\n                        repeatArray[index] = newValue;\n                    }\n                    return this;\n                };\n                // Pretend that our accessor function is an observable\n                f[koProtoName] = ko.observable;\n                return f;\n            }\n\n            function makeBinding(item, index, context) {\n                return repeatArray\n                    ? function() { return repeatBind.call(bindingContext.$data, item, index, context); }\n                    : function() { return repeatBind.call(bindingContext.$data, index, context); }\n            }\n\n            // Read and set up variable options--these options can change and will update the binding\n            var paramObservable = valueAccessor(), repeatParam = ko_unwrap(paramObservable), repeatCount = 0;\n            if (repeatParam && typeof repeatParam == 'object') {\n                if ('length' in repeatParam) {\n                    repeatArray = repeatParam;\n                    repeatCount = repeatArray.length;\n                } else {\n                    if ('foreach' in repeatParam) {\n                        repeatArray = ko_unwrap(paramObservable = repeatParam.foreach);\n                        if (repeatArray && typeof repeatArray == 'object' && 'length' in repeatArray) {\n                            repeatCount = repeatArray.length || 0;\n                        } else {\n                            repeatCount = repeatArray || 0;\n                            repeatArray = null;\n                        }\n                    }\n                    // If a count value is provided (>0), always output that number of items\n                    if ('count' in repeatParam)\n                        repeatCount = ko_unwrap(repeatParam.count) || repeatCount;\n                    // If a limit is provided, don't output more than the limit\n                    if ('limit' in repeatParam)\n                        repeatCount = Math.min(repeatCount, ko_unwrap(repeatParam.limit)) || repeatCount;\n                }\n                arrayObservable = repeatArray && ko.isObservable(paramObservable) ? paramObservable : null;\n            } else {\n                repeatCount = repeatParam || 0;\n            }\n\n            // Remove nodes from end if array is shorter\n            for (; lastRepeatCount > repeatCount; lastRepeatCount-=repeatStep) {\n                ko.removeNode(repeatReversed ? placeholder.nextSibling : placeholder.previousSibling);\n            }\n\n            // Notify existing nodes of change\n            notificationObservable.notifySubscribers();\n\n            // Add nodes to end if array is longer (also initially populates nodes)\n            for (; lastRepeatCount < repeatCount; lastRepeatCount+=repeatStep) {\n                // Clone node and add to document\n                var newNode = cleanNode.cloneNode(true);\n                parent.insertBefore(newNode, repeatReversed ? placeholder.nextSibling : placeholder);\n                newNode.setAttribute('data-repeat-index', lastRepeatCount);\n\n                // Apply bindings to inserted node\n                if (repeatArray && repeatData == '$data') {\n                    var newContext = bindingContext.createChildContext(makeArrayItemAccessor(lastRepeatCount));\n                } else {\n                    var newContext = bindingContext.extend();\n                    if (repeatArray)\n                        newContext[repeatData] = makeArrayItemAccessor(lastRepeatCount);\n                }\n                newContext[repeatIndex] = lastRepeatCount;\n                if (repeatBind) {\n                    var result = ko.applyBindingsToNode(newNode, makeBinding(newContext[repeatData], lastRepeatCount, newContext), newContext, true),\n                        shouldBindDescendants = result && result.shouldBindDescendants;\n                }\n                if (!repeatBind || (result && shouldBindDescendants !== false)) {\n                    ko.applyBindings(newContext, newNode);\n                }\n            }\n            if (repeatUpdate) {\n                repeatUpdate(parent);\n            }\n        }, null, {disposeWhenNodeIsRemoved: placeholder});\n\n        return { controlsDescendantBindings: true, subscribable: subscribable };\n    }\n};\n});","knockoutjs/knockout.js":"/*!\n * Knockout JavaScript library v3.5.1\n * (c) The Knockout.js team - http://knockoutjs.com/\n * License: MIT (http://www.opensource.org/licenses/mit-license.php)\n */\n\n(function(){\n    var DEBUG=true;\n    (function(undefined){\n        // (0, eval)('this') is a robust way of getting a reference to the global object\n        // For details, see http://stackoverflow.com/questions/14119988/return-this-0-evalthis/14120023#14120023\n        var window = this || (0, eval)('this'),\n            document = window['document'],\n            navigator = window['navigator'],\n            jQueryInstance = window[\"jQuery\"],\n            JSON = window[\"JSON\"];\n\n        if (!jQueryInstance && typeof jQuery !== \"undefined\") {\n            jQueryInstance = jQuery;\n        }\n        (function(factory) {\n            // Support three module loading scenarios\n            if (typeof define === 'function' && define['amd']) {\n                // [1] AMD anonymous module\n                define(['exports', 'require'], factory);\n            } else if (typeof exports === 'object' && typeof module === 'object') {\n                // [2] CommonJS/Node.js\n                factory(module['exports'] || exports);  // module.exports is for Node.js\n            } else {\n                // [3] No module loader (plain <script> tag) - put directly in global namespace\n                factory(window['ko'] = {});\n            }\n        }(function(koExports, amdRequire){\n// Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).\n// In the future, the following \"ko\" variable may be made distinct from \"koExports\" so that private objects are not externally reachable.\n            var ko = typeof koExports !== 'undefined' ? koExports : {};\n// Google Closure Compiler helpers (used only to make the minified file smaller)\n            ko.exportSymbol = function(koPath, object) {\n                var tokens = koPath.split(\".\");\n\n                // In the future, \"ko\" may become distinct from \"koExports\" (so that non-exported objects are not reachable)\n                // At that point, \"target\" would be set to: (typeof koExports !== \"undefined\" ? koExports : ko)\n                var target = ko;\n\n                for (var i = 0; i < tokens.length - 1; i++)\n                    target = target[tokens[i]];\n                target[tokens[tokens.length - 1]] = object;\n            };\n            ko.exportProperty = function(owner, publicName, object) {\n                owner[publicName] = object;\n            };\n            ko.version = \"3.5.1\";\n\n            ko.exportSymbol('version', ko.version);\n// For any options that may affect various areas of Knockout and aren't directly associated with data binding.\n            ko.options = {\n                'deferUpdates': false,\n                'useOnlyNativeEvents': false,\n                'foreachHidesDestroyed': false\n            };\n\n//ko.exportSymbol('options', ko.options);   // 'options' isn't minified\n            ko.utils = (function () {\n                var hasOwnProperty = Object.prototype.hasOwnProperty;\n\n                function objectForEach(obj, action) {\n                    for (var prop in obj) {\n                        if (hasOwnProperty.call(obj, prop)) {\n                            action(prop, obj[prop]);\n                        }\n                    }\n                }\n\n                function extend(target, source) {\n                    if (source) {\n                        for(var prop in source) {\n                            if(hasOwnProperty.call(source, prop)) {\n                                target[prop] = source[prop];\n                            }\n                        }\n                    }\n                    return target;\n                }\n\n                function setPrototypeOf(obj, proto) {\n                    obj.__proto__ = proto;\n                    return obj;\n                }\n\n                var canSetPrototype = ({ __proto__: [] } instanceof Array);\n                var canUseSymbols = !DEBUG && typeof Symbol === 'function';\n\n                // Represent the known event types in a compact way, then at runtime transform it into a hash with event name as key (for fast lookup)\n                var knownEvents = {}, knownEventTypesByEventName = {};\n                var keyEventTypeName = (navigator && /Firefox\\/2/i.test(navigator.userAgent)) ? 'KeyboardEvent' : 'UIEvents';\n                knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];\n                knownEvents['MouseEvents'] = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'];\n                objectForEach(knownEvents, function(eventType, knownEventsForType) {\n                    if (knownEventsForType.length) {\n                        for (var i = 0, j = knownEventsForType.length; i < j; i++)\n                            knownEventTypesByEventName[knownEventsForType[i]] = eventType;\n                    }\n                });\n                var eventsThatMustBeRegisteredUsingAttachEvent = { 'propertychange': true }; // Workaround for an IE9 issue - https://github.com/SteveSanderson/knockout/issues/406\n\n                // Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)\n                // Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.\n                // Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.\n                // If there is a future need to detect specific versions of IE10+, we will amend this.\n                var ieVersion = document && (function() {\n                    var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');\n\n                    // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment\n                    while (\n                        div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',\n                            iElems[0]\n                        ) {}\n                    return version > 4 ? version : undefined;\n                }());\n                var isIe6 = ieVersion === 6,\n                    isIe7 = ieVersion === 7;\n\n                function isClickOnCheckableElement(element, eventType) {\n                    if ((ko.utils.tagNameLower(element) !== \"input\") || !element.type) return false;\n                    if (eventType.toLowerCase() != \"click\") return false;\n                    var inputType = element.type;\n                    return (inputType == \"checkbox\") || (inputType == \"radio\");\n                }\n\n                // For details on the pattern for changing node classes\n                // see: https://github.com/knockout/knockout/issues/1597\n                var cssClassNameRegex = /\\S+/g;\n\n                var jQueryEventAttachName;\n\n                function toggleDomNodeCssClass(node, classNames, shouldHaveClass) {\n                    var addOrRemoveFn;\n                    if (classNames) {\n                        if (typeof node.classList === 'object') {\n                            addOrRemoveFn = node.classList[shouldHaveClass ? 'add' : 'remove'];\n                            ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {\n                                addOrRemoveFn.call(node.classList, className);\n                            });\n                        } else if (typeof node.className['baseVal'] === 'string') {\n                            // SVG tag .classNames is an SVGAnimatedString instance\n                            toggleObjectClassPropertyString(node.className, 'baseVal', classNames, shouldHaveClass);\n                        } else {\n                            // node.className ought to be a string.\n                            toggleObjectClassPropertyString(node, 'className', classNames, shouldHaveClass);\n                        }\n                    }\n                }\n\n                function toggleObjectClassPropertyString(obj, prop, classNames, shouldHaveClass) {\n                    // obj/prop is either a node/'className' or a SVGAnimatedString/'baseVal'.\n                    var currentClassNames = obj[prop].match(cssClassNameRegex) || [];\n                    ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {\n                        ko.utils.addOrRemoveItem(currentClassNames, className, shouldHaveClass);\n                    });\n                    obj[prop] = currentClassNames.join(\" \");\n                }\n\n                return {\n                    fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],\n\n                    arrayForEach: function (array, action, actionOwner) {\n                        for (var i = 0, j = array.length; i < j; i++) {\n                            action.call(actionOwner, array[i], i, array);\n                        }\n                    },\n\n                    arrayIndexOf: typeof Array.prototype.indexOf == \"function\"\n                        ? function (array, item) {\n                            return Array.prototype.indexOf.call(array, item);\n                        }\n                        : function (array, item) {\n                            for (var i = 0, j = array.length; i < j; i++) {\n                                if (array[i] === item)\n                                    return i;\n                            }\n                            return -1;\n                        },\n\n                    arrayFirst: function (array, predicate, predicateOwner) {\n                        for (var i = 0, j = array.length; i < j; i++) {\n                            if (predicate.call(predicateOwner, array[i], i, array))\n                                return array[i];\n                        }\n                        return undefined;\n                    },\n\n                    arrayRemoveItem: function (array, itemToRemove) {\n                        var index = ko.utils.arrayIndexOf(array, itemToRemove);\n                        if (index > 0) {\n                            array.splice(index, 1);\n                        }\n                        else if (index === 0) {\n                            array.shift();\n                        }\n                    },\n\n                    arrayGetDistinctValues: function (array) {\n                        var result = [];\n                        if (array) {\n                            ko.utils.arrayForEach(array, function(item) {\n                                if (ko.utils.arrayIndexOf(result, item) < 0)\n                                    result.push(item);\n                            });\n                        }\n                        return result;\n                    },\n\n                    arrayMap: function (array, mapping, mappingOwner) {\n                        var result = [];\n                        if (array) {\n                            for (var i = 0, j = array.length; i < j; i++)\n                                result.push(mapping.call(mappingOwner, array[i], i));\n                        }\n                        return result;\n                    },\n\n                    arrayFilter: function (array, predicate, predicateOwner) {\n                        var result = [];\n                        if (array) {\n                            for (var i = 0, j = array.length; i < j; i++)\n                                if (predicate.call(predicateOwner, array[i], i))\n                                    result.push(array[i]);\n                        }\n                        return result;\n                    },\n\n                    arrayPushAll: function (array, valuesToPush) {\n                        if (valuesToPush instanceof Array)\n                            array.push.apply(array, valuesToPush);\n                        else\n                            for (var i = 0, j = valuesToPush.length; i < j; i++)\n                                array.push(valuesToPush[i]);\n                        return array;\n                    },\n\n                    addOrRemoveItem: function(array, value, included) {\n                        var existingEntryIndex = ko.utils.arrayIndexOf(ko.utils.peekObservable(array), value);\n                        if (existingEntryIndex < 0) {\n                            if (included)\n                                array.push(value);\n                        } else {\n                            if (!included)\n                                array.splice(existingEntryIndex, 1);\n                        }\n                    },\n\n                    canSetPrototype: canSetPrototype,\n\n                    extend: extend,\n\n                    setPrototypeOf: setPrototypeOf,\n\n                    setPrototypeOfOrExtend: canSetPrototype ? setPrototypeOf : extend,\n\n                    objectForEach: objectForEach,\n\n                    objectMap: function(source, mapping, mappingOwner) {\n                        if (!source)\n                            return source;\n                        var target = {};\n                        for (var prop in source) {\n                            if (hasOwnProperty.call(source, prop)) {\n                                target[prop] = mapping.call(mappingOwner, source[prop], prop, source);\n                            }\n                        }\n                        return target;\n                    },\n\n                    emptyDomNode: function (domNode) {\n                        while (domNode.firstChild) {\n                            ko.removeNode(domNode.firstChild);\n                        }\n                    },\n\n                    moveCleanedNodesToContainerElement: function(nodes) {\n                        // Ensure it's a real array, as we're about to reparent the nodes and\n                        // we don't want the underlying collection to change while we're doing that.\n                        var nodesArray = ko.utils.makeArray(nodes);\n                        var templateDocument = (nodesArray[0] && nodesArray[0].ownerDocument) || document;\n\n                        var container = templateDocument.createElement('div');\n                        for (var i = 0, j = nodesArray.length; i < j; i++) {\n                            container.appendChild(ko.cleanNode(nodesArray[i]));\n                        }\n                        return container;\n                    },\n\n                    cloneNodes: function (nodesArray, shouldCleanNodes) {\n                        for (var i = 0, j = nodesArray.length, newNodesArray = []; i < j; i++) {\n                            var clonedNode = nodesArray[i].cloneNode(true);\n                            newNodesArray.push(shouldCleanNodes ? ko.cleanNode(clonedNode) : clonedNode);\n                        }\n                        return newNodesArray;\n                    },\n\n                    setDomNodeChildren: function (domNode, childNodes) {\n                        ko.utils.emptyDomNode(domNode);\n                        if (childNodes) {\n                            for (var i = 0, j = childNodes.length; i < j; i++)\n                                domNode.appendChild(childNodes[i]);\n                        }\n                    },\n\n                    replaceDomNodes: function (nodeToReplaceOrNodeArray, newNodesArray) {\n                        var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType ? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;\n                        if (nodesToReplaceArray.length > 0) {\n                            var insertionPoint = nodesToReplaceArray[0];\n                            var parent = insertionPoint.parentNode;\n                            for (var i = 0, j = newNodesArray.length; i < j; i++)\n                                parent.insertBefore(newNodesArray[i], insertionPoint);\n                            for (var i = 0, j = nodesToReplaceArray.length; i < j; i++) {\n                                ko.removeNode(nodesToReplaceArray[i]);\n                            }\n                        }\n                    },\n\n                    fixUpContinuousNodeArray: function(continuousNodeArray, parentNode) {\n                        // Before acting on a set of nodes that were previously outputted by a template function, we have to reconcile\n                        // them against what is in the DOM right now. It may be that some of the nodes have already been removed, or that\n                        // new nodes might have been inserted in the middle, for example by a binding. Also, there may previously have been\n                        // leading comment nodes (created by rewritten string-based templates) that have since been removed during binding.\n                        // So, this function translates the old \"map\" output array into its best guess of the set of current DOM nodes.\n                        //\n                        // Rules:\n                        //   [A] Any leading nodes that have been removed should be ignored\n                        //       These most likely correspond to memoization nodes that were already removed during binding\n                        //       See https://github.com/knockout/knockout/pull/440\n                        //   [B] Any trailing nodes that have been remove should be ignored\n                        //       This prevents the code here from adding unrelated nodes to the array while processing rule [C]\n                        //       See https://github.com/knockout/knockout/pull/1903\n                        //   [C] We want to output a continuous series of nodes. So, ignore any nodes that have already been removed,\n                        //       and include any nodes that have been inserted among the previous collection\n\n                        if (continuousNodeArray.length) {\n                            // The parent node can be a virtual element; so get the real parent node\n                            parentNode = (parentNode.nodeType === 8 && parentNode.parentNode) || parentNode;\n\n                            // Rule [A]\n                            while (continuousNodeArray.length && continuousNodeArray[0].parentNode !== parentNode)\n                                continuousNodeArray.splice(0, 1);\n\n                            // Rule [B]\n                            while (continuousNodeArray.length > 1 && continuousNodeArray[continuousNodeArray.length - 1].parentNode !== parentNode)\n                                continuousNodeArray.length--;\n\n                            // Rule [C]\n                            if (continuousNodeArray.length > 1) {\n                                var current = continuousNodeArray[0], last = continuousNodeArray[continuousNodeArray.length - 1];\n                                // Replace with the actual new continuous node set\n                                continuousNodeArray.length = 0;\n                                while (current !== last) {\n                                    continuousNodeArray.push(current);\n                                    current = current.nextSibling;\n                                }\n                                continuousNodeArray.push(last);\n                            }\n                        }\n                        return continuousNodeArray;\n                    },\n\n                    setOptionNodeSelectionState: function (optionNode, isSelected) {\n                        // IE6 sometimes throws \"unknown error\" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser.\n                        if (ieVersion < 7)\n                            optionNode.setAttribute(\"selected\", isSelected);\n                        else\n                            optionNode.selected = isSelected;\n                    },\n\n                    stringTrim: function (string) {\n                        return string === null || string === undefined ? '' :\n                            string.trim ?\n                                string.trim() :\n                                string.toString().replace(/^[\\s\\xa0]+|[\\s\\xa0]+$/g, '');\n                    },\n\n                    stringStartsWith: function (string, startsWith) {\n                        string = string || \"\";\n                        if (startsWith.length > string.length)\n                            return false;\n                        return string.substring(0, startsWith.length) === startsWith;\n                    },\n\n                    domNodeIsContainedBy: function (node, containedByNode) {\n                        if (node === containedByNode)\n                            return true;\n                        if (node.nodeType === 11)\n                            return false; // Fixes issue #1162 - can't use node.contains for document fragments on IE8\n                        if (containedByNode.contains)\n                            return containedByNode.contains(node.nodeType !== 1 ? node.parentNode : node);\n                        if (containedByNode.compareDocumentPosition)\n                            return (containedByNode.compareDocumentPosition(node) & 16) == 16;\n                        while (node && node != containedByNode) {\n                            node = node.parentNode;\n                        }\n                        return !!node;\n                    },\n\n                    domNodeIsAttachedToDocument: function (node) {\n                        return ko.utils.domNodeIsContainedBy(node, node.ownerDocument.documentElement);\n                    },\n\n                    anyDomNodeIsAttachedToDocument: function(nodes) {\n                        return !!ko.utils.arrayFirst(nodes, ko.utils.domNodeIsAttachedToDocument);\n                    },\n\n                    tagNameLower: function(element) {\n                        // For HTML elements, tagName will always be upper case; for XHTML elements, it'll be lower case.\n                        // Possible future optimization: If we know it's an element from an XHTML document (not HTML),\n                        // we don't need to do the .toLowerCase() as it will always be lower case anyway.\n                        return element && element.tagName && element.tagName.toLowerCase();\n                    },\n\n                    catchFunctionErrors: function (delegate) {\n                        return ko['onError'] ? function () {\n                            try {\n                                return delegate.apply(this, arguments);\n                            } catch (e) {\n                                ko['onError'] && ko['onError'](e);\n                                throw e;\n                            }\n                        } : delegate;\n                    },\n\n                    setTimeout: function (handler, timeout) {\n                        return setTimeout(ko.utils.catchFunctionErrors(handler), timeout);\n                    },\n\n                    deferError: function (error) {\n                        setTimeout(function () {\n                            ko['onError'] && ko['onError'](error);\n                            throw error;\n                        }, 0);\n                    },\n\n                    registerEventHandler: function (element, eventType, handler) {\n                        var wrappedHandler = ko.utils.catchFunctionErrors(handler);\n\n                        var mustUseAttachEvent = eventsThatMustBeRegisteredUsingAttachEvent[eventType];\n                        if (!ko.options['useOnlyNativeEvents'] && !mustUseAttachEvent && jQueryInstance) {\n                            if (!jQueryEventAttachName) {\n                                jQueryEventAttachName = (typeof jQueryInstance(element)['on'] == 'function') ? 'on' : 'bind';\n                            }\n                            jQueryInstance(element)[jQueryEventAttachName](eventType, wrappedHandler);\n                        } else if (!mustUseAttachEvent && typeof element.addEventListener == \"function\")\n                            element.addEventListener(eventType, wrappedHandler, false);\n                        else if (typeof element.attachEvent != \"undefined\") {\n                            var attachEventHandler = function (event) { wrappedHandler.call(element, event); },\n                                attachEventName = \"on\" + eventType;\n                            element.attachEvent(attachEventName, attachEventHandler);\n\n                            // IE does not dispose attachEvent handlers automatically (unlike with addEventListener)\n                            // so to avoid leaks, we have to remove them manually. See bug #856\n                            ko.utils.domNodeDisposal.addDisposeCallback(element, function() {\n                                element.detachEvent(attachEventName, attachEventHandler);\n                            });\n                        } else\n                            throw new Error(\"Browser doesn't support addEventListener or attachEvent\");\n                    },\n\n                    triggerEvent: function (element, eventType) {\n                        if (!(element && element.nodeType))\n                            throw new Error(\"element must be a DOM node when calling triggerEvent\");\n\n                        // For click events on checkboxes and radio buttons, jQuery toggles the element checked state *after* the\n                        // event handler runs instead of *before*. (This was fixed in 1.9 for checkboxes but not for radio buttons.)\n                        // IE doesn't change the checked state when you trigger the click event using \"fireEvent\".\n                        // In both cases, we'll use the click method instead.\n                        var useClickWorkaround = isClickOnCheckableElement(element, eventType);\n\n                        if (!ko.options['useOnlyNativeEvents'] && jQueryInstance && !useClickWorkaround) {\n                            jQueryInstance(element)['trigger'](eventType);\n                        } else if (typeof document.createEvent == \"function\") {\n                            if (typeof element.dispatchEvent == \"function\") {\n                                var eventCategory = knownEventTypesByEventName[eventType] || \"HTMLEvents\";\n                                var event = document.createEvent(eventCategory);\n                                event.initEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);\n                                element.dispatchEvent(event);\n                            }\n                            else\n                                throw new Error(\"The supplied element doesn't support dispatchEvent\");\n                        } else if (useClickWorkaround && element.click) {\n                            element.click();\n                        } else if (typeof element.fireEvent != \"undefined\") {\n                            element.fireEvent(\"on\" + eventType);\n                        } else {\n                            throw new Error(\"Browser doesn't support triggering events\");\n                        }\n                    },\n\n                    unwrapObservable: function (value) {\n                        return ko.isObservable(value) ? value() : value;\n                    },\n\n                    peekObservable: function (value) {\n                        return ko.isObservable(value) ? value.peek() : value;\n                    },\n\n                    toggleDomNodeCssClass: toggleDomNodeCssClass,\n\n                    setTextContent: function(element, textContent) {\n                        var value = ko.utils.unwrapObservable(textContent);\n                        if ((value === null) || (value === undefined))\n                            value = \"\";\n\n                        // We need there to be exactly one child: a text node.\n                        // If there are no children, more than one, or if it's not a text node,\n                        // we'll clear everything and create a single text node.\n                        var innerTextNode = ko.virtualElements.firstChild(element);\n                        if (!innerTextNode || innerTextNode.nodeType != 3 || ko.virtualElements.nextSibling(innerTextNode)) {\n                            ko.virtualElements.setDomNodeChildren(element, [element.ownerDocument.createTextNode(value)]);\n                        } else {\n                            innerTextNode.data = value;\n                        }\n\n                        ko.utils.forceRefresh(element);\n                    },\n\n                    setElementName: function(element, name) {\n                        element.name = name;\n\n                        // Workaround IE 6/7 issue\n                        // - https://github.com/SteveSanderson/knockout/issues/197\n                        // - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/\n                        if (ieVersion <= 7) {\n                            try {\n                                var escapedName = element.name.replace(/[&<>'\"]/g, function(r){ return \"&#\" + r.charCodeAt(0) + \";\"; });\n                                element.mergeAttributes(document.createElement(\"<input name='\" + escapedName + \"'/>\"), false);\n                            }\n                            catch(e) {} // For IE9 with doc mode \"IE9 Standards\" and browser mode \"IE9 Compatibility View\"\n                        }\n                    },\n\n                    forceRefresh: function(node) {\n                        // Workaround for an IE9 rendering bug - https://github.com/SteveSanderson/knockout/issues/209\n                        if (ieVersion >= 9) {\n                            // For text nodes and comment nodes (most likely virtual elements), we will have to refresh the container\n                            var elem = node.nodeType == 1 ? node : node.parentNode;\n                            if (elem.style)\n                                elem.style.zoom = elem.style.zoom;\n                        }\n                    },\n\n                    ensureSelectElementIsRenderedCorrectly: function(selectElement) {\n                        // Workaround for IE9 rendering bug - it doesn't reliably display all the text in dynamically-added select boxes unless you force it to re-render by updating the width.\n                        // (See https://github.com/SteveSanderson/knockout/issues/312, http://stackoverflow.com/questions/5908494/select-only-shows-first-char-of-selected-option)\n                        // Also fixes IE7 and IE8 bug that causes selects to be zero width if enclosed by 'if' or 'with'. (See issue #839)\n                        if (ieVersion) {\n                            var originalWidth = selectElement.style.width;\n                            selectElement.style.width = 0;\n                            selectElement.style.width = originalWidth;\n                        }\n                    },\n\n                    range: function (min, max) {\n                        min = ko.utils.unwrapObservable(min);\n                        max = ko.utils.unwrapObservable(max);\n                        var result = [];\n                        for (var i = min; i <= max; i++)\n                            result.push(i);\n                        return result;\n                    },\n\n                    makeArray: function(arrayLikeObject) {\n                        var result = [];\n                        for (var i = 0, j = arrayLikeObject.length; i < j; i++) {\n                            result.push(arrayLikeObject[i]);\n                        };\n                        return result;\n                    },\n\n                    createSymbolOrString: function(identifier) {\n                        return canUseSymbols ? Symbol(identifier) : identifier;\n                    },\n\n                    isIe6 : isIe6,\n                    isIe7 : isIe7,\n                    ieVersion : ieVersion,\n\n                    getFormFields: function(form, fieldName) {\n                        var fields = ko.utils.makeArray(form.getElementsByTagName(\"input\")).concat(ko.utils.makeArray(form.getElementsByTagName(\"textarea\")));\n                        var isMatchingField = (typeof fieldName == 'string')\n                            ? function(field) { return field.name === fieldName }\n                            : function(field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate\n                        var matches = [];\n                        for (var i = fields.length - 1; i >= 0; i--) {\n                            if (isMatchingField(fields[i]))\n                                matches.push(fields[i]);\n                        };\n                        return matches;\n                    },\n\n                    parseJson: function (jsonString) {\n                        if (typeof jsonString == \"string\") {\n                            jsonString = ko.utils.stringTrim(jsonString);\n                            if (jsonString) {\n                                if (JSON && JSON.parse) // Use native parsing where available\n                                    return JSON.parse(jsonString);\n                                return (new Function(\"return \" + jsonString))(); // Fallback on less safe parsing for older browsers\n                            }\n                        }\n                        return null;\n                    },\n\n                    stringifyJson: function (data, replacer, space) {   // replacer and space are optional\n                        if (!JSON || !JSON.stringify)\n                            throw new Error(\"Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js\");\n                        return JSON.stringify(ko.utils.unwrapObservable(data), replacer, space);\n                    },\n\n                    postJson: function (urlOrForm, data, options) {\n                        options = options || {};\n                        var params = options['params'] || {};\n                        var includeFields = options['includeFields'] || this.fieldsIncludedWithJsonPost;\n                        var url = urlOrForm;\n\n                        // If we were given a form, use its 'action' URL and pick out any requested field values\n                        if((typeof urlOrForm == 'object') && (ko.utils.tagNameLower(urlOrForm) === \"form\")) {\n                            var originalForm = urlOrForm;\n                            url = originalForm.action;\n                            for (var i = includeFields.length - 1; i >= 0; i--) {\n                                var fields = ko.utils.getFormFields(originalForm, includeFields[i]);\n                                for (var j = fields.length - 1; j >= 0; j--)\n                                    params[fields[j].name] = fields[j].value;\n                            }\n                        }\n\n                        data = ko.utils.unwrapObservable(data);\n                        var form = document.createElement(\"form\");\n                        form.style.display = \"none\";\n                        form.action = url;\n                        form.method = \"post\";\n                        for (var key in data) {\n                            // Since 'data' this is a model object, we include all properties including those inherited from its prototype\n                            var input = document.createElement(\"input\");\n                            input.type = \"hidden\";\n                            input.name = key;\n                            input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));\n                            form.appendChild(input);\n                        }\n                        objectForEach(params, function(key, value) {\n                            var input = document.createElement(\"input\");\n                            input.type = \"hidden\";\n                            input.name = key;\n                            input.value = value;\n                            form.appendChild(input);\n                        });\n                        document.body.appendChild(form);\n                        options['submitter'] ? options['submitter'](form) : form.submit();\n                        setTimeout(function () { form.parentNode.removeChild(form); }, 0);\n                    }\n                }\n            }());\n\n            ko.exportSymbol('utils', ko.utils);\n            ko.exportSymbol('utils.arrayForEach', ko.utils.arrayForEach);\n            ko.exportSymbol('utils.arrayFirst', ko.utils.arrayFirst);\n            ko.exportSymbol('utils.arrayFilter', ko.utils.arrayFilter);\n            ko.exportSymbol('utils.arrayGetDistinctValues', ko.utils.arrayGetDistinctValues);\n            ko.exportSymbol('utils.arrayIndexOf', ko.utils.arrayIndexOf);\n            ko.exportSymbol('utils.arrayMap', ko.utils.arrayMap);\n            ko.exportSymbol('utils.arrayPushAll', ko.utils.arrayPushAll);\n            ko.exportSymbol('utils.arrayRemoveItem', ko.utils.arrayRemoveItem);\n            ko.exportSymbol('utils.cloneNodes', ko.utils.cloneNodes);\n            ko.exportSymbol('utils.createSymbolOrString', ko.utils.createSymbolOrString);\n            ko.exportSymbol('utils.extend', ko.utils.extend);\n            ko.exportSymbol('utils.fieldsIncludedWithJsonPost', ko.utils.fieldsIncludedWithJsonPost);\n            ko.exportSymbol('utils.getFormFields', ko.utils.getFormFields);\n            ko.exportSymbol('utils.objectMap', ko.utils.objectMap);\n            ko.exportSymbol('utils.peekObservable', ko.utils.peekObservable);\n            ko.exportSymbol('utils.postJson', ko.utils.postJson);\n            ko.exportSymbol('utils.parseJson', ko.utils.parseJson);\n            ko.exportSymbol('utils.registerEventHandler', ko.utils.registerEventHandler);\n            ko.exportSymbol('utils.stringifyJson', ko.utils.stringifyJson);\n            ko.exportSymbol('utils.range', ko.utils.range);\n            ko.exportSymbol('utils.toggleDomNodeCssClass', ko.utils.toggleDomNodeCssClass);\n            ko.exportSymbol('utils.triggerEvent', ko.utils.triggerEvent);\n            ko.exportSymbol('utils.unwrapObservable', ko.utils.unwrapObservable);\n            ko.exportSymbol('utils.objectForEach', ko.utils.objectForEach);\n            ko.exportSymbol('utils.addOrRemoveItem', ko.utils.addOrRemoveItem);\n            ko.exportSymbol('utils.setTextContent', ko.utils.setTextContent);\n            ko.exportSymbol('unwrap', ko.utils.unwrapObservable); // Convenient shorthand, because this is used so commonly\n\n            if (!Function.prototype['bind']) {\n                // Function.prototype.bind is a standard part of ECMAScript 5th Edition (December 2009, http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf)\n                // In case the browser doesn't implement it natively, provide a JavaScript implementation. This implementation is based on the one in prototype.js\n                Function.prototype['bind'] = function (object) {\n                    var originalFunction = this;\n                    if (arguments.length === 1) {\n                        return function () {\n                            return originalFunction.apply(object, arguments);\n                        };\n                    } else {\n                        var partialArgs = Array.prototype.slice.call(arguments, 1);\n                        return function () {\n                            var args = partialArgs.slice(0);\n                            args.push.apply(args, arguments);\n                            return originalFunction.apply(object, args);\n                        };\n                    }\n                };\n            }\n\n            ko.utils.domData = new (function () {\n                var uniqueId = 0;\n                var dataStoreKeyExpandoPropertyName = \"__ko__\" + (new Date).getTime();\n                var dataStore = {};\n\n                var getDataForNode, clear;\n                if (!ko.utils.ieVersion) {\n                    // We considered using WeakMap, but it has a problem in IE 11 and Edge that prevents using\n                    // it cross-window, so instead we just store the data directly on the node.\n                    // See https://github.com/knockout/knockout/issues/2141\n                    getDataForNode = function (node, createIfNotFound) {\n                        var dataForNode = node[dataStoreKeyExpandoPropertyName];\n                        if (!dataForNode && createIfNotFound) {\n                            dataForNode = node[dataStoreKeyExpandoPropertyName] = {};\n                        }\n                        return dataForNode;\n                    };\n                    clear = function (node) {\n                        if (node[dataStoreKeyExpandoPropertyName]) {\n                            delete node[dataStoreKeyExpandoPropertyName];\n                            return true; // Exposing \"did clean\" flag purely so specs can infer whether things have been cleaned up as intended\n                        }\n                        return false;\n                    };\n                } else {\n                    // Old IE versions have memory issues if you store objects on the node, so we use a\n                    // separate data storage and link to it from the node using a string key.\n                    getDataForNode = function (node, createIfNotFound) {\n                        var dataStoreKey = node[dataStoreKeyExpandoPropertyName];\n                        var hasExistingDataStore = dataStoreKey && (dataStoreKey !== \"null\") && dataStore[dataStoreKey];\n                        if (!hasExistingDataStore) {\n                            if (!createIfNotFound)\n                                return undefined;\n                            dataStoreKey = node[dataStoreKeyExpandoPropertyName] = \"ko\" + uniqueId++;\n                            dataStore[dataStoreKey] = {};\n                        }\n                        return dataStore[dataStoreKey];\n                    };\n                    clear = function (node) {\n                        var dataStoreKey = node[dataStoreKeyExpandoPropertyName];\n                        if (dataStoreKey) {\n                            delete dataStore[dataStoreKey];\n                            node[dataStoreKeyExpandoPropertyName] = null;\n                            return true; // Exposing \"did clean\" flag purely so specs can infer whether things have been cleaned up as intended\n                        }\n                        return false;\n                    };\n                }\n\n                return {\n                    get: function (node, key) {\n                        var dataForNode = getDataForNode(node, false);\n                        return dataForNode && dataForNode[key];\n                    },\n                    set: function (node, key, value) {\n                        // Make sure we don't actually create a new domData key if we are actually deleting a value\n                        var dataForNode = getDataForNode(node, value !== undefined /* createIfNotFound */);\n                        dataForNode && (dataForNode[key] = value);\n                    },\n                    getOrSet: function (node, key, value) {\n                        var dataForNode = getDataForNode(node, true /* createIfNotFound */);\n                        return dataForNode[key] || (dataForNode[key] = value);\n                    },\n                    clear: clear,\n\n                    nextKey: function () {\n                        return (uniqueId++) + dataStoreKeyExpandoPropertyName;\n                    }\n                };\n            })();\n\n            ko.exportSymbol('utils.domData', ko.utils.domData);\n            ko.exportSymbol('utils.domData.clear', ko.utils.domData.clear); // Exporting only so specs can clear up after themselves fully\n\n            ko.utils.domNodeDisposal = new (function () {\n                var domDataKey = ko.utils.domData.nextKey();\n                var cleanableNodeTypes = { 1: true, 8: true, 9: true };       // Element, Comment, Document\n                var cleanableNodeTypesWithDescendants = { 1: true, 9: true }; // Element, Document\n\n                function getDisposeCallbacksCollection(node, createIfNotFound) {\n                    var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey);\n                    if ((allDisposeCallbacks === undefined) && createIfNotFound) {\n                        allDisposeCallbacks = [];\n                        ko.utils.domData.set(node, domDataKey, allDisposeCallbacks);\n                    }\n                    return allDisposeCallbacks;\n                }\n                function destroyCallbacksCollection(node) {\n                    ko.utils.domData.set(node, domDataKey, undefined);\n                }\n\n                function cleanSingleNode(node) {\n                    // Run all the dispose callbacks\n                    var callbacks = getDisposeCallbacksCollection(node, false);\n                    if (callbacks) {\n                        callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves)\n                        for (var i = 0; i < callbacks.length; i++)\n                            callbacks[i](node);\n                    }\n\n                    // Erase the DOM data\n                    ko.utils.domData.clear(node);\n\n                    // Perform cleanup needed by external libraries (currently only jQuery, but can be extended)\n                    ko.utils.domNodeDisposal[\"cleanExternalData\"](node);\n\n                    // Clear any immediate-child comment nodes, as these wouldn't have been found by\n                    // node.getElementsByTagName(\"*\") in cleanNode() (comment nodes aren't elements)\n                    if (cleanableNodeTypesWithDescendants[node.nodeType]) {\n                        cleanNodesInList(node.childNodes, true/*onlyComments*/);\n                    }\n                }\n\n                function cleanNodesInList(nodeList, onlyComments) {\n                    var cleanedNodes = [], lastCleanedNode;\n                    for (var i = 0; i < nodeList.length; i++) {\n                        if (!onlyComments || nodeList[i].nodeType === 8) {\n                            cleanSingleNode(cleanedNodes[cleanedNodes.length] = lastCleanedNode = nodeList[i]);\n                            if (nodeList[i] !== lastCleanedNode) {\n                                while (i-- && ko.utils.arrayIndexOf(cleanedNodes, nodeList[i]) == -1) {}\n                            }\n                        }\n                    }\n                }\n\n                return {\n                    addDisposeCallback : function(node, callback) {\n                        if (typeof callback != \"function\")\n                            throw new Error(\"Callback must be a function\");\n                        getDisposeCallbacksCollection(node, true).push(callback);\n                    },\n\n                    removeDisposeCallback : function(node, callback) {\n                        var callbacksCollection = getDisposeCallbacksCollection(node, false);\n                        if (callbacksCollection) {\n                            ko.utils.arrayRemoveItem(callbacksCollection, callback);\n                            if (callbacksCollection.length == 0)\n                                destroyCallbacksCollection(node);\n                        }\n                    },\n\n                    cleanNode : function(node) {\n                        ko.dependencyDetection.ignore(function () {\n                            // First clean this node, where applicable\n                            if (cleanableNodeTypes[node.nodeType]) {\n                                cleanSingleNode(node);\n\n                                // ... then its descendants, where applicable\n                                if (cleanableNodeTypesWithDescendants[node.nodeType]) {\n                                    cleanNodesInList(node.getElementsByTagName(\"*\"));\n                                }\n                            }\n                        });\n\n                        return node;\n                    },\n\n                    removeNode : function(node) {\n                        ko.cleanNode(node);\n                        if (node.parentNode)\n                            node.parentNode.removeChild(node);\n                    },\n\n                    \"cleanExternalData\" : function (node) {\n                        // Special support for jQuery here because it's so commonly used.\n                        // Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData\n                        // so notify it to tear down any resources associated with the node & descendants here.\n                        if (jQueryInstance && (typeof jQueryInstance['cleanData'] == \"function\"))\n                            jQueryInstance['cleanData']([node]);\n                    }\n                };\n            })();\n            ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience\n            ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience\n            ko.exportSymbol('cleanNode', ko.cleanNode);\n            ko.exportSymbol('removeNode', ko.removeNode);\n            ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal);\n            ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);\n            ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeDisposal.removeDisposeCallback);\n            (function () {\n                var none = [0, \"\", \"\"],\n                    table = [1, \"<table>\", \"</table>\"],\n                    tbody = [2, \"<table><tbody>\", \"</tbody></table>\"],\n                    tr = [3, \"<table><tbody><tr>\", \"</tr></tbody></table>\"],\n                    select = [1, \"<select multiple='multiple'>\", \"</select>\"],\n                    lookup = {\n                        'thead': table,\n                        'tbody': table,\n                        'tfoot': table,\n                        'tr': tbody,\n                        'td': tr,\n                        'th': tr,\n                        'option': select,\n                        'optgroup': select\n                    },\n\n                    // This is needed for old IE if you're *not* using either jQuery or innerShiv. Doesn't affect other cases.\n                    mayRequireCreateElementHack = ko.utils.ieVersion <= 8;\n\n                function getWrap(tags) {\n                    var m = tags.match(/^(?:<!--.*?-->\\s*?)*?<([a-z]+)[\\s>]/);\n                    return (m && lookup[m[1]]) || none;\n                }\n\n                function simpleHtmlParse(html, documentContext) {\n                    documentContext || (documentContext = document);\n                    var windowContext = documentContext['parentWindow'] || documentContext['defaultView'] || window;\n\n                    // Based on jQuery's \"clean\" function, but only accounting for table-related elements.\n                    // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's \"clean\" function directly\n\n                    // Note that there's still an issue in IE < 9 whereby it will discard comment nodes that are the first child of\n                    // a descendant node. For example: \"<div><!-- mycomment -->abc</div>\" will get parsed as \"<div>abc</div>\"\n                    // This won't affect anyone who has referenced jQuery, and there's always the workaround of inserting a dummy node\n                    // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.\n\n                    // Trim whitespace, otherwise indexOf won't work as expected\n                    var tags = ko.utils.stringTrim(html).toLowerCase(), div = documentContext.createElement(\"div\"),\n                        wrap = getWrap(tags),\n                        depth = wrap[0];\n\n                    // Go to html and back, then peel off extra wrappers\n                    // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.\n                    var markup = \"ignored<div>\" + wrap[1] + html + wrap[2] + \"</div>\";\n                    if (typeof windowContext['innerShiv'] == \"function\") {\n                        // Note that innerShiv is deprecated in favour of html5shiv. We should consider adding\n                        // support for html5shiv (except if no explicit support is needed, e.g., if html5shiv\n                        // somehow shims the native APIs so it just works anyway)\n                        div.appendChild(windowContext['innerShiv'](markup));\n                    } else {\n                        if (mayRequireCreateElementHack) {\n                            // The document.createElement('my-element') trick to enable custom elements in IE6-8\n                            // only works if we assign innerHTML on an element associated with that document.\n                            documentContext.body.appendChild(div);\n                        }\n\n                        div.innerHTML = markup;\n\n                        if (mayRequireCreateElementHack) {\n                            div.parentNode.removeChild(div);\n                        }\n                    }\n\n                    // Move to the right depth\n                    while (depth--)\n                        div = div.lastChild;\n\n                    return ko.utils.makeArray(div.lastChild.childNodes);\n                }\n\n                function jQueryHtmlParse(html, documentContext) {\n                    // jQuery's \"parseHTML\" function was introduced in jQuery 1.8.0 and is a documented public API.\n                    if (jQueryInstance['parseHTML']) {\n                        return jQueryInstance['parseHTML'](html, documentContext) || []; // Ensure we always return an array and never null\n                    } else {\n                        // For jQuery < 1.8.0, we fall back on the undocumented internal \"clean\" function.\n                        var elems = jQueryInstance['clean']([html], documentContext);\n\n                        // As of jQuery 1.7.1, jQuery parses the HTML by appending it to some dummy parent nodes held in an in-memory document fragment.\n                        // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.\n                        // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.\n                        if (elems && elems[0]) {\n                            // Find the top-most parent element that's a direct child of a document fragment\n                            var elem = elems[0];\n                            while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)\n                                elem = elem.parentNode;\n                            // ... then detach it\n                            if (elem.parentNode)\n                                elem.parentNode.removeChild(elem);\n                        }\n\n                        return elems;\n                    }\n                }\n\n                ko.utils.parseHtmlFragment = function(html, documentContext) {\n                    return jQueryInstance ?\n                        jQueryHtmlParse(html, documentContext) :   // As below, benefit from jQuery's optimisations where possible\n                        simpleHtmlParse(html, documentContext);  // ... otherwise, this simple logic will do in most common cases.\n                };\n\n                ko.utils.parseHtmlForTemplateNodes = function(html, documentContext) {\n                    var nodes = ko.utils.parseHtmlFragment(html, documentContext);\n                    return (nodes.length && nodes[0].parentElement) || ko.utils.moveCleanedNodesToContainerElement(nodes);\n                };\n\n                ko.utils.setHtml = function(node, html) {\n                    ko.utils.emptyDomNode(node);\n\n                    // There's no legitimate reason to display a stringified observable without unwrapping it, so we'll unwrap it\n                    html = ko.utils.unwrapObservable(html);\n\n                    if ((html !== null) && (html !== undefined)) {\n                        if (typeof html != 'string')\n                            html = html.toString();\n\n                        // jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,\n                        // for example <tr> elements which are not normally allowed to exist on their own.\n                        // If you've referenced jQuery we'll use that rather than duplicating its code.\n                        if (jQueryInstance) {\n                            jQueryInstance(node)['html'](html);\n                        } else {\n                            // ... otherwise, use KO's own parsing logic.\n                            var parsedNodes = ko.utils.parseHtmlFragment(html, node.ownerDocument);\n                            for (var i = 0; i < parsedNodes.length; i++)\n                                node.appendChild(parsedNodes[i]);\n                        }\n                    }\n                };\n            })();\n\n            ko.exportSymbol('utils.parseHtmlFragment', ko.utils.parseHtmlFragment);\n            ko.exportSymbol('utils.setHtml', ko.utils.setHtml);\n\n            ko.memoization = (function () {\n                var memos = {};\n\n                function randomMax8HexChars() {\n                    return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);\n                }\n                function generateRandomId() {\n                    return randomMax8HexChars() + randomMax8HexChars();\n                }\n                function findMemoNodes(rootNode, appendToArray) {\n                    if (!rootNode)\n                        return;\n                    if (rootNode.nodeType == 8) {\n                        var memoId = ko.memoization.parseMemoText(rootNode.nodeValue);\n                        if (memoId != null)\n                            appendToArray.push({ domNode: rootNode, memoId: memoId });\n                    } else if (rootNode.nodeType == 1) {\n                        for (var i = 0, childNodes = rootNode.childNodes, j = childNodes.length; i < j; i++)\n                            findMemoNodes(childNodes[i], appendToArray);\n                    }\n                }\n\n                return {\n                    memoize: function (callback) {\n                        if (typeof callback != \"function\")\n                            throw new Error(\"You can only pass a function to ko.memoization.memoize()\");\n                        var memoId = generateRandomId();\n                        memos[memoId] = callback;\n                        return \"<!--[ko_memo:\" + memoId + \"]-->\";\n                    },\n\n                    unmemoize: function (memoId, callbackParams) {\n                        var callback = memos[memoId];\n                        if (callback === undefined)\n                            throw new Error(\"Couldn't find any memo with ID \" + memoId + \". Perhaps it's already been unmemoized.\");\n                        try {\n                            callback.apply(null, callbackParams || []);\n                            return true;\n                        }\n                        finally { delete memos[memoId]; }\n                    },\n\n                    unmemoizeDomNodeAndDescendants: function (domNode, extraCallbackParamsArray) {\n                        var memos = [];\n                        findMemoNodes(domNode, memos);\n                        for (var i = 0, j = memos.length; i < j; i++) {\n                            var node = memos[i].domNode;\n                            var combinedParams = [node];\n                            if (extraCallbackParamsArray)\n                                ko.utils.arrayPushAll(combinedParams, extraCallbackParamsArray);\n                            ko.memoization.unmemoize(memos[i].memoId, combinedParams);\n                            node.nodeValue = \"\"; // Neuter this node so we don't try to unmemoize it again\n                            if (node.parentNode)\n                                node.parentNode.removeChild(node); // If possible, erase it totally (not always possible - someone else might just hold a reference to it then call unmemoizeDomNodeAndDescendants again)\n                        }\n                    },\n\n                    parseMemoText: function (memoText) {\n                        var match = memoText.match(/^\\[ko_memo\\:(.*?)\\]$/);\n                        return match ? match[1] : null;\n                    }\n                };\n            })();\n\n            ko.exportSymbol('memoization', ko.memoization);\n            ko.exportSymbol('memoization.memoize', ko.memoization.memoize);\n            ko.exportSymbol('memoization.unmemoize', ko.memoization.unmemoize);\n            ko.exportSymbol('memoization.parseMemoText', ko.memoization.parseMemoText);\n            ko.exportSymbol('memoization.unmemoizeDomNodeAndDescendants', ko.memoization.unmemoizeDomNodeAndDescendants);\n            ko.tasks = (function () {\n                var scheduler,\n                    taskQueue = [],\n                    taskQueueLength = 0,\n                    nextHandle = 1,\n                    nextIndexToProcess = 0;\n\n                if (window['MutationObserver']) {\n                    // Chrome 27+, Firefox 14+, IE 11+, Opera 15+, Safari 6.1+\n                    // From https://github.com/petkaantonov/bluebird * Copyright (c) 2014 Petka Antonov * License: MIT\n                    scheduler = (function (callback) {\n                        var div = document.createElement(\"div\");\n                        new MutationObserver(callback).observe(div, {attributes: true});\n                        return function () { div.classList.toggle(\"foo\"); };\n                    })(scheduledProcess);\n                } else if (document && \"onreadystatechange\" in document.createElement(\"script\")) {\n                    // IE 6-10\n                    // From https://github.com/YuzuJS/setImmediate * Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola * License: MIT\n                    scheduler = function (callback) {\n                        var script = document.createElement(\"script\");\n                        script.onreadystatechange = function () {\n                            script.onreadystatechange = null;\n                            document.documentElement.removeChild(script);\n                            script = null;\n                            callback();\n                        };\n                        document.documentElement.appendChild(script);\n                    };\n                } else {\n                    scheduler = function (callback) {\n                        setTimeout(callback, 0);\n                    };\n                }\n\n                function processTasks() {\n                    if (taskQueueLength) {\n                        // Each mark represents the end of a logical group of tasks and the number of these groups is\n                        // limited to prevent unchecked recursion.\n                        var mark = taskQueueLength, countMarks = 0;\n\n                        // nextIndexToProcess keeps track of where we are in the queue; processTasks can be called recursively without issue\n                        for (var task; nextIndexToProcess < taskQueueLength; ) {\n                            if (task = taskQueue[nextIndexToProcess++]) {\n                                if (nextIndexToProcess > mark) {\n                                    if (++countMarks >= 5000) {\n                                        nextIndexToProcess = taskQueueLength;   // skip all tasks remaining in the queue since any of them could be causing the recursion\n                                        ko.utils.deferError(Error(\"'Too much recursion' after processing \" + countMarks + \" task groups.\"));\n                                        break;\n                                    }\n                                    mark = taskQueueLength;\n                                }\n                                try {\n                                    task();\n                                } catch (ex) {\n                                    ko.utils.deferError(ex);\n                                }\n                            }\n                        }\n                    }\n                }\n\n                function scheduledProcess() {\n                    processTasks();\n\n                    // Reset the queue\n                    nextIndexToProcess = taskQueueLength = taskQueue.length = 0;\n                }\n\n                function scheduleTaskProcessing() {\n                    ko.tasks['scheduler'](scheduledProcess);\n                }\n\n                var tasks = {\n                    'scheduler': scheduler,     // Allow overriding the scheduler\n\n                    schedule: function (func) {\n                        if (!taskQueueLength) {\n                            scheduleTaskProcessing();\n                        }\n\n                        taskQueue[taskQueueLength++] = func;\n                        return nextHandle++;\n                    },\n\n                    cancel: function (handle) {\n                        var index = handle - (nextHandle - taskQueueLength);\n                        if (index >= nextIndexToProcess && index < taskQueueLength) {\n                            taskQueue[index] = null;\n                        }\n                    },\n\n                    // For testing only: reset the queue and return the previous queue length\n                    'resetForTesting': function () {\n                        var length = taskQueueLength - nextIndexToProcess;\n                        nextIndexToProcess = taskQueueLength = taskQueue.length = 0;\n                        return length;\n                    },\n\n                    runEarly: processTasks\n                };\n\n                return tasks;\n            })();\n\n            ko.exportSymbol('tasks', ko.tasks);\n            ko.exportSymbol('tasks.schedule', ko.tasks.schedule);\n//ko.exportSymbol('tasks.cancel', ko.tasks.cancel);  \"cancel\" isn't minified\n            ko.exportSymbol('tasks.runEarly', ko.tasks.runEarly);\n            ko.extenders = {\n                'throttle': function(target, timeout) {\n                    // Throttling means two things:\n\n                    // (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies\n                    //     notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate\n                    target['throttleEvaluation'] = timeout;\n\n                    // (2) For writable targets (observables, or writable dependent observables), we throttle *writes*\n                    //     so the target cannot change value synchronously or faster than a certain rate\n                    var writeTimeoutInstance = null;\n                    return ko.dependentObservable({\n                        'read': target,\n                        'write': function(value) {\n                            clearTimeout(writeTimeoutInstance);\n                            writeTimeoutInstance = ko.utils.setTimeout(function() {\n                                target(value);\n                            }, timeout);\n                        }\n                    });\n                },\n\n                'rateLimit': function(target, options) {\n                    var timeout, method, limitFunction;\n\n                    if (typeof options == 'number') {\n                        timeout = options;\n                    } else {\n                        timeout = options['timeout'];\n                        method = options['method'];\n                    }\n\n                    // rateLimit supersedes deferred updates\n                    target._deferUpdates = false;\n\n                    limitFunction = typeof method == 'function' ? method : method == 'notifyWhenChangesStop' ?  debounce : throttle;\n                    target.limit(function(callback) {\n                        return limitFunction(callback, timeout, options);\n                    });\n                },\n\n                'deferred': function(target, options) {\n                    if (options !== true) {\n                        throw new Error('The \\'deferred\\' extender only accepts the value \\'true\\', because it is not supported to turn deferral off once enabled.')\n                    }\n\n                    if (!target._deferUpdates) {\n                        target._deferUpdates = true;\n                        target.limit(function (callback) {\n                            var handle,\n                                ignoreUpdates = false;\n                            return function () {\n                                if (!ignoreUpdates) {\n                                    ko.tasks.cancel(handle);\n                                    handle = ko.tasks.schedule(callback);\n\n                                    try {\n                                        ignoreUpdates = true;\n                                        target['notifySubscribers'](undefined, 'dirty');\n                                    } finally {\n                                        ignoreUpdates = false;\n                                    }\n                                }\n                            };\n                        });\n                    }\n                },\n\n                'notify': function(target, notifyWhen) {\n                    target[\"equalityComparer\"] = notifyWhen == \"always\" ?\n                        null :  // null equalityComparer means to always notify\n                        valuesArePrimitiveAndEqual;\n                }\n            };\n\n            var primitiveTypes = { 'undefined':1, 'boolean':1, 'number':1, 'string':1 };\n            function valuesArePrimitiveAndEqual(a, b) {\n                var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);\n                return oldValueIsPrimitive ? (a === b) : false;\n            }\n\n            function throttle(callback, timeout) {\n                var timeoutInstance;\n                return function () {\n                    if (!timeoutInstance) {\n                        timeoutInstance = ko.utils.setTimeout(function () {\n                            timeoutInstance = undefined;\n                            callback();\n                        }, timeout);\n                    }\n                };\n            }\n\n            function debounce(callback, timeout) {\n                var timeoutInstance;\n                return function () {\n                    clearTimeout(timeoutInstance);\n                    timeoutInstance = ko.utils.setTimeout(callback, timeout);\n                };\n            }\n\n            function applyExtenders(requestedExtenders) {\n                var target = this;\n                if (requestedExtenders) {\n                    ko.utils.objectForEach(requestedExtenders, function(key, value) {\n                        var extenderHandler = ko.extenders[key];\n                        if (typeof extenderHandler == 'function') {\n                            target = extenderHandler(target, value) || target;\n                        }\n                    });\n                }\n                return target;\n            }\n\n            ko.exportSymbol('extenders', ko.extenders);\n\n            ko.subscription = function (target, callback, disposeCallback) {\n                this._target = target;\n                this._callback = callback;\n                this._disposeCallback = disposeCallback;\n                this._isDisposed = false;\n                this._node = null;\n                this._domNodeDisposalCallback = null;\n                ko.exportProperty(this, 'dispose', this.dispose);\n                ko.exportProperty(this, 'disposeWhenNodeIsRemoved', this.disposeWhenNodeIsRemoved);\n            };\n            ko.subscription.prototype.dispose = function () {\n                var self = this;\n                if (!self._isDisposed) {\n                    if (self._domNodeDisposalCallback) {\n                        ko.utils.domNodeDisposal.removeDisposeCallback(self._node, self._domNodeDisposalCallback);\n                    }\n                    self._isDisposed = true;\n                    self._disposeCallback();\n\n                    self._target = self._callback = self._disposeCallback = self._node = self._domNodeDisposalCallback = null;\n                }\n            };\n            ko.subscription.prototype.disposeWhenNodeIsRemoved = function (node) {\n                this._node = node;\n                ko.utils.domNodeDisposal.addDisposeCallback(node, this._domNodeDisposalCallback = this.dispose.bind(this));\n            };\n\n            ko.subscribable = function () {\n                ko.utils.setPrototypeOfOrExtend(this, ko_subscribable_fn);\n                ko_subscribable_fn.init(this);\n            }\n\n            var defaultEvent = \"change\";\n\n// Moved out of \"limit\" to avoid the extra closure\n            function limitNotifySubscribers(value, event) {\n                if (!event || event === defaultEvent) {\n                    this._limitChange(value);\n                } else if (event === 'beforeChange') {\n                    this._limitBeforeChange(value);\n                } else {\n                    this._origNotifySubscribers(value, event);\n                }\n            }\n\n            var ko_subscribable_fn = {\n                init: function(instance) {\n                    instance._subscriptions = { \"change\": [] };\n                    instance._versionNumber = 1;\n                },\n\n                subscribe: function (callback, callbackTarget, event) {\n                    var self = this;\n\n                    event = event || defaultEvent;\n                    var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;\n\n                    var subscription = new ko.subscription(self, boundCallback, function () {\n                        ko.utils.arrayRemoveItem(self._subscriptions[event], subscription);\n                        if (self.afterSubscriptionRemove)\n                            self.afterSubscriptionRemove(event);\n                    });\n\n                    if (self.beforeSubscriptionAdd)\n                        self.beforeSubscriptionAdd(event);\n\n                    if (!self._subscriptions[event])\n                        self._subscriptions[event] = [];\n                    self._subscriptions[event].push(subscription);\n\n                    return subscription;\n                },\n\n                \"notifySubscribers\": function (valueToNotify, event) {\n                    event = event || defaultEvent;\n                    if (event === defaultEvent) {\n                        this.updateVersion();\n                    }\n                    if (this.hasSubscriptionsForEvent(event)) {\n                        var subs = event === defaultEvent && this._changeSubscriptions || this._subscriptions[event].slice(0);\n                        try {\n                            ko.dependencyDetection.begin(); // Begin suppressing dependency detection (by setting the top frame to undefined)\n                            for (var i = 0, subscription; subscription = subs[i]; ++i) {\n                                // In case a subscription was disposed during the arrayForEach cycle, check\n                                // for isDisposed on each subscription before invoking its callback\n                                if (!subscription._isDisposed)\n                                    subscription._callback(valueToNotify);\n                            }\n                        } finally {\n                            ko.dependencyDetection.end(); // End suppressing dependency detection\n                        }\n                    }\n                },\n\n                getVersion: function () {\n                    return this._versionNumber;\n                },\n\n                hasChanged: function (versionToCheck) {\n                    return this.getVersion() !== versionToCheck;\n                },\n\n                updateVersion: function () {\n                    ++this._versionNumber;\n                },\n\n                limit: function(limitFunction) {\n                    var self = this, selfIsObservable = ko.isObservable(self),\n                        ignoreBeforeChange, notifyNextChange, previousValue, pendingValue, didUpdate,\n                        beforeChange = 'beforeChange';\n\n                    if (!self._origNotifySubscribers) {\n                        self._origNotifySubscribers = self[\"notifySubscribers\"];\n                        self[\"notifySubscribers\"] = limitNotifySubscribers;\n                    }\n\n                    var finish = limitFunction(function() {\n                        self._notificationIsPending = false;\n\n                        // If an observable provided a reference to itself, access it to get the latest value.\n                        // This allows computed observables to delay calculating their value until needed.\n                        if (selfIsObservable && pendingValue === self) {\n                            pendingValue = self._evalIfChanged ? self._evalIfChanged() : self();\n                        }\n                        var shouldNotify = notifyNextChange || (didUpdate && self.isDifferent(previousValue, pendingValue));\n\n                        didUpdate = notifyNextChange = ignoreBeforeChange = false;\n\n                        if (shouldNotify) {\n                            self._origNotifySubscribers(previousValue = pendingValue);\n                        }\n                    });\n\n                    self._limitChange = function(value, isDirty) {\n                        if (!isDirty || !self._notificationIsPending) {\n                            didUpdate = !isDirty;\n                        }\n                        self._changeSubscriptions = self._subscriptions[defaultEvent].slice(0);\n                        self._notificationIsPending = ignoreBeforeChange = true;\n                        pendingValue = value;\n                        finish();\n                    };\n                    self._limitBeforeChange = function(value) {\n                        if (!ignoreBeforeChange) {\n                            previousValue = value;\n                            self._origNotifySubscribers(value, beforeChange);\n                        }\n                    };\n                    self._recordUpdate = function() {\n                        didUpdate = true;\n                    };\n                    self._notifyNextChangeIfValueIsDifferent = function() {\n                        if (self.isDifferent(previousValue, self.peek(true /*evaluate*/))) {\n                            notifyNextChange = true;\n                        }\n                    };\n                },\n\n                hasSubscriptionsForEvent: function(event) {\n                    return this._subscriptions[event] && this._subscriptions[event].length;\n                },\n\n                getSubscriptionsCount: function (event) {\n                    if (event) {\n                        return this._subscriptions[event] && this._subscriptions[event].length || 0;\n                    } else {\n                        var total = 0;\n                        ko.utils.objectForEach(this._subscriptions, function(eventName, subscriptions) {\n                            if (eventName !== 'dirty')\n                                total += subscriptions.length;\n                        });\n                        return total;\n                    }\n                },\n\n                isDifferent: function(oldValue, newValue) {\n                    return !this['equalityComparer'] || !this['equalityComparer'](oldValue, newValue);\n                },\n\n                toString: function() {\n                    return '[object Object]'\n                },\n\n                extend: applyExtenders\n            };\n\n            ko.exportProperty(ko_subscribable_fn, 'init', ko_subscribable_fn.init);\n            ko.exportProperty(ko_subscribable_fn, 'subscribe', ko_subscribable_fn.subscribe);\n            ko.exportProperty(ko_subscribable_fn, 'extend', ko_subscribable_fn.extend);\n            ko.exportProperty(ko_subscribable_fn, 'getSubscriptionsCount', ko_subscribable_fn.getSubscriptionsCount);\n\n// For browsers that support proto assignment, we overwrite the prototype of each\n// observable instance. Since observables are functions, we need Function.prototype\n// to still be in the prototype chain.\n            if (ko.utils.canSetPrototype) {\n                ko.utils.setPrototypeOf(ko_subscribable_fn, Function.prototype);\n            }\n\n            ko.subscribable['fn'] = ko_subscribable_fn;\n\n\n            ko.isSubscribable = function (instance) {\n                return instance != null && typeof instance.subscribe == \"function\" && typeof instance[\"notifySubscribers\"] == \"function\";\n            };\n\n            ko.exportSymbol('subscribable', ko.subscribable);\n            ko.exportSymbol('isSubscribable', ko.isSubscribable);\n\n            ko.computedContext = ko.dependencyDetection = (function () {\n                var outerFrames = [],\n                    currentFrame,\n                    lastId = 0;\n\n                // Return a unique ID that can be assigned to an observable for dependency tracking.\n                // Theoretically, you could eventually overflow the number storage size, resulting\n                // in duplicate IDs. But in JavaScript, the largest exact integral value is 2^53\n                // or 9,007,199,254,740,992. If you created 1,000,000 IDs per second, it would\n                // take over 285 years to reach that number.\n                // Reference http://blog.vjeux.com/2010/javascript/javascript-max_int-number-limits.html\n                function getId() {\n                    return ++lastId;\n                }\n\n                function begin(options) {\n                    outerFrames.push(currentFrame);\n                    currentFrame = options;\n                }\n\n                function end() {\n                    currentFrame = outerFrames.pop();\n                }\n\n                return {\n                    begin: begin,\n\n                    end: end,\n\n                    registerDependency: function (subscribable) {\n                        if (currentFrame) {\n                            if (!ko.isSubscribable(subscribable))\n                                throw new Error(\"Only subscribable things can act as dependencies\");\n                            currentFrame.callback.call(currentFrame.callbackTarget, subscribable, subscribable._id || (subscribable._id = getId()));\n                        }\n                    },\n\n                    ignore: function (callback, callbackTarget, callbackArgs) {\n                        try {\n                            begin();\n                            return callback.apply(callbackTarget, callbackArgs || []);\n                        } finally {\n                            end();\n                        }\n                    },\n\n                    getDependenciesCount: function () {\n                        if (currentFrame)\n                            return currentFrame.computed.getDependenciesCount();\n                    },\n\n                    getDependencies: function () {\n                        if (currentFrame)\n                            return currentFrame.computed.getDependencies();\n                    },\n\n                    isInitial: function() {\n                        if (currentFrame)\n                            return currentFrame.isInitial;\n                    },\n\n                    computed: function() {\n                        if (currentFrame)\n                            return currentFrame.computed;\n                    }\n                };\n            })();\n\n            ko.exportSymbol('computedContext', ko.computedContext);\n            ko.exportSymbol('computedContext.getDependenciesCount', ko.computedContext.getDependenciesCount);\n            ko.exportSymbol('computedContext.getDependencies', ko.computedContext.getDependencies);\n            ko.exportSymbol('computedContext.isInitial', ko.computedContext.isInitial);\n            ko.exportSymbol('computedContext.registerDependency', ko.computedContext.registerDependency);\n\n            ko.exportSymbol('ignoreDependencies', ko.ignoreDependencies = ko.dependencyDetection.ignore);\n            var observableLatestValue = ko.utils.createSymbolOrString('_latestValue');\n\n            ko.observable = function (initialValue) {\n                function observable() {\n                    if (arguments.length > 0) {\n                        // Write\n\n                        // Ignore writes if the value hasn't changed\n                        if (observable.isDifferent(observable[observableLatestValue], arguments[0])) {\n                            observable.valueWillMutate();\n                            observable[observableLatestValue] = arguments[0];\n                            observable.valueHasMutated();\n                        }\n                        return this; // Permits chained assignments\n                    }\n                    else {\n                        // Read\n                        ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a \"read\" operation\n                        return observable[observableLatestValue];\n                    }\n                }\n\n                observable[observableLatestValue] = initialValue;\n\n                // Inherit from 'subscribable'\n                if (!ko.utils.canSetPrototype) {\n                    // 'subscribable' won't be on the prototype chain unless we put it there directly\n                    ko.utils.extend(observable, ko.subscribable['fn']);\n                }\n                ko.subscribable['fn'].init(observable);\n\n                // Inherit from 'observable'\n                ko.utils.setPrototypeOfOrExtend(observable, observableFn);\n\n                if (ko.options['deferUpdates']) {\n                    ko.extenders['deferred'](observable, true);\n                }\n\n                return observable;\n            }\n\n// Define prototype for observables\n            var observableFn = {\n                'equalityComparer': valuesArePrimitiveAndEqual,\n                peek: function() { return this[observableLatestValue]; },\n                valueHasMutated: function () {\n                    this['notifySubscribers'](this[observableLatestValue], 'spectate');\n                    this['notifySubscribers'](this[observableLatestValue]);\n                },\n                valueWillMutate: function () { this['notifySubscribers'](this[observableLatestValue], 'beforeChange'); }\n            };\n\n// Note that for browsers that don't support proto assignment, the\n// inheritance chain is created manually in the ko.observable constructor\n            if (ko.utils.canSetPrototype) {\n                ko.utils.setPrototypeOf(observableFn, ko.subscribable['fn']);\n            }\n\n            var protoProperty = ko.observable.protoProperty = '__ko_proto__';\n            observableFn[protoProperty] = ko.observable;\n\n            ko.isObservable = function (instance) {\n                var proto = typeof instance == 'function' && instance[protoProperty];\n                if (proto && proto !== observableFn[protoProperty] && proto !== ko.computed['fn'][protoProperty]) {\n                    throw Error(\"Invalid object that looks like an observable; possibly from another Knockout instance\");\n                }\n                return !!proto;\n            };\n\n            ko.isWriteableObservable = function (instance) {\n                return (typeof instance == 'function' && (\n                    (instance[protoProperty] === observableFn[protoProperty]) ||  // Observable\n                    (instance[protoProperty] === ko.computed['fn'][protoProperty] && instance.hasWriteFunction)));   // Writable computed observable\n            };\n\n            ko.exportSymbol('observable', ko.observable);\n            ko.exportSymbol('isObservable', ko.isObservable);\n            ko.exportSymbol('isWriteableObservable', ko.isWriteableObservable);\n            ko.exportSymbol('isWritableObservable', ko.isWriteableObservable);\n            ko.exportSymbol('observable.fn', observableFn);\n            ko.exportProperty(observableFn, 'peek', observableFn.peek);\n            ko.exportProperty(observableFn, 'valueHasMutated', observableFn.valueHasMutated);\n            ko.exportProperty(observableFn, 'valueWillMutate', observableFn.valueWillMutate);\n            ko.observableArray = function (initialValues) {\n                initialValues = initialValues || [];\n\n                if (typeof initialValues != 'object' || !('length' in initialValues))\n                    throw new Error(\"The argument passed when initializing an observable array must be an array, or null, or undefined.\");\n\n                var result = ko.observable(initialValues);\n                ko.utils.setPrototypeOfOrExtend(result, ko.observableArray['fn']);\n                return result.extend({'trackArrayChanges':true});\n            };\n\n            ko.observableArray['fn'] = {\n                'remove': function (valueOrPredicate) {\n                    var underlyingArray = this.peek();\n                    var removedValues = [];\n                    var predicate = typeof valueOrPredicate == \"function\" && !ko.isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; };\n                    for (var i = 0; i < underlyingArray.length; i++) {\n                        var value = underlyingArray[i];\n                        if (predicate(value)) {\n                            if (removedValues.length === 0) {\n                                this.valueWillMutate();\n                            }\n                            if (underlyingArray[i] !== value) {\n                                throw Error(\"Array modified during remove; cannot remove item\");\n                            }\n                            removedValues.push(value);\n                            underlyingArray.splice(i, 1);\n                            i--;\n                        }\n                    }\n                    if (removedValues.length) {\n                        this.valueHasMutated();\n                    }\n                    return removedValues;\n                },\n\n                'removeAll': function (arrayOfValues) {\n                    // If you passed zero args, we remove everything\n                    if (arrayOfValues === undefined) {\n                        var underlyingArray = this.peek();\n                        var allValues = underlyingArray.slice(0);\n                        this.valueWillMutate();\n                        underlyingArray.splice(0, underlyingArray.length);\n                        this.valueHasMutated();\n                        return allValues;\n                    }\n                    // If you passed an arg, we interpret it as an array of entries to remove\n                    if (!arrayOfValues)\n                        return [];\n                    return this['remove'](function (value) {\n                        return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;\n                    });\n                },\n\n                'destroy': function (valueOrPredicate) {\n                    var underlyingArray = this.peek();\n                    var predicate = typeof valueOrPredicate == \"function\" && !ko.isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; };\n                    this.valueWillMutate();\n                    for (var i = underlyingArray.length - 1; i >= 0; i--) {\n                        var value = underlyingArray[i];\n                        if (predicate(value))\n                            value[\"_destroy\"] = true;\n                    }\n                    this.valueHasMutated();\n                },\n\n                'destroyAll': function (arrayOfValues) {\n                    // If you passed zero args, we destroy everything\n                    if (arrayOfValues === undefined)\n                        return this['destroy'](function() { return true });\n\n                    // If you passed an arg, we interpret it as an array of entries to destroy\n                    if (!arrayOfValues)\n                        return [];\n                    return this['destroy'](function (value) {\n                        return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;\n                    });\n                },\n\n                'indexOf': function (item) {\n                    var underlyingArray = this();\n                    return ko.utils.arrayIndexOf(underlyingArray, item);\n                },\n\n                'replace': function(oldItem, newItem) {\n                    var index = this['indexOf'](oldItem);\n                    if (index >= 0) {\n                        this.valueWillMutate();\n                        this.peek()[index] = newItem;\n                        this.valueHasMutated();\n                    }\n                },\n\n                'sorted': function (compareFunction) {\n                    var arrayCopy = this().slice(0);\n                    return compareFunction ? arrayCopy.sort(compareFunction) : arrayCopy.sort();\n                },\n\n                'reversed': function () {\n                    return this().slice(0).reverse();\n                }\n            };\n\n// Note that for browsers that don't support proto assignment, the\n// inheritance chain is created manually in the ko.observableArray constructor\n            if (ko.utils.canSetPrototype) {\n                ko.utils.setPrototypeOf(ko.observableArray['fn'], ko.observable['fn']);\n            }\n\n// Populate ko.observableArray.fn with read/write functions from native arrays\n// Important: Do not add any additional functions here that may reasonably be used to *read* data from the array\n// because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale\n            ko.utils.arrayForEach([\"pop\", \"push\", \"reverse\", \"shift\", \"sort\", \"splice\", \"unshift\"], function (methodName) {\n                ko.observableArray['fn'][methodName] = function () {\n                    // Use \"peek\" to avoid creating a subscription in any computed that we're executing in the context of\n                    // (for consistency with mutating regular observables)\n                    var underlyingArray = this.peek();\n                    this.valueWillMutate();\n                    this.cacheDiffForKnownOperation(underlyingArray, methodName, arguments);\n                    var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);\n                    this.valueHasMutated();\n                    // The native sort and reverse methods return a reference to the array, but it makes more sense to return the observable array instead.\n                    return methodCallResult === underlyingArray ? this : methodCallResult;\n                };\n            });\n\n// Populate ko.observableArray.fn with read-only functions from native arrays\n            ko.utils.arrayForEach([\"slice\"], function (methodName) {\n                ko.observableArray['fn'][methodName] = function () {\n                    var underlyingArray = this();\n                    return underlyingArray[methodName].apply(underlyingArray, arguments);\n                };\n            });\n\n            ko.isObservableArray = function (instance) {\n                return ko.isObservable(instance)\n                    && typeof instance[\"remove\"] == \"function\"\n                    && typeof instance[\"push\"] == \"function\";\n            };\n\n            ko.exportSymbol('observableArray', ko.observableArray);\n            ko.exportSymbol('isObservableArray', ko.isObservableArray);\n            var arrayChangeEventName = 'arrayChange';\n            ko.extenders['trackArrayChanges'] = function(target, options) {\n                // Use the provided options--each call to trackArrayChanges overwrites the previously set options\n                target.compareArrayOptions = {};\n                if (options && typeof options == \"object\") {\n                    ko.utils.extend(target.compareArrayOptions, options);\n                }\n                target.compareArrayOptions['sparse'] = true;\n\n                // Only modify the target observable once\n                if (target.cacheDiffForKnownOperation) {\n                    return;\n                }\n                var trackingChanges = false,\n                    cachedDiff = null,\n                    changeSubscription,\n                    spectateSubscription,\n                    pendingChanges = 0,\n                    previousContents,\n                    underlyingBeforeSubscriptionAddFunction = target.beforeSubscriptionAdd,\n                    underlyingAfterSubscriptionRemoveFunction = target.afterSubscriptionRemove;\n\n                // Watch \"subscribe\" calls, and for array change events, ensure change tracking is enabled\n                target.beforeSubscriptionAdd = function (event) {\n                    if (underlyingBeforeSubscriptionAddFunction) {\n                        underlyingBeforeSubscriptionAddFunction.call(target, event);\n                    }\n                    if (event === arrayChangeEventName) {\n                        trackChanges();\n                    }\n                };\n                // Watch \"dispose\" calls, and for array change events, ensure change tracking is disabled when all are disposed\n                target.afterSubscriptionRemove = function (event) {\n                    if (underlyingAfterSubscriptionRemoveFunction) {\n                        underlyingAfterSubscriptionRemoveFunction.call(target, event);\n                    }\n                    if (event === arrayChangeEventName && !target.hasSubscriptionsForEvent(arrayChangeEventName)) {\n                        if (changeSubscription) {\n                            changeSubscription.dispose();\n                        }\n                        if (spectateSubscription) {\n                            spectateSubscription.dispose();\n                        }\n                        spectateSubscription = changeSubscription = null;\n                        trackingChanges = false;\n                        previousContents = undefined;\n                    }\n                };\n\n                function trackChanges() {\n                    if (trackingChanges) {\n                        // Whenever there's a new subscription and there are pending notifications, make sure all previous\n                        // subscriptions are notified of the change so that all subscriptions are in sync.\n                        notifyChanges();\n                        return;\n                    }\n\n                    trackingChanges = true;\n\n                    // Track how many times the array actually changed value\n                    spectateSubscription = target.subscribe(function () {\n                        ++pendingChanges;\n                    }, null, \"spectate\");\n\n                    // Each time the array changes value, capture a clone so that on the next\n                    // change it's possible to produce a diff\n                    previousContents = [].concat(target.peek() || []);\n                    cachedDiff = null;\n                    changeSubscription = target.subscribe(notifyChanges);\n\n                    function notifyChanges() {\n                        if (pendingChanges) {\n                            // Make a copy of the current contents and ensure it's an array\n                            var currentContents = [].concat(target.peek() || []), changes;\n\n                            // Compute the diff and issue notifications, but only if someone is listening\n                            if (target.hasSubscriptionsForEvent(arrayChangeEventName)) {\n                                changes = getChanges(previousContents, currentContents);\n                            }\n\n                            // Eliminate references to the old, removed items, so they can be GCed\n                            previousContents = currentContents;\n                            cachedDiff = null;\n                            pendingChanges = 0;\n\n                            if (changes && changes.length) {\n                                target['notifySubscribers'](changes, arrayChangeEventName);\n                            }\n                        }\n                    }\n                }\n\n                function getChanges(previousContents, currentContents) {\n                    // We try to re-use cached diffs.\n                    // The scenarios where pendingChanges > 1 are when using rate limiting or deferred updates,\n                    // which without this check would not be compatible with arrayChange notifications. Normally,\n                    // notifications are issued immediately so we wouldn't be queueing up more than one.\n                    if (!cachedDiff || pendingChanges > 1) {\n                        cachedDiff = ko.utils.compareArrays(previousContents, currentContents, target.compareArrayOptions);\n                    }\n\n                    return cachedDiff;\n                }\n\n                target.cacheDiffForKnownOperation = function(rawArray, operationName, args) {\n                    // Only run if we're currently tracking changes for this observable array\n                    // and there aren't any pending deferred notifications.\n                    if (!trackingChanges || pendingChanges) {\n                        return;\n                    }\n                    var diff = [],\n                        arrayLength = rawArray.length,\n                        argsLength = args.length,\n                        offset = 0;\n\n                    function pushDiff(status, value, index) {\n                        return diff[diff.length] = { 'status': status, 'value': value, 'index': index };\n                    }\n                    switch (operationName) {\n                        case 'push':\n                            offset = arrayLength;\n                        case 'unshift':\n                            for (var index = 0; index < argsLength; index++) {\n                                pushDiff('added', args[index], offset + index);\n                            }\n                            break;\n\n                        case 'pop':\n                            offset = arrayLength - 1;\n                        case 'shift':\n                            if (arrayLength) {\n                                pushDiff('deleted', rawArray[offset], offset);\n                            }\n                            break;\n\n                        case 'splice':\n                            // Negative start index means 'from end of array'. After that we clamp to [0...arrayLength].\n                            // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice\n                            var startIndex = Math.min(Math.max(0, args[0] < 0 ? arrayLength + args[0] : args[0]), arrayLength),\n                                endDeleteIndex = argsLength === 1 ? arrayLength : Math.min(startIndex + (args[1] || 0), arrayLength),\n                                endAddIndex = startIndex + argsLength - 2,\n                                endIndex = Math.max(endDeleteIndex, endAddIndex),\n                                additions = [], deletions = [];\n                            for (var index = startIndex, argsIndex = 2; index < endIndex; ++index, ++argsIndex) {\n                                if (index < endDeleteIndex)\n                                    deletions.push(pushDiff('deleted', rawArray[index], index));\n                                if (index < endAddIndex)\n                                    additions.push(pushDiff('added', args[argsIndex], index));\n                            }\n                            ko.utils.findMovesInArrayComparison(deletions, additions);\n                            break;\n\n                        default:\n                            return;\n                    }\n                    cachedDiff = diff;\n                };\n            };\n            var computedState = ko.utils.createSymbolOrString('_state');\n\n            ko.computed = ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {\n                if (typeof evaluatorFunctionOrOptions === \"object\") {\n                    // Single-parameter syntax - everything is on this \"options\" param\n                    options = evaluatorFunctionOrOptions;\n                } else {\n                    // Multi-parameter syntax - construct the options according to the params passed\n                    options = options || {};\n                    if (evaluatorFunctionOrOptions) {\n                        options[\"read\"] = evaluatorFunctionOrOptions;\n                    }\n                }\n                if (typeof options[\"read\"] != \"function\")\n                    throw Error(\"Pass a function that returns the value of the ko.computed\");\n\n                var writeFunction = options[\"write\"];\n                var state = {\n                    latestValue: undefined,\n                    isStale: true,\n                    isDirty: true,\n                    isBeingEvaluated: false,\n                    suppressDisposalUntilDisposeWhenReturnsFalse: false,\n                    isDisposed: false,\n                    pure: false,\n                    isSleeping: false,\n                    readFunction: options[\"read\"],\n                    evaluatorFunctionTarget: evaluatorFunctionTarget || options[\"owner\"],\n                    disposeWhenNodeIsRemoved: options[\"disposeWhenNodeIsRemoved\"] || options.disposeWhenNodeIsRemoved || null,\n                    disposeWhen: options[\"disposeWhen\"] || options.disposeWhen,\n                    domNodeDisposalCallback: null,\n                    dependencyTracking: {},\n                    dependenciesCount: 0,\n                    evaluationTimeoutInstance: null\n                };\n\n                function computedObservable() {\n                    if (arguments.length > 0) {\n                        if (typeof writeFunction === \"function\") {\n                            // Writing a value\n                            writeFunction.apply(state.evaluatorFunctionTarget, arguments);\n                        } else {\n                            throw new Error(\"Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.\");\n                        }\n                        return this; // Permits chained assignments\n                    } else {\n                        // Reading the value\n                        if (!state.isDisposed) {\n                            ko.dependencyDetection.registerDependency(computedObservable);\n                        }\n                        if (state.isDirty || (state.isSleeping && computedObservable.haveDependenciesChanged())) {\n                            computedObservable.evaluateImmediate();\n                        }\n                        return state.latestValue;\n                    }\n                }\n\n                computedObservable[computedState] = state;\n                computedObservable.hasWriteFunction = typeof writeFunction === \"function\";\n\n                // Inherit from 'subscribable'\n                if (!ko.utils.canSetPrototype) {\n                    // 'subscribable' won't be on the prototype chain unless we put it there directly\n                    ko.utils.extend(computedObservable, ko.subscribable['fn']);\n                }\n                ko.subscribable['fn'].init(computedObservable);\n\n                // Inherit from 'computed'\n                ko.utils.setPrototypeOfOrExtend(computedObservable, computedFn);\n\n                if (options['pure']) {\n                    state.pure = true;\n                    state.isSleeping = true;     // Starts off sleeping; will awake on the first subscription\n                    ko.utils.extend(computedObservable, pureComputedOverrides);\n                } else if (options['deferEvaluation']) {\n                    ko.utils.extend(computedObservable, deferEvaluationOverrides);\n                }\n\n                if (ko.options['deferUpdates']) {\n                    ko.extenders['deferred'](computedObservable, true);\n                }\n\n                if (DEBUG) {\n                    // #1731 - Aid debugging by exposing the computed's options\n                    computedObservable[\"_options\"] = options;\n                }\n\n                if (state.disposeWhenNodeIsRemoved) {\n                    // Since this computed is associated with a DOM node, and we don't want to dispose the computed\n                    // until the DOM node is *removed* from the document (as opposed to never having been in the document),\n                    // we'll prevent disposal until \"disposeWhen\" first returns false.\n                    state.suppressDisposalUntilDisposeWhenReturnsFalse = true;\n\n                    // disposeWhenNodeIsRemoved: true can be used to opt into the \"only dispose after first false result\"\n                    // behaviour even if there's no specific node to watch. In that case, clear the option so we don't try\n                    // to watch for a non-node's disposal. This technique is intended for KO's internal use only and shouldn't\n                    // be documented or used by application code, as it's likely to change in a future version of KO.\n                    if (!state.disposeWhenNodeIsRemoved.nodeType) {\n                        state.disposeWhenNodeIsRemoved = null;\n                    }\n                }\n\n                // Evaluate, unless sleeping or deferEvaluation is true\n                if (!state.isSleeping && !options['deferEvaluation']) {\n                    computedObservable.evaluateImmediate();\n                }\n\n                // Attach a DOM node disposal callback so that the computed will be proactively disposed as soon as the node is\n                // removed using ko.removeNode. But skip if isActive is false (there will never be any dependencies to dispose).\n                if (state.disposeWhenNodeIsRemoved && computedObservable.isActive()) {\n                    ko.utils.domNodeDisposal.addDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback = function () {\n                        computedObservable.dispose();\n                    });\n                }\n\n                return computedObservable;\n            };\n\n// Utility function that disposes a given dependencyTracking entry\n            function computedDisposeDependencyCallback(id, entryToDispose) {\n                if (entryToDispose !== null && entryToDispose.dispose) {\n                    entryToDispose.dispose();\n                }\n            }\n\n// This function gets called each time a dependency is detected while evaluating a computed.\n// It's factored out as a shared function to avoid creating unnecessary function instances during evaluation.\n            function computedBeginDependencyDetectionCallback(subscribable, id) {\n                var computedObservable = this.computedObservable,\n                    state = computedObservable[computedState];\n                if (!state.isDisposed) {\n                    if (this.disposalCount && this.disposalCandidates[id]) {\n                        // Don't want to dispose this subscription, as it's still being used\n                        computedObservable.addDependencyTracking(id, subscribable, this.disposalCandidates[id]);\n                        this.disposalCandidates[id] = null; // No need to actually delete the property - disposalCandidates is a transient object anyway\n                        --this.disposalCount;\n                    } else if (!state.dependencyTracking[id]) {\n                        // Brand new subscription - add it\n                        computedObservable.addDependencyTracking(id, subscribable, state.isSleeping ? { _target: subscribable } : computedObservable.subscribeToDependency(subscribable));\n                    }\n                    // If the observable we've accessed has a pending notification, ensure we get notified of the actual final value (bypass equality checks)\n                    if (subscribable._notificationIsPending) {\n                        subscribable._notifyNextChangeIfValueIsDifferent();\n                    }\n                }\n            }\n\n            var computedFn = {\n                \"equalityComparer\": valuesArePrimitiveAndEqual,\n                getDependenciesCount: function () {\n                    return this[computedState].dependenciesCount;\n                },\n                getDependencies: function () {\n                    var dependencyTracking = this[computedState].dependencyTracking, dependentObservables = [];\n\n                    ko.utils.objectForEach(dependencyTracking, function (id, dependency) {\n                        dependentObservables[dependency._order] = dependency._target;\n                    });\n\n                    return dependentObservables;\n                },\n                hasAncestorDependency: function (obs) {\n                    if (!this[computedState].dependenciesCount) {\n                        return false;\n                    }\n                    var dependencies = this.getDependencies();\n                    if (ko.utils.arrayIndexOf(dependencies, obs) !== -1) {\n                        return true;\n                    }\n                    return !!ko.utils.arrayFirst(dependencies, function (dep) {\n                        return dep.hasAncestorDependency && dep.hasAncestorDependency(obs);\n                    });\n                },\n                addDependencyTracking: function (id, target, trackingObj) {\n                    if (this[computedState].pure && target === this) {\n                        throw Error(\"A 'pure' computed must not be called recursively\");\n                    }\n\n                    this[computedState].dependencyTracking[id] = trackingObj;\n                    trackingObj._order = this[computedState].dependenciesCount++;\n                    trackingObj._version = target.getVersion();\n                },\n                haveDependenciesChanged: function () {\n                    var id, dependency, dependencyTracking = this[computedState].dependencyTracking;\n                    for (id in dependencyTracking) {\n                        if (Object.prototype.hasOwnProperty.call(dependencyTracking, id)) {\n                            dependency = dependencyTracking[id];\n                            if ((this._evalDelayed && dependency._target._notificationIsPending) || dependency._target.hasChanged(dependency._version)) {\n                                return true;\n                            }\n                        }\n                    }\n                },\n                markDirty: function () {\n                    // Process \"dirty\" events if we can handle delayed notifications\n                    if (this._evalDelayed && !this[computedState].isBeingEvaluated) {\n                        this._evalDelayed(false /*isChange*/);\n                    }\n                },\n                isActive: function () {\n                    var state = this[computedState];\n                    return state.isDirty || state.dependenciesCount > 0;\n                },\n                respondToChange: function () {\n                    // Ignore \"change\" events if we've already scheduled a delayed notification\n                    if (!this._notificationIsPending) {\n                        this.evaluatePossiblyAsync();\n                    } else if (this[computedState].isDirty) {\n                        this[computedState].isStale = true;\n                    }\n                },\n                subscribeToDependency: function (target) {\n                    if (target._deferUpdates) {\n                        var dirtySub = target.subscribe(this.markDirty, this, 'dirty'),\n                            changeSub = target.subscribe(this.respondToChange, this);\n                        return {\n                            _target: target,\n                            dispose: function () {\n                                dirtySub.dispose();\n                                changeSub.dispose();\n                            }\n                        };\n                    } else {\n                        return target.subscribe(this.evaluatePossiblyAsync, this);\n                    }\n                },\n                evaluatePossiblyAsync: function () {\n                    var computedObservable = this,\n                        throttleEvaluationTimeout = computedObservable['throttleEvaluation'];\n                    if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {\n                        clearTimeout(this[computedState].evaluationTimeoutInstance);\n                        this[computedState].evaluationTimeoutInstance = ko.utils.setTimeout(function () {\n                            computedObservable.evaluateImmediate(true /*notifyChange*/);\n                        }, throttleEvaluationTimeout);\n                    } else if (computedObservable._evalDelayed) {\n                        computedObservable._evalDelayed(true /*isChange*/);\n                    } else {\n                        computedObservable.evaluateImmediate(true /*notifyChange*/);\n                    }\n                },\n                evaluateImmediate: function (notifyChange) {\n                    var computedObservable = this,\n                        state = computedObservable[computedState],\n                        disposeWhen = state.disposeWhen,\n                        changed = false;\n\n                    if (state.isBeingEvaluated) {\n                        // If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation.\n                        // This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost\n                        // certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing\n                        // their own re-evaluation. Further discussion at https://github.com/SteveSanderson/knockout/pull/387\n                        return;\n                    }\n\n                    // Do not evaluate (and possibly capture new dependencies) if disposed\n                    if (state.isDisposed) {\n                        return;\n                    }\n\n                    if (state.disposeWhenNodeIsRemoved && !ko.utils.domNodeIsAttachedToDocument(state.disposeWhenNodeIsRemoved) || disposeWhen && disposeWhen()) {\n                        // See comment above about suppressDisposalUntilDisposeWhenReturnsFalse\n                        if (!state.suppressDisposalUntilDisposeWhenReturnsFalse) {\n                            computedObservable.dispose();\n                            return;\n                        }\n                    } else {\n                        // It just did return false, so we can stop suppressing now\n                        state.suppressDisposalUntilDisposeWhenReturnsFalse = false;\n                    }\n\n                    state.isBeingEvaluated = true;\n                    try {\n                        changed = this.evaluateImmediate_CallReadWithDependencyDetection(notifyChange);\n                    } finally {\n                        state.isBeingEvaluated = false;\n                    }\n\n                    return changed;\n                },\n                evaluateImmediate_CallReadWithDependencyDetection: function (notifyChange) {\n                    // This function is really just part of the evaluateImmediate logic. You would never call it from anywhere else.\n                    // Factoring it out into a separate function means it can be independent of the try/catch block in evaluateImmediate,\n                    // which contributes to saving about 40% off the CPU overhead of computed evaluation (on V8 at least).\n\n                    var computedObservable = this,\n                        state = computedObservable[computedState],\n                        changed = false;\n\n                    // Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).\n                    // Then, during evaluation, we cross off any that are in fact still being used.\n                    var isInitial = state.pure ? undefined : !state.dependenciesCount,   // If we're evaluating when there are no previous dependencies, it must be the first time\n                        dependencyDetectionContext = {\n                            computedObservable: computedObservable,\n                            disposalCandidates: state.dependencyTracking,\n                            disposalCount: state.dependenciesCount\n                        };\n\n                    ko.dependencyDetection.begin({\n                        callbackTarget: dependencyDetectionContext,\n                        callback: computedBeginDependencyDetectionCallback,\n                        computed: computedObservable,\n                        isInitial: isInitial\n                    });\n\n                    state.dependencyTracking = {};\n                    state.dependenciesCount = 0;\n\n                    var newValue = this.evaluateImmediate_CallReadThenEndDependencyDetection(state, dependencyDetectionContext);\n\n                    if (!state.dependenciesCount) {\n                        computedObservable.dispose();\n                        changed = true; // When evaluation causes a disposal, make sure all dependent computeds get notified so they'll see the new state\n                    } else {\n                        changed = computedObservable.isDifferent(state.latestValue, newValue);\n                    }\n\n                    if (changed) {\n                        if (!state.isSleeping) {\n                            computedObservable[\"notifySubscribers\"](state.latestValue, \"beforeChange\");\n                        } else {\n                            computedObservable.updateVersion();\n                        }\n\n                        state.latestValue = newValue;\n                        if (DEBUG) computedObservable._latestValue = newValue;\n\n                        computedObservable[\"notifySubscribers\"](state.latestValue, \"spectate\");\n\n                        if (!state.isSleeping && notifyChange) {\n                            computedObservable[\"notifySubscribers\"](state.latestValue);\n                        }\n                        if (computedObservable._recordUpdate) {\n                            computedObservable._recordUpdate();\n                        }\n                    }\n\n                    if (isInitial) {\n                        computedObservable[\"notifySubscribers\"](state.latestValue, \"awake\");\n                    }\n\n                    return changed;\n                },\n                evaluateImmediate_CallReadThenEndDependencyDetection: function (state, dependencyDetectionContext) {\n                    // This function is really part of the evaluateImmediate_CallReadWithDependencyDetection logic.\n                    // You'd never call it from anywhere else. Factoring it out means that evaluateImmediate_CallReadWithDependencyDetection\n                    // can be independent of try/finally blocks, which contributes to saving about 40% off the CPU\n                    // overhead of computed evaluation (on V8 at least).\n\n                    try {\n                        var readFunction = state.readFunction;\n                        return state.evaluatorFunctionTarget ? readFunction.call(state.evaluatorFunctionTarget) : readFunction();\n                    } finally {\n                        ko.dependencyDetection.end();\n\n                        // For each subscription no longer being used, remove it from the active subscriptions list and dispose it\n                        if (dependencyDetectionContext.disposalCount && !state.isSleeping) {\n                            ko.utils.objectForEach(dependencyDetectionContext.disposalCandidates, computedDisposeDependencyCallback);\n                        }\n\n                        state.isStale = state.isDirty = false;\n                    }\n                },\n                peek: function (evaluate) {\n                    // By default, peek won't re-evaluate, except while the computed is sleeping or to get the initial value when \"deferEvaluation\" is set.\n                    // Pass in true to evaluate if needed.\n                    var state = this[computedState];\n                    if ((state.isDirty && (evaluate || !state.dependenciesCount)) || (state.isSleeping && this.haveDependenciesChanged())) {\n                        this.evaluateImmediate();\n                    }\n                    return state.latestValue;\n                },\n                limit: function (limitFunction) {\n                    // Override the limit function with one that delays evaluation as well\n                    ko.subscribable['fn'].limit.call(this, limitFunction);\n                    this._evalIfChanged = function () {\n                        if (!this[computedState].isSleeping) {\n                            if (this[computedState].isStale) {\n                                this.evaluateImmediate();\n                            } else {\n                                this[computedState].isDirty = false;\n                            }\n                        }\n                        return this[computedState].latestValue;\n                    };\n                    this._evalDelayed = function (isChange) {\n                        this._limitBeforeChange(this[computedState].latestValue);\n\n                        // Mark as dirty\n                        this[computedState].isDirty = true;\n                        if (isChange) {\n                            this[computedState].isStale = true;\n                        }\n\n                        // Pass the observable to the \"limit\" code, which will evaluate it when\n                        // it's time to do the notification.\n                        this._limitChange(this, !isChange /* isDirty */);\n                    };\n                },\n                dispose: function () {\n                    var state = this[computedState];\n                    if (!state.isSleeping && state.dependencyTracking) {\n                        ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {\n                            if (dependency.dispose)\n                                dependency.dispose();\n                        });\n                    }\n                    if (state.disposeWhenNodeIsRemoved && state.domNodeDisposalCallback) {\n                        ko.utils.domNodeDisposal.removeDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback);\n                    }\n                    state.dependencyTracking = undefined;\n                    state.dependenciesCount = 0;\n                    state.isDisposed = true;\n                    state.isStale = false;\n                    state.isDirty = false;\n                    state.isSleeping = false;\n                    state.disposeWhenNodeIsRemoved = undefined;\n                    state.disposeWhen = undefined;\n                    state.readFunction = undefined;\n                    if (!this.hasWriteFunction) {\n                        state.evaluatorFunctionTarget = undefined;\n                    }\n                }\n            };\n\n            var pureComputedOverrides = {\n                beforeSubscriptionAdd: function (event) {\n                    // If asleep, wake up the computed by subscribing to any dependencies.\n                    var computedObservable = this,\n                        state = computedObservable[computedState];\n                    if (!state.isDisposed && state.isSleeping && event == 'change') {\n                        state.isSleeping = false;\n                        if (state.isStale || computedObservable.haveDependenciesChanged()) {\n                            state.dependencyTracking = null;\n                            state.dependenciesCount = 0;\n                            if (computedObservable.evaluateImmediate()) {\n                                computedObservable.updateVersion();\n                            }\n                        } else {\n                            // First put the dependencies in order\n                            var dependenciesOrder = [];\n                            ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {\n                                dependenciesOrder[dependency._order] = id;\n                            });\n                            // Next, subscribe to each one\n                            ko.utils.arrayForEach(dependenciesOrder, function (id, order) {\n                                var dependency = state.dependencyTracking[id],\n                                    subscription = computedObservable.subscribeToDependency(dependency._target);\n                                subscription._order = order;\n                                subscription._version = dependency._version;\n                                state.dependencyTracking[id] = subscription;\n                            });\n                            // Waking dependencies may have triggered effects\n                            if (computedObservable.haveDependenciesChanged()) {\n                                if (computedObservable.evaluateImmediate()) {\n                                    computedObservable.updateVersion();\n                                }\n                            }\n                        }\n\n                        if (!state.isDisposed) {     // test since evaluating could trigger disposal\n                            computedObservable[\"notifySubscribers\"](state.latestValue, \"awake\");\n                        }\n                    }\n                },\n                afterSubscriptionRemove: function (event) {\n                    var state = this[computedState];\n                    if (!state.isDisposed && event == 'change' && !this.hasSubscriptionsForEvent('change')) {\n                        ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {\n                            if (dependency.dispose) {\n                                state.dependencyTracking[id] = {\n                                    _target: dependency._target,\n                                    _order: dependency._order,\n                                    _version: dependency._version\n                                };\n                                dependency.dispose();\n                            }\n                        });\n                        state.isSleeping = true;\n                        this[\"notifySubscribers\"](undefined, \"asleep\");\n                    }\n                },\n                getVersion: function () {\n                    // Because a pure computed is not automatically updated while it is sleeping, we can't\n                    // simply return the version number. Instead, we check if any of the dependencies have\n                    // changed and conditionally re-evaluate the computed observable.\n                    var state = this[computedState];\n                    if (state.isSleeping && (state.isStale || this.haveDependenciesChanged())) {\n                        this.evaluateImmediate();\n                    }\n                    return ko.subscribable['fn'].getVersion.call(this);\n                }\n            };\n\n            var deferEvaluationOverrides = {\n                beforeSubscriptionAdd: function (event) {\n                    // This will force a computed with deferEvaluation to evaluate when the first subscription is registered.\n                    if (event == 'change' || event == 'beforeChange') {\n                        this.peek();\n                    }\n                }\n            };\n\n// Note that for browsers that don't support proto assignment, the\n// inheritance chain is created manually in the ko.computed constructor\n            if (ko.utils.canSetPrototype) {\n                ko.utils.setPrototypeOf(computedFn, ko.subscribable['fn']);\n            }\n\n// Set the proto values for ko.computed\n            var protoProp = ko.observable.protoProperty; // == \"__ko_proto__\"\n            computedFn[protoProp] = ko.computed;\n\n            ko.isComputed = function (instance) {\n                return (typeof instance == 'function' && instance[protoProp] === computedFn[protoProp]);\n            };\n\n            ko.isPureComputed = function (instance) {\n                return ko.isComputed(instance) && instance[computedState] && instance[computedState].pure;\n            };\n\n            ko.exportSymbol('computed', ko.computed);\n            ko.exportSymbol('dependentObservable', ko.computed);    // export ko.dependentObservable for backwards compatibility (1.x)\n            ko.exportSymbol('isComputed', ko.isComputed);\n            ko.exportSymbol('isPureComputed', ko.isPureComputed);\n            ko.exportSymbol('computed.fn', computedFn);\n            ko.exportProperty(computedFn, 'peek', computedFn.peek);\n            ko.exportProperty(computedFn, 'dispose', computedFn.dispose);\n            ko.exportProperty(computedFn, 'isActive', computedFn.isActive);\n            ko.exportProperty(computedFn, 'getDependenciesCount', computedFn.getDependenciesCount);\n            ko.exportProperty(computedFn, 'getDependencies', computedFn.getDependencies);\n\n            ko.pureComputed = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget) {\n                if (typeof evaluatorFunctionOrOptions === 'function') {\n                    return ko.computed(evaluatorFunctionOrOptions, evaluatorFunctionTarget, {'pure':true});\n                } else {\n                    evaluatorFunctionOrOptions = ko.utils.extend({}, evaluatorFunctionOrOptions);   // make a copy of the parameter object\n                    evaluatorFunctionOrOptions['pure'] = true;\n                    return ko.computed(evaluatorFunctionOrOptions, evaluatorFunctionTarget);\n                }\n            }\n            ko.exportSymbol('pureComputed', ko.pureComputed);\n\n            (function() {\n                var maxNestedObservableDepth = 10; // Escape the (unlikely) pathological case where an observable's current value is itself (or similar reference cycle)\n\n                ko.toJS = function(rootObject) {\n                    if (arguments.length == 0)\n                        throw new Error(\"When calling ko.toJS, pass the object you want to convert.\");\n\n                    // We just unwrap everything at every level in the object graph\n                    return mapJsObjectGraph(rootObject, function(valueToMap) {\n                        // Loop because an observable's value might in turn be another observable wrapper\n                        for (var i = 0; ko.isObservable(valueToMap) && (i < maxNestedObservableDepth); i++)\n                            valueToMap = valueToMap();\n                        return valueToMap;\n                    });\n                };\n\n                ko.toJSON = function(rootObject, replacer, space) {     // replacer and space are optional\n                    var plainJavaScriptObject = ko.toJS(rootObject);\n                    return ko.utils.stringifyJson(plainJavaScriptObject, replacer, space);\n                };\n\n                function mapJsObjectGraph(rootObject, mapInputCallback, visitedObjects) {\n                    visitedObjects = visitedObjects || new objectLookup();\n\n                    rootObject = mapInputCallback(rootObject);\n                    var canHaveProperties = (typeof rootObject == \"object\") && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof RegExp)) && (!(rootObject instanceof Date)) && (!(rootObject instanceof String)) && (!(rootObject instanceof Number)) && (!(rootObject instanceof Boolean));\n                    if (!canHaveProperties)\n                        return rootObject;\n\n                    var outputProperties = rootObject instanceof Array ? [] : {};\n                    visitedObjects.save(rootObject, outputProperties);\n\n                    visitPropertiesOrArrayEntries(rootObject, function(indexer) {\n                        var propertyValue = mapInputCallback(rootObject[indexer]);\n\n                        switch (typeof propertyValue) {\n                            case \"boolean\":\n                            case \"number\":\n                            case \"string\":\n                            case \"function\":\n                                outputProperties[indexer] = propertyValue;\n                                break;\n                            case \"object\":\n                            case \"undefined\":\n                                var previouslyMappedValue = visitedObjects.get(propertyValue);\n                                outputProperties[indexer] = (previouslyMappedValue !== undefined)\n                                    ? previouslyMappedValue\n                                    : mapJsObjectGraph(propertyValue, mapInputCallback, visitedObjects);\n                                break;\n                        }\n                    });\n\n                    return outputProperties;\n                }\n\n                function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {\n                    if (rootObject instanceof Array) {\n                        for (var i = 0; i < rootObject.length; i++)\n                            visitorCallback(i);\n\n                        // For arrays, also respect toJSON property for custom mappings (fixes #278)\n                        if (typeof rootObject['toJSON'] == 'function')\n                            visitorCallback('toJSON');\n                    } else {\n                        for (var propertyName in rootObject) {\n                            visitorCallback(propertyName);\n                        }\n                    }\n                };\n\n                function objectLookup() {\n                    this.keys = [];\n                    this.values = [];\n                };\n\n                objectLookup.prototype = {\n                    constructor: objectLookup,\n                    save: function(key, value) {\n                        var existingIndex = ko.utils.arrayIndexOf(this.keys, key);\n                        if (existingIndex >= 0)\n                            this.values[existingIndex] = value;\n                        else {\n                            this.keys.push(key);\n                            this.values.push(value);\n                        }\n                    },\n                    get: function(key) {\n                        var existingIndex = ko.utils.arrayIndexOf(this.keys, key);\n                        return (existingIndex >= 0) ? this.values[existingIndex] : undefined;\n                    }\n                };\n            })();\n\n            ko.exportSymbol('toJS', ko.toJS);\n            ko.exportSymbol('toJSON', ko.toJSON);\n            ko.when = function(predicate, callback, context) {\n                function kowhen (resolve) {\n                    var observable = ko.pureComputed(predicate, context).extend({notify:'always'});\n                    var subscription = observable.subscribe(function(value) {\n                        if (value) {\n                            subscription.dispose();\n                            resolve(value);\n                        }\n                    });\n                    // In case the initial value is true, process it right away\n                    observable['notifySubscribers'](observable.peek());\n\n                    return subscription;\n                }\n                if (typeof Promise === \"function\" && !callback) {\n                    return new Promise(kowhen);\n                } else {\n                    return kowhen(callback.bind(context));\n                }\n            };\n\n            ko.exportSymbol('when', ko.when);\n            (function () {\n                var hasDomDataExpandoProperty = '__ko__hasDomDataOptionValue__';\n\n                // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values\n                // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values\n                // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.\n                ko.selectExtensions = {\n                    readValue : function(element) {\n                        switch (ko.utils.tagNameLower(element)) {\n                            case 'option':\n                                if (element[hasDomDataExpandoProperty] === true)\n                                    return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);\n                                return ko.utils.ieVersion <= 7\n                                    ? (element.getAttributeNode('value') && element.getAttributeNode('value').specified ? element.value : element.text)\n                                    : element.value;\n                            case 'select':\n                                return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;\n                            default:\n                                return element.value;\n                        }\n                    },\n\n                    writeValue: function(element, value, allowUnset) {\n                        switch (ko.utils.tagNameLower(element)) {\n                            case 'option':\n                                if (typeof value === \"string\") {\n                                    ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, undefined);\n                                    if (hasDomDataExpandoProperty in element) { // IE <= 8 throws errors if you delete non-existent properties from a DOM node\n                                        delete element[hasDomDataExpandoProperty];\n                                    }\n                                    element.value = value;\n                                }\n                                else {\n                                    // Store arbitrary object using DomData\n                                    ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, value);\n                                    element[hasDomDataExpandoProperty] = true;\n\n                                    // Special treatment of numbers is just for backward compatibility. KO 1.2.1 wrote numerical values to element.value.\n                                    element.value = typeof value === \"number\" ? value : \"\";\n                                }\n                                break;\n                            case 'select':\n                                if (value === \"\" || value === null)       // A blank string or null value will select the caption\n                                    value = undefined;\n                                var selection = -1;\n                                for (var i = 0, n = element.options.length, optionValue; i < n; ++i) {\n                                    optionValue = ko.selectExtensions.readValue(element.options[i]);\n                                    // Include special check to handle selecting a caption with a blank string value\n                                    if (optionValue == value || (optionValue === \"\" && value === undefined)) {\n                                        selection = i;\n                                        break;\n                                    }\n                                }\n                                if (allowUnset || selection >= 0 || (value === undefined && element.size > 1)) {\n                                    element.selectedIndex = selection;\n                                    if (ko.utils.ieVersion === 6) {\n                                        // Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread\n                                        // right after you've changed the set of OPTION nodes on it. So for that node type, we'll schedule a second thread\n                                        // to apply the value as well.\n                                        ko.utils.setTimeout(function () {\n                                            element.selectedIndex = selection;\n                                        }, 0);\n                                    }\n                                }\n                                break;\n                            default:\n                                if ((value === null) || (value === undefined))\n                                    value = \"\";\n                                element.value = value;\n                                break;\n                        }\n                    }\n                };\n            })();\n\n            ko.exportSymbol('selectExtensions', ko.selectExtensions);\n            ko.exportSymbol('selectExtensions.readValue', ko.selectExtensions.readValue);\n            ko.exportSymbol('selectExtensions.writeValue', ko.selectExtensions.writeValue);\n            ko.expressionRewriting = (function () {\n                var javaScriptReservedWords = [\"true\", \"false\", \"null\", \"undefined\"];\n\n                // Matches something that can be assigned to--either an isolated identifier or something ending with a property accessor\n                // This is designed to be simple and avoid false negatives, but could produce false positives (e.g., a+b.c).\n                // This also will not properly handle nested brackets (e.g., obj1[obj2['prop']]; see #911).\n                var javaScriptAssignmentTarget = /^(?:[$_a-z][$\\w]*|(.+)(\\.\\s*[$_a-z][$\\w]*|\\[.+\\]))$/i;\n\n                function getWriteableValue(expression) {\n                    if (ko.utils.arrayIndexOf(javaScriptReservedWords, expression) >= 0)\n                        return false;\n                    var match = expression.match(javaScriptAssignmentTarget);\n                    return match === null ? false : match[1] ? ('Object(' + match[1] + ')' + match[2]) : expression;\n                }\n\n                // The following regular expressions will be used to split an object-literal string into tokens\n\n                var specials = ',\"\\'`{}()/:[\\\\]',    // These characters have special meaning to the parser and must not appear in the middle of a token, except as part of a string.\n                    // Create the actual regular expression by or-ing the following regex strings. The order is important.\n                    bindingToken = RegExp([\n                        // These match strings, either with double quotes, single quotes, or backticks\n                        '\"(?:\\\\\\\\.|[^\"])*\"',\n                        \"'(?:\\\\\\\\.|[^'])*'\",\n                        \"`(?:\\\\\\\\.|[^`])*`\",\n                        // Match C style comments\n                        \"/\\\\*(?:[^*]|\\\\*+[^*/])*\\\\*+/\",\n                        // Match C++ style comments\n                        \"//.*\\n\",\n                        // Match a regular expression (text enclosed by slashes), but will also match sets of divisions\n                        // as a regular expression (this is handled by the parsing loop below).\n                        '/(?:\\\\\\\\.|[^/])+/\\w*',\n                        // Match text (at least two characters) that does not contain any of the above special characters,\n                        // although some of the special characters are allowed to start it (all but the colon and comma).\n                        // The text can contain spaces, but leading or trailing spaces are skipped.\n                        '[^\\\\s:,/][^' + specials + ']*[^\\\\s' + specials + ']',\n                        // Match any non-space character not matched already. This will match colons and commas, since they're\n                        // not matched by \"everyThingElse\", but will also match any other single character that wasn't already\n                        // matched (for example: in \"a: 1, b: 2\", each of the non-space characters will be matched by oneNotSpace).\n                        '[^\\\\s]'\n                    ].join('|'), 'g'),\n\n                    // Match end of previous token to determine whether a slash is a division or regex.\n                    divisionLookBehind = /[\\])\"'A-Za-z0-9_$]+$/,\n                    keywordRegexLookBehind = {'in':1,'return':1,'typeof':1};\n\n                function parseObjectLiteral(objectLiteralString) {\n                    // Trim leading and trailing spaces from the string\n                    var str = ko.utils.stringTrim(objectLiteralString);\n\n                    // Trim braces '{' surrounding the whole object literal\n                    if (str.charCodeAt(0) === 123) str = str.slice(1, -1);\n\n                    // Add a newline to correctly match a C++ style comment at the end of the string and\n                    // add a comma so that we don't need a separate code block to deal with the last item\n                    str += \"\\n,\";\n\n                    // Split into tokens\n                    var result = [], toks = str.match(bindingToken), key, values = [], depth = 0;\n\n                    if (toks.length > 1) {\n                        for (var i = 0, tok; tok = toks[i]; ++i) {\n                            var c = tok.charCodeAt(0);\n                            // A comma signals the end of a key/value pair if depth is zero\n                            if (c === 44) { // \",\"\n                                if (depth <= 0) {\n                                    result.push((key && values.length) ? {key: key, value: values.join('')} : {'unknown': key || values.join('')});\n                                    key = depth = 0;\n                                    values = [];\n                                    continue;\n                                }\n                                // Simply skip the colon that separates the name and value\n                            } else if (c === 58) { // \":\"\n                                if (!depth && !key && values.length === 1) {\n                                    key = values.pop();\n                                    continue;\n                                }\n                                // Comments: skip them\n                            } else if (c === 47 && tok.length > 1 && (tok.charCodeAt(1) === 47 || tok.charCodeAt(1) === 42)) {  // \"//\" or \"/*\"\n                                continue;\n                                // A set of slashes is initially matched as a regular expression, but could be division\n                            } else if (c === 47 && i && tok.length > 1) {  // \"/\"\n                                // Look at the end of the previous token to determine if the slash is actually division\n                                var match = toks[i-1].match(divisionLookBehind);\n                                if (match && !keywordRegexLookBehind[match[0]]) {\n                                    // The slash is actually a division punctuator; re-parse the remainder of the string (not including the slash)\n                                    str = str.substr(str.indexOf(tok) + 1);\n                                    toks = str.match(bindingToken);\n                                    i = -1;\n                                    // Continue with just the slash\n                                    tok = '/';\n                                }\n                                // Increment depth for parentheses, braces, and brackets so that interior commas are ignored\n                            } else if (c === 40 || c === 123 || c === 91) { // '(', '{', '['\n                                ++depth;\n                            } else if (c === 41 || c === 125 || c === 93) { // ')', '}', ']'\n                                --depth;\n                                // The key will be the first token; if it's a string, trim the quotes\n                            } else if (!key && !values.length && (c === 34 || c === 39)) { // '\"', \"'\"\n                                tok = tok.slice(1, -1);\n                            }\n                            values.push(tok);\n                        }\n                        if (depth > 0) {\n                            throw Error(\"Unbalanced parentheses, braces, or brackets\");\n                        }\n                    }\n                    return result;\n                }\n\n                // Two-way bindings include a write function that allow the handler to update the value even if it's not an observable.\n                var twoWayBindings = {};\n\n                function preProcessBindings(bindingsStringOrKeyValueArray, bindingOptions) {\n                    bindingOptions = bindingOptions || {};\n\n                    function processKeyValue(key, val) {\n                        var writableVal;\n                        function callPreprocessHook(obj) {\n                            return (obj && obj['preprocess']) ? (val = obj['preprocess'](val, key, processKeyValue)) : true;\n                        }\n                        if (!bindingParams) {\n                            if (!callPreprocessHook(ko['getBindingHandler'](key)))\n                                return;\n\n                            if (twoWayBindings[key] && (writableVal = getWriteableValue(val))) {\n                                // For two-way bindings, provide a write method in case the value\n                                // isn't a writable observable.\n                                var writeKey = typeof twoWayBindings[key] == 'string' ? twoWayBindings[key] : key;\n                                propertyAccessorResultStrings.push(\"'\" + writeKey + \"':function(_z){\" + writableVal + \"=_z}\");\n                            }\n                        }\n                        // Values are wrapped in a function so that each value can be accessed independently\n                        if (makeValueAccessors) {\n                            val = 'function(){return ' + val + ' }';\n                        }\n                        resultStrings.push(\"'\" + key + \"':\" + val);\n                    }\n\n                    var resultStrings = [],\n                        propertyAccessorResultStrings = [],\n                        makeValueAccessors = bindingOptions['valueAccessors'],\n                        bindingParams = bindingOptions['bindingParams'],\n                        keyValueArray = typeof bindingsStringOrKeyValueArray === \"string\" ?\n                            parseObjectLiteral(bindingsStringOrKeyValueArray) : bindingsStringOrKeyValueArray;\n\n                    ko.utils.arrayForEach(keyValueArray, function(keyValue) {\n                        processKeyValue(keyValue.key || keyValue['unknown'], keyValue.value);\n                    });\n\n                    if (propertyAccessorResultStrings.length)\n                        processKeyValue('_ko_property_writers', \"{\" + propertyAccessorResultStrings.join(\",\") + \" }\");\n\n                    return resultStrings.join(\",\");\n                }\n\n                return {\n                    bindingRewriteValidators: [],\n\n                    twoWayBindings: twoWayBindings,\n\n                    parseObjectLiteral: parseObjectLiteral,\n\n                    preProcessBindings: preProcessBindings,\n\n                    keyValueArrayContainsKey: function(keyValueArray, key) {\n                        for (var i = 0; i < keyValueArray.length; i++)\n                            if (keyValueArray[i]['key'] == key)\n                                return true;\n                        return false;\n                    },\n\n                    // Internal, private KO utility for updating model properties from within bindings\n                    // property:            If the property being updated is (or might be) an observable, pass it here\n                    //                      If it turns out to be a writable observable, it will be written to directly\n                    // allBindings:         An object with a get method to retrieve bindings in the current execution context.\n                    //                      This will be searched for a '_ko_property_writers' property in case you're writing to a non-observable\n                    // key:                 The key identifying the property to be written. Example: for { hasFocus: myValue }, write to 'myValue' by specifying the key 'hasFocus'\n                    // value:               The value to be written\n                    // checkIfDifferent:    If true, and if the property being written is a writable observable, the value will only be written if\n                    //                      it is !== existing value on that writable observable\n                    writeValueToProperty: function(property, allBindings, key, value, checkIfDifferent) {\n                        if (!property || !ko.isObservable(property)) {\n                            var propWriters = allBindings.get('_ko_property_writers');\n                            if (propWriters && propWriters[key])\n                                propWriters[key](value);\n                        } else if (ko.isWriteableObservable(property) && (!checkIfDifferent || property.peek() !== value)) {\n                            property(value);\n                        }\n                    }\n                };\n            })();\n\n            ko.exportSymbol('expressionRewriting', ko.expressionRewriting);\n            ko.exportSymbol('expressionRewriting.bindingRewriteValidators', ko.expressionRewriting.bindingRewriteValidators);\n            ko.exportSymbol('expressionRewriting.parseObjectLiteral', ko.expressionRewriting.parseObjectLiteral);\n            ko.exportSymbol('expressionRewriting.preProcessBindings', ko.expressionRewriting.preProcessBindings);\n\n// Making bindings explicitly declare themselves as \"two way\" isn't ideal in the long term (it would be better if\n// all bindings could use an official 'property writer' API without needing to declare that they might). However,\n// since this is not, and has never been, a public API (_ko_property_writers was never documented), it's acceptable\n// as an internal implementation detail in the short term.\n// For those developers who rely on _ko_property_writers in their custom bindings, we expose _twoWayBindings as an\n// undocumented feature that makes it relatively easy to upgrade to KO 3.0. However, this is still not an official\n// public API, and we reserve the right to remove it at any time if we create a real public property writers API.\n            ko.exportSymbol('expressionRewriting._twoWayBindings', ko.expressionRewriting.twoWayBindings);\n\n// For backward compatibility, define the following aliases. (Previously, these function names were misleading because\n// they referred to JSON specifically, even though they actually work with arbitrary JavaScript object literal expressions.)\n            ko.exportSymbol('jsonExpressionRewriting', ko.expressionRewriting);\n            ko.exportSymbol('jsonExpressionRewriting.insertPropertyAccessorsIntoJson', ko.expressionRewriting.preProcessBindings);\n            (function() {\n                // \"Virtual elements\" is an abstraction on top of the usual DOM API which understands the notion that comment nodes\n                // may be used to represent hierarchy (in addition to the DOM's natural hierarchy).\n                // If you call the DOM-manipulating functions on ko.virtualElements, you will be able to read and write the state\n                // of that virtual hierarchy\n                //\n                // The point of all this is to support containerless templates (e.g., <!-- ko foreach:someCollection -->blah<!-- /ko -->)\n                // without having to scatter special cases all over the binding and templating code.\n\n                // IE 9 cannot reliably read the \"nodeValue\" property of a comment node (see https://github.com/SteveSanderson/knockout/issues/186)\n                // but it does give them a nonstandard alternative property called \"text\" that it can read reliably. Other browsers don't have that property.\n                // So, use node.text where available, and node.nodeValue elsewhere\n                var commentNodesHaveTextProperty = document && document.createComment(\"test\").text === \"<!--test-->\";\n\n                var startCommentRegex = commentNodesHaveTextProperty ? /^<!--\\s*ko(?:\\s+([\\s\\S]+))?\\s*-->$/ : /^\\s*ko(?:\\s+([\\s\\S]+))?\\s*$/;\n                var endCommentRegex =   commentNodesHaveTextProperty ? /^<!--\\s*\\/ko\\s*-->$/ : /^\\s*\\/ko\\s*$/;\n                var htmlTagsWithOptionallyClosingChildren = { 'ul': true, 'ol': true };\n\n                function isStartComment(node) {\n                    return (node.nodeType == 8) && startCommentRegex.test(commentNodesHaveTextProperty ? node.text : node.nodeValue);\n                }\n\n                function isEndComment(node) {\n                    return (node.nodeType == 8) && endCommentRegex.test(commentNodesHaveTextProperty ? node.text : node.nodeValue);\n                }\n\n                function isUnmatchedEndComment(node) {\n                    return isEndComment(node) && !(ko.utils.domData.get(node, matchedEndCommentDataKey));\n                }\n\n                var matchedEndCommentDataKey = \"__ko_matchedEndComment__\"\n\n                function getVirtualChildren(startComment, allowUnbalanced) {\n                    var currentNode = startComment;\n                    var depth = 1;\n                    var children = [];\n                    while (currentNode = currentNode.nextSibling) {\n                        if (isEndComment(currentNode)) {\n                            ko.utils.domData.set(currentNode, matchedEndCommentDataKey, true);\n                            depth--;\n                            if (depth === 0)\n                                return children;\n                        }\n\n                        children.push(currentNode);\n\n                        if (isStartComment(currentNode))\n                            depth++;\n                    }\n                    if (!allowUnbalanced)\n                        throw new Error(\"Cannot find closing comment tag to match: \" + startComment.nodeValue);\n                    return null;\n                }\n\n                function getMatchingEndComment(startComment, allowUnbalanced) {\n                    var allVirtualChildren = getVirtualChildren(startComment, allowUnbalanced);\n                    if (allVirtualChildren) {\n                        if (allVirtualChildren.length > 0)\n                            return allVirtualChildren[allVirtualChildren.length - 1].nextSibling;\n                        return startComment.nextSibling;\n                    } else\n                        return null; // Must have no matching end comment, and allowUnbalanced is true\n                }\n\n                function getUnbalancedChildTags(node) {\n                    // e.g., from <div>OK</div><!-- ko blah --><span>Another</span>, returns: <!-- ko blah --><span>Another</span>\n                    //       from <div>OK</div><!-- /ko --><!-- /ko -->,             returns: <!-- /ko --><!-- /ko -->\n                    var childNode = node.firstChild, captureRemaining = null;\n                    if (childNode) {\n                        do {\n                            if (captureRemaining)                   // We already hit an unbalanced node and are now just scooping up all subsequent nodes\n                                captureRemaining.push(childNode);\n                            else if (isStartComment(childNode)) {\n                                var matchingEndComment = getMatchingEndComment(childNode, /* allowUnbalanced: */ true);\n                                if (matchingEndComment)             // It's a balanced tag, so skip immediately to the end of this virtual set\n                                    childNode = matchingEndComment;\n                                else\n                                    captureRemaining = [childNode]; // It's unbalanced, so start capturing from this point\n                            } else if (isEndComment(childNode)) {\n                                captureRemaining = [childNode];     // It's unbalanced (if it wasn't, we'd have skipped over it already), so start capturing\n                            }\n                        } while (childNode = childNode.nextSibling);\n                    }\n                    return captureRemaining;\n                }\n\n                ko.virtualElements = {\n                    allowedBindings: {},\n\n                    childNodes: function(node) {\n                        return isStartComment(node) ? getVirtualChildren(node) : node.childNodes;\n                    },\n\n                    emptyNode: function(node) {\n                        if (!isStartComment(node))\n                            ko.utils.emptyDomNode(node);\n                        else {\n                            var virtualChildren = ko.virtualElements.childNodes(node);\n                            for (var i = 0, j = virtualChildren.length; i < j; i++)\n                                ko.removeNode(virtualChildren[i]);\n                        }\n                    },\n\n                    setDomNodeChildren: function(node, childNodes) {\n                        if (!isStartComment(node))\n                            ko.utils.setDomNodeChildren(node, childNodes);\n                        else {\n                            ko.virtualElements.emptyNode(node);\n                            var endCommentNode = node.nextSibling; // Must be the next sibling, as we just emptied the children\n                            for (var i = 0, j = childNodes.length; i < j; i++)\n                                endCommentNode.parentNode.insertBefore(childNodes[i], endCommentNode);\n                        }\n                    },\n\n                    prepend: function(containerNode, nodeToPrepend) {\n                        var insertBeforeNode;\n\n                        if (isStartComment(containerNode)) {\n                            // Start comments must always have a parent and at least one following sibling (the end comment)\n                            insertBeforeNode = containerNode.nextSibling;\n                            containerNode = containerNode.parentNode;\n                        } else {\n                            insertBeforeNode = containerNode.firstChild;\n                        }\n\n                        if (!insertBeforeNode) {\n                            containerNode.appendChild(nodeToPrepend);\n                        } else if (nodeToPrepend !== insertBeforeNode) {       // IE will sometimes crash if you try to insert a node before itself\n                            containerNode.insertBefore(nodeToPrepend, insertBeforeNode);\n                        }\n                    },\n\n                    insertAfter: function(containerNode, nodeToInsert, insertAfterNode) {\n                        if (!insertAfterNode) {\n                            ko.virtualElements.prepend(containerNode, nodeToInsert);\n                        } else {\n                            // Children of start comments must always have a parent and at least one following sibling (the end comment)\n                            var insertBeforeNode = insertAfterNode.nextSibling;\n\n                            if (isStartComment(containerNode)) {\n                                containerNode = containerNode.parentNode;\n                            }\n\n                            if (!insertBeforeNode) {\n                                containerNode.appendChild(nodeToInsert);\n                            } else if (nodeToInsert !== insertBeforeNode) {       // IE will sometimes crash if you try to insert a node before itself\n                                containerNode.insertBefore(nodeToInsert, insertBeforeNode);\n                            }\n                        }\n                    },\n\n                    firstChild: function(node) {\n                        if (!isStartComment(node)) {\n                            if (node.firstChild && isEndComment(node.firstChild)) {\n                                throw new Error(\"Found invalid end comment, as the first child of \" + node);\n                            }\n                            return node.firstChild;\n                        } else if (!node.nextSibling || isEndComment(node.nextSibling)) {\n                            return null;\n                        } else {\n                            return node.nextSibling;\n                        }\n                    },\n\n                    nextSibling: function(node) {\n                        if (isStartComment(node)) {\n                            node = getMatchingEndComment(node);\n                        }\n\n                        if (node.nextSibling && isEndComment(node.nextSibling)) {\n                            if (isUnmatchedEndComment(node.nextSibling)) {\n                                throw Error(\"Found end comment without a matching opening comment, as child of \" + node);\n                            } else {\n                                return null;\n                            }\n                        } else {\n                            return node.nextSibling;\n                        }\n                    },\n\n                    hasBindingValue: isStartComment,\n\n                    virtualNodeBindingValue: function(node) {\n                        var regexMatch = (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(startCommentRegex);\n                        return regexMatch ? regexMatch[1] : null;\n                    },\n\n                    normaliseVirtualElementDomStructure: function(elementVerified) {\n                        // Workaround for https://github.com/SteveSanderson/knockout/issues/155\n                        // (IE <= 8 or IE 9 quirks mode parses your HTML weirdly, treating closing </li> tags as if they don't exist, thereby moving comment nodes\n                        // that are direct descendants of <ul> into the preceding <li>)\n                        if (!htmlTagsWithOptionallyClosingChildren[ko.utils.tagNameLower(elementVerified)])\n                            return;\n\n                        // Scan immediate children to see if they contain unbalanced comment tags. If they do, those comment tags\n                        // must be intended to appear *after* that child, so move them there.\n                        var childNode = elementVerified.firstChild;\n                        if (childNode) {\n                            do {\n                                if (childNode.nodeType === 1) {\n                                    var unbalancedTags = getUnbalancedChildTags(childNode);\n                                    if (unbalancedTags) {\n                                        // Fix up the DOM by moving the unbalanced tags to where they most likely were intended to be placed - *after* the child\n                                        var nodeToInsertBefore = childNode.nextSibling;\n                                        for (var i = 0; i < unbalancedTags.length; i++) {\n                                            if (nodeToInsertBefore)\n                                                elementVerified.insertBefore(unbalancedTags[i], nodeToInsertBefore);\n                                            else\n                                                elementVerified.appendChild(unbalancedTags[i]);\n                                        }\n                                    }\n                                }\n                            } while (childNode = childNode.nextSibling);\n                        }\n                    }\n                };\n            })();\n            ko.exportSymbol('virtualElements', ko.virtualElements);\n            ko.exportSymbol('virtualElements.allowedBindings', ko.virtualElements.allowedBindings);\n            ko.exportSymbol('virtualElements.emptyNode', ko.virtualElements.emptyNode);\n//ko.exportSymbol('virtualElements.firstChild', ko.virtualElements.firstChild);     // firstChild is not minified\n            ko.exportSymbol('virtualElements.insertAfter', ko.virtualElements.insertAfter);\n//ko.exportSymbol('virtualElements.nextSibling', ko.virtualElements.nextSibling);   // nextSibling is not minified\n            ko.exportSymbol('virtualElements.prepend', ko.virtualElements.prepend);\n            ko.exportSymbol('virtualElements.setDomNodeChildren', ko.virtualElements.setDomNodeChildren);\n            (function() {\n                var defaultBindingAttributeName = \"data-bind\";\n\n                ko.bindingProvider = function() {\n                    this.bindingCache = {};\n                };\n\n                ko.utils.extend(ko.bindingProvider.prototype, {\n                    'nodeHasBindings': function(node) {\n                        switch (node.nodeType) {\n                            case 1: // Element\n                                return node.getAttribute(defaultBindingAttributeName) != null\n                                    || ko.components['getComponentNameForNode'](node);\n                            case 8: // Comment node\n                                return ko.virtualElements.hasBindingValue(node);\n                            default: return false;\n                        }\n                    },\n\n                    'getBindings': function(node, bindingContext) {\n                        var bindingsString = this['getBindingsString'](node, bindingContext),\n                            parsedBindings = bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node) : null;\n                        return ko.components.addBindingsForCustomElement(parsedBindings, node, bindingContext, /* valueAccessors */ false);\n                    },\n\n                    'getBindingAccessors': function(node, bindingContext) {\n                        var bindingsString = this['getBindingsString'](node, bindingContext),\n                            parsedBindings = bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node, { 'valueAccessors': true }) : null;\n                        return ko.components.addBindingsForCustomElement(parsedBindings, node, bindingContext, /* valueAccessors */ true);\n                    },\n\n                    // The following function is only used internally by this default provider.\n                    // It's not part of the interface definition for a general binding provider.\n                    'getBindingsString': function(node, bindingContext) {\n                        switch (node.nodeType) {\n                            case 1: return node.getAttribute(defaultBindingAttributeName);   // Element\n                            case 8: return ko.virtualElements.virtualNodeBindingValue(node); // Comment node\n                            default: return null;\n                        }\n                    },\n\n                    // The following function is only used internally by this default provider.\n                    // It's not part of the interface definition for a general binding provider.\n                    'parseBindingsString': function(bindingsString, bindingContext, node, options) {\n                        try {\n                            var bindingFunction = createBindingsStringEvaluatorViaCache(bindingsString, this.bindingCache, options);\n                            return bindingFunction(bindingContext, node);\n                        } catch (ex) {\n                            ex.message = \"Unable to parse bindings.\\nBindings value: \" + bindingsString + \"\\nMessage: \" + ex.message;\n                            throw ex;\n                        }\n                    }\n                });\n\n                ko.bindingProvider['instance'] = new ko.bindingProvider();\n\n                function createBindingsStringEvaluatorViaCache(bindingsString, cache, options) {\n                    var cacheKey = bindingsString + (options && options['valueAccessors'] || '');\n                    return cache[cacheKey]\n                        || (cache[cacheKey] = createBindingsStringEvaluator(bindingsString, options));\n                }\n\n                function createBindingsStringEvaluator(bindingsString, options) {\n                    // Build the source for a function that evaluates \"expression\"\n                    // For each scope variable, add an extra level of \"with\" nesting\n                    // Example result: with(sc1) { with(sc0) { return (expression) } }\n                    var rewrittenBindings = ko.expressionRewriting.preProcessBindings(bindingsString, options),\n                        functionBody = \"with($context){with($data||{}){return{\" + rewrittenBindings + \"}}}\";\n                    return new Function(\"$context\", \"$element\", functionBody);\n                }\n            })();\n\n            ko.exportSymbol('bindingProvider', ko.bindingProvider);\n            (function () {\n                // Hide or don't minify context properties, see https://github.com/knockout/knockout/issues/2294\n                var contextSubscribable = ko.utils.createSymbolOrString('_subscribable');\n                var contextAncestorBindingInfo = ko.utils.createSymbolOrString('_ancestorBindingInfo');\n                var contextDataDependency = ko.utils.createSymbolOrString('_dataDependency');\n\n                ko.bindingHandlers = {};\n\n                // The following element types will not be recursed into during binding.\n                var bindingDoesNotRecurseIntoElementTypes = {\n                    // Don't want bindings that operate on text nodes to mutate <script> and <textarea> contents,\n                    // because it's unexpected and a potential XSS issue.\n                    // Also bindings should not operate on <template> elements since this breaks in Internet Explorer\n                    // and because such elements' contents are always intended to be bound in a different context\n                    // from where they appear in the document.\n                    'script': true,\n                    'textarea': true,\n                    'template': true\n                };\n\n                // Use an overridable method for retrieving binding handlers so that plugins may support dynamically created handlers\n                ko['getBindingHandler'] = function(bindingKey) {\n                    return ko.bindingHandlers[bindingKey];\n                };\n\n                var inheritParentVm = {};\n\n                // The ko.bindingContext constructor is only called directly to create the root context. For child\n                // contexts, use bindingContext.createChildContext or bindingContext.extend.\n                ko.bindingContext = function(dataItemOrAccessor, parentContext, dataItemAlias, extendCallback, options) {\n\n                    // The binding context object includes static properties for the current, parent, and root view models.\n                    // If a view model is actually stored in an observable, the corresponding binding context object, and\n                    // any child contexts, must be updated when the view model is changed.\n                    function updateContext() {\n                        // Most of the time, the context will directly get a view model object, but if a function is given,\n                        // we call the function to retrieve the view model. If the function accesses any observables or returns\n                        // an observable, the dependency is tracked, and those observables can later cause the binding\n                        // context to be updated.\n                        var dataItemOrObservable = isFunc ? realDataItemOrAccessor() : realDataItemOrAccessor,\n                            dataItem = ko.utils.unwrapObservable(dataItemOrObservable);\n\n                        if (parentContext) {\n                            // Copy $root and any custom properties from the parent context\n                            ko.utils.extend(self, parentContext);\n\n                            // Copy Symbol properties\n                            if (contextAncestorBindingInfo in parentContext) {\n                                self[contextAncestorBindingInfo] = parentContext[contextAncestorBindingInfo];\n                            }\n                        } else {\n                            self['$parents'] = [];\n                            self['$root'] = dataItem;\n\n                            // Export 'ko' in the binding context so it will be available in bindings and templates\n                            // even if 'ko' isn't exported as a global, such as when using an AMD loader.\n                            // See https://github.com/SteveSanderson/knockout/issues/490\n                            self['ko'] = ko;\n                        }\n\n                        self[contextSubscribable] = subscribable;\n\n                        if (shouldInheritData) {\n                            dataItem = self['$data'];\n                        } else {\n                            self['$rawData'] = dataItemOrObservable;\n                            self['$data'] = dataItem;\n                        }\n\n                        if (dataItemAlias)\n                            self[dataItemAlias] = dataItem;\n\n                        // The extendCallback function is provided when creating a child context or extending a context.\n                        // It handles the specific actions needed to finish setting up the binding context. Actions in this\n                        // function could also add dependencies to this binding context.\n                        if (extendCallback)\n                            extendCallback(self, parentContext, dataItem);\n\n                        // When a \"parent\" context is given and we don't already have a dependency on its context, register a dependency on it.\n                        // Thus whenever the parent context is updated, this context will also be updated.\n                        if (parentContext && parentContext[contextSubscribable] && !ko.computedContext.computed().hasAncestorDependency(parentContext[contextSubscribable])) {\n                            parentContext[contextSubscribable]();\n                        }\n\n                        if (dataDependency) {\n                            self[contextDataDependency] = dataDependency;\n                        }\n\n                        return self['$data'];\n                    }\n\n                    var self = this,\n                        shouldInheritData = dataItemOrAccessor === inheritParentVm,\n                        realDataItemOrAccessor = shouldInheritData ? undefined : dataItemOrAccessor,\n                        isFunc = typeof(realDataItemOrAccessor) == \"function\" && !ko.isObservable(realDataItemOrAccessor),\n                        nodes,\n                        subscribable,\n                        dataDependency = options && options['dataDependency'];\n\n                    if (options && options['exportDependencies']) {\n                        // The \"exportDependencies\" option means that the calling code will track any dependencies and re-create\n                        // the binding context when they change.\n                        updateContext();\n                    } else {\n                        subscribable = ko.pureComputed(updateContext);\n                        subscribable.peek();\n\n                        // At this point, the binding context has been initialized, and the \"subscribable\" computed observable is\n                        // subscribed to any observables that were accessed in the process. If there is nothing to track, the\n                        // computed will be inactive, and we can safely throw it away. If it's active, the computed is stored in\n                        // the context object.\n                        if (subscribable.isActive()) {\n                            // Always notify because even if the model ($data) hasn't changed, other context properties might have changed\n                            subscribable['equalityComparer'] = null;\n                        } else {\n                            self[contextSubscribable] = undefined;\n                        }\n                    }\n                }\n\n                // Extend the binding context hierarchy with a new view model object. If the parent context is watching\n                // any observables, the new child context will automatically get a dependency on the parent context.\n                // But this does not mean that the $data value of the child context will also get updated. If the child\n                // view model also depends on the parent view model, you must provide a function that returns the correct\n                // view model on each update.\n                ko.bindingContext.prototype['createChildContext'] = function (dataItemOrAccessor, dataItemAlias, extendCallback, options) {\n                    if (!options && dataItemAlias && typeof dataItemAlias == \"object\") {\n                        options = dataItemAlias;\n                        dataItemAlias = options['as'];\n                        extendCallback = options['extend'];\n                    }\n\n                    if (dataItemAlias && options && options['noChildContext']) {\n                        var isFunc = typeof(dataItemOrAccessor) == \"function\" && !ko.isObservable(dataItemOrAccessor);\n                        return new ko.bindingContext(inheritParentVm, this, null, function (self) {\n                            if (extendCallback)\n                                extendCallback(self);\n                            self[dataItemAlias] = isFunc ? dataItemOrAccessor() : dataItemOrAccessor;\n                        }, options);\n                    }\n\n                    return new ko.bindingContext(dataItemOrAccessor, this, dataItemAlias, function (self, parentContext) {\n                        // Extend the context hierarchy by setting the appropriate pointers\n                        self['$parentContext'] = parentContext;\n                        self['$parent'] = parentContext['$data'];\n                        self['$parents'] = (parentContext['$parents'] || []).slice(0);\n                        self['$parents'].unshift(self['$parent']);\n                        if (extendCallback)\n                            extendCallback(self);\n                    }, options);\n                };\n\n                // Extend the binding context with new custom properties. This doesn't change the context hierarchy.\n                // Similarly to \"child\" contexts, provide a function here to make sure that the correct values are set\n                // when an observable view model is updated.\n                ko.bindingContext.prototype['extend'] = function(properties, options) {\n                    return new ko.bindingContext(inheritParentVm, this, null, function(self, parentContext) {\n                        ko.utils.extend(self, typeof(properties) == \"function\" ? properties(self) : properties);\n                    }, options);\n                };\n\n                var boundElementDomDataKey = ko.utils.domData.nextKey();\n\n                function asyncContextDispose(node) {\n                    var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey),\n                        asyncContext = bindingInfo && bindingInfo.asyncContext;\n                    if (asyncContext) {\n                        bindingInfo.asyncContext = null;\n                        asyncContext.notifyAncestor();\n                    }\n                }\n                function AsyncCompleteContext(node, bindingInfo, ancestorBindingInfo) {\n                    this.node = node;\n                    this.bindingInfo = bindingInfo;\n                    this.asyncDescendants = [];\n                    this.childrenComplete = false;\n\n                    if (!bindingInfo.asyncContext) {\n                        ko.utils.domNodeDisposal.addDisposeCallback(node, asyncContextDispose);\n                    }\n\n                    if (ancestorBindingInfo && ancestorBindingInfo.asyncContext) {\n                        ancestorBindingInfo.asyncContext.asyncDescendants.push(node);\n                        this.ancestorBindingInfo = ancestorBindingInfo;\n                    }\n                }\n                AsyncCompleteContext.prototype.notifyAncestor = function () {\n                    if (this.ancestorBindingInfo && this.ancestorBindingInfo.asyncContext) {\n                        this.ancestorBindingInfo.asyncContext.descendantComplete(this.node);\n                    }\n                };\n                AsyncCompleteContext.prototype.descendantComplete = function (node) {\n                    ko.utils.arrayRemoveItem(this.asyncDescendants, node);\n                    if (!this.asyncDescendants.length && this.childrenComplete) {\n                        this.completeChildren();\n                    }\n                };\n                AsyncCompleteContext.prototype.completeChildren = function () {\n                    this.childrenComplete = true;\n                    if (this.bindingInfo.asyncContext && !this.asyncDescendants.length) {\n                        this.bindingInfo.asyncContext = null;\n                        ko.utils.domNodeDisposal.removeDisposeCallback(this.node, asyncContextDispose);\n                        ko.bindingEvent.notify(this.node, ko.bindingEvent.descendantsComplete);\n                        this.notifyAncestor();\n                    }\n                };\n\n                ko.bindingEvent = {\n                    childrenComplete: \"childrenComplete\",\n                    descendantsComplete : \"descendantsComplete\",\n\n                    subscribe: function (node, event, callback, context, options) {\n                        var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {});\n                        if (!bindingInfo.eventSubscribable) {\n                            bindingInfo.eventSubscribable = new ko.subscribable;\n                        }\n                        if (options && options['notifyImmediately'] && bindingInfo.notifiedEvents[event]) {\n                            ko.dependencyDetection.ignore(callback, context, [node]);\n                        }\n                        return bindingInfo.eventSubscribable.subscribe(callback, context, event);\n                    },\n\n                    notify: function (node, event) {\n                        var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey);\n                        if (bindingInfo) {\n                            bindingInfo.notifiedEvents[event] = true;\n                            if (bindingInfo.eventSubscribable) {\n                                bindingInfo.eventSubscribable['notifySubscribers'](node, event);\n                            }\n                            if (event == ko.bindingEvent.childrenComplete) {\n                                if (bindingInfo.asyncContext) {\n                                    bindingInfo.asyncContext.completeChildren();\n                                } else if (bindingInfo.asyncContext === undefined && bindingInfo.eventSubscribable && bindingInfo.eventSubscribable.hasSubscriptionsForEvent(ko.bindingEvent.descendantsComplete)) {\n                                    // It's currently an error to register a descendantsComplete handler for a node that was never registered as completing asynchronously.\n                                    // That's because without the asyncContext, we don't have a way to know that all descendants have completed.\n                                    throw new Error(\"descendantsComplete event not supported for bindings on this node\");\n                                }\n                            }\n                        }\n                    },\n\n                    startPossiblyAsyncContentBinding: function (node, bindingContext) {\n                        var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {});\n\n                        if (!bindingInfo.asyncContext) {\n                            bindingInfo.asyncContext = new AsyncCompleteContext(node, bindingInfo, bindingContext[contextAncestorBindingInfo]);\n                        }\n\n                        // If the provided context was already extended with this node's binding info, just return the extended context\n                        if (bindingContext[contextAncestorBindingInfo] == bindingInfo) {\n                            return bindingContext;\n                        }\n\n                        return bindingContext['extend'](function (ctx) {\n                            ctx[contextAncestorBindingInfo] = bindingInfo;\n                        });\n                    }\n                };\n\n                // Returns the valueAccessor function for a binding value\n                function makeValueAccessor(value) {\n                    return function() {\n                        return value;\n                    };\n                }\n\n                // Returns the value of a valueAccessor function\n                function evaluateValueAccessor(valueAccessor) {\n                    return valueAccessor();\n                }\n\n                // Given a function that returns bindings, create and return a new object that contains\n                // binding value-accessors functions. Each accessor function calls the original function\n                // so that it always gets the latest value and all dependencies are captured. This is used\n                // by ko.applyBindingsToNode and getBindingsAndMakeAccessors.\n                function makeAccessorsFromFunction(callback) {\n                    return ko.utils.objectMap(ko.dependencyDetection.ignore(callback), function(value, key) {\n                        return function() {\n                            return callback()[key];\n                        };\n                    });\n                }\n\n                // Given a bindings function or object, create and return a new object that contains\n                // binding value-accessors functions. This is used by ko.applyBindingsToNode.\n                function makeBindingAccessors(bindings, context, node) {\n                    if (typeof bindings === 'function') {\n                        return makeAccessorsFromFunction(bindings.bind(null, context, node));\n                    } else {\n                        return ko.utils.objectMap(bindings, makeValueAccessor);\n                    }\n                }\n\n                // This function is used if the binding provider doesn't include a getBindingAccessors function.\n                // It must be called with 'this' set to the provider instance.\n                function getBindingsAndMakeAccessors(node, context) {\n                    return makeAccessorsFromFunction(this['getBindings'].bind(this, node, context));\n                }\n\n                function validateThatBindingIsAllowedForVirtualElements(bindingName) {\n                    var validator = ko.virtualElements.allowedBindings[bindingName];\n                    if (!validator)\n                        throw new Error(\"The binding '\" + bindingName + \"' cannot be used with virtual elements\")\n                }\n\n                function applyBindingsToDescendantsInternal(bindingContext, elementOrVirtualElement) {\n                    var nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement);\n\n                    if (nextInQueue) {\n                        var currentChild,\n                            provider = ko.bindingProvider['instance'],\n                            preprocessNode = provider['preprocessNode'];\n\n                        // Preprocessing allows a binding provider to mutate a node before bindings are applied to it. For example it's\n                        // possible to insert new siblings after it, and/or replace the node with a different one. This can be used to\n                        // implement custom binding syntaxes, such as {{ value }} for string interpolation, or custom element types that\n                        // trigger insertion of <template> contents at that point in the document.\n                        if (preprocessNode) {\n                            while (currentChild = nextInQueue) {\n                                nextInQueue = ko.virtualElements.nextSibling(currentChild);\n                                preprocessNode.call(provider, currentChild);\n                            }\n                            // Reset nextInQueue for the next loop\n                            nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement);\n                        }\n\n                        while (currentChild = nextInQueue) {\n                            // Keep a record of the next child *before* applying bindings, in case the binding removes the current child from its position\n                            nextInQueue = ko.virtualElements.nextSibling(currentChild);\n                            applyBindingsToNodeAndDescendantsInternal(bindingContext, currentChild);\n                        }\n                    }\n                    ko.bindingEvent.notify(elementOrVirtualElement, ko.bindingEvent.childrenComplete);\n                }\n\n                function applyBindingsToNodeAndDescendantsInternal(bindingContext, nodeVerified) {\n                    var bindingContextForDescendants = bindingContext;\n\n                    var isElement = (nodeVerified.nodeType === 1);\n                    if (isElement) // Workaround IE <= 8 HTML parsing weirdness\n                        ko.virtualElements.normaliseVirtualElementDomStructure(nodeVerified);\n\n                    // Perf optimisation: Apply bindings only if...\n                    // (1) We need to store the binding info for the node (all element nodes)\n                    // (2) It might have bindings (e.g., it has a data-bind attribute, or it's a marker for a containerless template)\n                    var shouldApplyBindings = isElement || ko.bindingProvider['instance']['nodeHasBindings'](nodeVerified);\n                    if (shouldApplyBindings)\n                        bindingContextForDescendants = applyBindingsToNodeInternal(nodeVerified, null, bindingContext)['bindingContextForDescendants'];\n\n                    if (bindingContextForDescendants && !bindingDoesNotRecurseIntoElementTypes[ko.utils.tagNameLower(nodeVerified)]) {\n                        applyBindingsToDescendantsInternal(bindingContextForDescendants, nodeVerified);\n                    }\n                }\n\n                function topologicalSortBindings(bindings) {\n                    // Depth-first sort\n                    var result = [],                // The list of key/handler pairs that we will return\n                        bindingsConsidered = {},    // A temporary record of which bindings are already in 'result'\n                        cyclicDependencyStack = []; // Keeps track of a depth-search so that, if there's a cycle, we know which bindings caused it\n                    ko.utils.objectForEach(bindings, function pushBinding(bindingKey) {\n                        if (!bindingsConsidered[bindingKey]) {\n                            var binding = ko['getBindingHandler'](bindingKey);\n                            if (binding) {\n                                // First add dependencies (if any) of the current binding\n                                if (binding['after']) {\n                                    cyclicDependencyStack.push(bindingKey);\n                                    ko.utils.arrayForEach(binding['after'], function(bindingDependencyKey) {\n                                        if (bindings[bindingDependencyKey]) {\n                                            if (ko.utils.arrayIndexOf(cyclicDependencyStack, bindingDependencyKey) !== -1) {\n                                                throw Error(\"Cannot combine the following bindings, because they have a cyclic dependency: \" + cyclicDependencyStack.join(\", \"));\n                                            } else {\n                                                pushBinding(bindingDependencyKey);\n                                            }\n                                        }\n                                    });\n                                    cyclicDependencyStack.length--;\n                                }\n                                // Next add the current binding\n                                result.push({ key: bindingKey, handler: binding });\n                            }\n                            bindingsConsidered[bindingKey] = true;\n                        }\n                    });\n\n                    return result;\n                }\n\n                function applyBindingsToNodeInternal(node, sourceBindings, bindingContext) {\n                    var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {});\n\n                    // Prevent multiple applyBindings calls for the same node, except when a binding value is specified\n                    var alreadyBound = bindingInfo.alreadyBound;\n                    if (!sourceBindings) {\n                        if (alreadyBound) {\n                            throw Error(\"You cannot apply bindings multiple times to the same element.\");\n                        }\n                        bindingInfo.alreadyBound = true;\n                    }\n                    if (!alreadyBound) {\n                        bindingInfo.context = bindingContext;\n                    }\n                    if (!bindingInfo.notifiedEvents) {\n                        bindingInfo.notifiedEvents = {};\n                    }\n\n                    // Use bindings if given, otherwise fall back on asking the bindings provider to give us some bindings\n                    var bindings;\n                    if (sourceBindings && typeof sourceBindings !== 'function') {\n                        bindings = sourceBindings;\n                    } else {\n                        var provider = ko.bindingProvider['instance'],\n                            getBindings = provider['getBindingAccessors'] || getBindingsAndMakeAccessors;\n\n                        // Get the binding from the provider within a computed observable so that we can update the bindings whenever\n                        // the binding context is updated or if the binding provider accesses observables.\n                        var bindingsUpdater = ko.dependentObservable(\n                            function() {\n                                bindings = sourceBindings ? sourceBindings(bindingContext, node) : getBindings.call(provider, node, bindingContext);\n                                // Register a dependency on the binding context to support observable view models.\n                                if (bindings) {\n                                    if (bindingContext[contextSubscribable]) {\n                                        bindingContext[contextSubscribable]();\n                                    }\n                                    if (bindingContext[contextDataDependency]) {\n                                        bindingContext[contextDataDependency]();\n                                    }\n                                }\n                                return bindings;\n                            },\n                            null, { disposeWhenNodeIsRemoved: node }\n                        );\n\n                        if (!bindings || !bindingsUpdater.isActive())\n                            bindingsUpdater = null;\n                    }\n\n                    var contextToExtend = bindingContext;\n                    var bindingHandlerThatControlsDescendantBindings;\n                    if (bindings) {\n                        // Return the value accessor for a given binding. When bindings are static (won't be updated because of a binding\n                        // context update), just return the value accessor from the binding. Otherwise, return a function that always gets\n                        // the latest binding value and registers a dependency on the binding updater.\n                        var getValueAccessor = bindingsUpdater\n                            ? function(bindingKey) {\n                                return function() {\n                                    return evaluateValueAccessor(bindingsUpdater()[bindingKey]);\n                                };\n                            } : function(bindingKey) {\n                                return bindings[bindingKey];\n                            };\n\n                        // Use of allBindings as a function is maintained for backwards compatibility, but its use is deprecated\n                        function allBindings() {\n                            return ko.utils.objectMap(bindingsUpdater ? bindingsUpdater() : bindings, evaluateValueAccessor);\n                        }\n                        // The following is the 3.x allBindings API\n                        allBindings['get'] = function(key) {\n                            return bindings[key] && evaluateValueAccessor(getValueAccessor(key));\n                        };\n                        allBindings['has'] = function(key) {\n                            return key in bindings;\n                        };\n\n                        if (ko.bindingEvent.childrenComplete in bindings) {\n                            ko.bindingEvent.subscribe(node, ko.bindingEvent.childrenComplete, function () {\n                                var callback = evaluateValueAccessor(bindings[ko.bindingEvent.childrenComplete]);\n                                if (callback) {\n                                    var nodes = ko.virtualElements.childNodes(node);\n                                    if (nodes.length) {\n                                        callback(nodes, ko.dataFor(nodes[0]));\n                                    }\n                                }\n                            });\n                        }\n\n                        if (ko.bindingEvent.descendantsComplete in bindings) {\n                            contextToExtend = ko.bindingEvent.startPossiblyAsyncContentBinding(node, bindingContext);\n                            ko.bindingEvent.subscribe(node, ko.bindingEvent.descendantsComplete, function () {\n                                var callback = evaluateValueAccessor(bindings[ko.bindingEvent.descendantsComplete]);\n                                if (callback && ko.virtualElements.firstChild(node)) {\n                                    callback(node);\n                                }\n                            });\n                        }\n\n                        // First put the bindings into the right order\n                        var orderedBindings = topologicalSortBindings(bindings);\n\n                        // Go through the sorted bindings, calling init and update for each\n                        ko.utils.arrayForEach(orderedBindings, function(bindingKeyAndHandler) {\n                            // Note that topologicalSortBindings has already filtered out any nonexistent binding handlers,\n                            // so bindingKeyAndHandler.handler will always be nonnull.\n                            var handlerInitFn = bindingKeyAndHandler.handler[\"init\"],\n                                handlerUpdateFn = bindingKeyAndHandler.handler[\"update\"],\n                                bindingKey = bindingKeyAndHandler.key;\n\n                            if (node.nodeType === 8) {\n                                validateThatBindingIsAllowedForVirtualElements(bindingKey);\n                            }\n\n                            try {\n                                // Run init, ignoring any dependencies\n                                if (typeof handlerInitFn == \"function\") {\n                                    ko.dependencyDetection.ignore(function() {\n                                        var initResult = handlerInitFn(node, getValueAccessor(bindingKey), allBindings, contextToExtend['$data'], contextToExtend);\n\n                                        // If this binding handler claims to control descendant bindings, make a note of this\n                                        if (initResult && initResult['controlsDescendantBindings']) {\n                                            if (bindingHandlerThatControlsDescendantBindings !== undefined)\n                                                throw new Error(\"Multiple bindings (\" + bindingHandlerThatControlsDescendantBindings + \" and \" + bindingKey + \") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.\");\n                                            bindingHandlerThatControlsDescendantBindings = bindingKey;\n                                        }\n                                    });\n                                }\n\n                                // Run update in its own computed wrapper\n                                if (typeof handlerUpdateFn == \"function\") {\n                                    ko.dependentObservable(\n                                        function() {\n                                            handlerUpdateFn(node, getValueAccessor(bindingKey), allBindings, contextToExtend['$data'], contextToExtend);\n                                        },\n                                        null,\n                                        { disposeWhenNodeIsRemoved: node }\n                                    );\n                                }\n                            } catch (ex) {\n                                ex.message = \"Unable to process binding \\\"\" + bindingKey + \": \" + bindings[bindingKey] + \"\\\"\\nMessage: \" + ex.message;\n                                throw ex;\n                            }\n                        });\n                    }\n\n                    var shouldBindDescendants = bindingHandlerThatControlsDescendantBindings === undefined;\n                    return {\n                        'shouldBindDescendants': shouldBindDescendants,\n                        'bindingContextForDescendants': shouldBindDescendants && contextToExtend\n                    };\n                };\n\n                ko.storedBindingContextForNode = function (node) {\n                    var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey);\n                    return bindingInfo && bindingInfo.context;\n                }\n\n                function getBindingContext(viewModelOrBindingContext, extendContextCallback) {\n                    return viewModelOrBindingContext && (viewModelOrBindingContext instanceof ko.bindingContext)\n                        ? viewModelOrBindingContext\n                        : new ko.bindingContext(viewModelOrBindingContext, undefined, undefined, extendContextCallback);\n                }\n\n                ko.applyBindingAccessorsToNode = function (node, bindings, viewModelOrBindingContext) {\n                    if (node.nodeType === 1) // If it's an element, workaround IE <= 8 HTML parsing weirdness\n                        ko.virtualElements.normaliseVirtualElementDomStructure(node);\n                    return applyBindingsToNodeInternal(node, bindings, getBindingContext(viewModelOrBindingContext));\n                };\n\n                ko.applyBindingsToNode = function (node, bindings, viewModelOrBindingContext) {\n                    var context = getBindingContext(viewModelOrBindingContext);\n                    return ko.applyBindingAccessorsToNode(node, makeBindingAccessors(bindings, context, node), context);\n                };\n\n                ko.applyBindingsToDescendants = function(viewModelOrBindingContext, rootNode) {\n                    if (rootNode.nodeType === 1 || rootNode.nodeType === 8)\n                        applyBindingsToDescendantsInternal(getBindingContext(viewModelOrBindingContext), rootNode);\n                };\n\n                ko.applyBindings = function (viewModelOrBindingContext, rootNode, extendContextCallback) {\n                    // If jQuery is loaded after Knockout, we won't initially have access to it. So save it here.\n                    if (!jQueryInstance && window['jQuery']) {\n                        jQueryInstance = window['jQuery'];\n                    }\n\n                    if (arguments.length < 2) {\n                        rootNode = document.body;\n                        if (!rootNode) {\n                            throw Error(\"ko.applyBindings: could not find document.body; has the document been loaded?\");\n                        }\n                    } else if (!rootNode || (rootNode.nodeType !== 1 && rootNode.nodeType !== 8)) {\n                        throw Error(\"ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node\");\n                    }\n\n                    applyBindingsToNodeAndDescendantsInternal(getBindingContext(viewModelOrBindingContext, extendContextCallback), rootNode);\n                };\n\n                // Retrieving binding context from arbitrary nodes\n                ko.contextFor = function(node) {\n                    // We can only do something meaningful for elements and comment nodes (in particular, not text nodes, as IE can't store domdata for them)\n                    if (node && (node.nodeType === 1 || node.nodeType === 8)) {\n                        return ko.storedBindingContextForNode(node);\n                    }\n                    return undefined;\n                };\n                ko.dataFor = function(node) {\n                    var context = ko.contextFor(node);\n                    return context ? context['$data'] : undefined;\n                };\n\n                ko.exportSymbol('bindingHandlers', ko.bindingHandlers);\n                ko.exportSymbol('bindingEvent', ko.bindingEvent);\n                ko.exportSymbol('bindingEvent.subscribe', ko.bindingEvent.subscribe);\n                ko.exportSymbol('bindingEvent.startPossiblyAsyncContentBinding', ko.bindingEvent.startPossiblyAsyncContentBinding);\n                ko.exportSymbol('applyBindings', ko.applyBindings);\n                ko.exportSymbol('applyBindingsToDescendants', ko.applyBindingsToDescendants);\n                ko.exportSymbol('applyBindingAccessorsToNode', ko.applyBindingAccessorsToNode);\n                ko.exportSymbol('applyBindingsToNode', ko.applyBindingsToNode);\n                ko.exportSymbol('contextFor', ko.contextFor);\n                ko.exportSymbol('dataFor', ko.dataFor);\n            })();\n            (function(undefined) {\n                var loadingSubscribablesCache = {}, // Tracks component loads that are currently in flight\n                    loadedDefinitionsCache = {};    // Tracks component loads that have already completed\n\n                ko.components = {\n                    get: function(componentName, callback) {\n                        var cachedDefinition = getObjectOwnProperty(loadedDefinitionsCache, componentName);\n                        if (cachedDefinition) {\n                            // It's already loaded and cached. Reuse the same definition object.\n                            // Note that for API consistency, even cache hits complete asynchronously by default.\n                            // You can bypass this by putting synchronous:true on your component config.\n                            if (cachedDefinition.isSynchronousComponent) {\n                                ko.dependencyDetection.ignore(function() { // See comment in loaderRegistryBehaviors.js for reasoning\n                                    callback(cachedDefinition.definition);\n                                });\n                            } else {\n                                ko.tasks.schedule(function() { callback(cachedDefinition.definition); });\n                            }\n                        } else {\n                            // Join the loading process that is already underway, or start a new one.\n                            loadComponentAndNotify(componentName, callback);\n                        }\n                    },\n\n                    clearCachedDefinition: function(componentName) {\n                        delete loadedDefinitionsCache[componentName];\n                    },\n\n                    _getFirstResultFromLoaders: getFirstResultFromLoaders\n                };\n\n                function getObjectOwnProperty(obj, propName) {\n                    return Object.prototype.hasOwnProperty.call(obj, propName) ? obj[propName] : undefined;\n                }\n\n                function loadComponentAndNotify(componentName, callback) {\n                    var subscribable = getObjectOwnProperty(loadingSubscribablesCache, componentName),\n                        completedAsync;\n                    if (!subscribable) {\n                        // It's not started loading yet. Start loading, and when it's done, move it to loadedDefinitionsCache.\n                        subscribable = loadingSubscribablesCache[componentName] = new ko.subscribable();\n                        subscribable.subscribe(callback);\n\n                        beginLoadingComponent(componentName, function(definition, config) {\n                            var isSynchronousComponent = !!(config && config['synchronous']);\n                            loadedDefinitionsCache[componentName] = { definition: definition, isSynchronousComponent: isSynchronousComponent };\n                            delete loadingSubscribablesCache[componentName];\n\n                            // For API consistency, all loads complete asynchronously. However we want to avoid\n                            // adding an extra task schedule if it's unnecessary (i.e., the completion is already\n                            // async).\n                            //\n                            // You can bypass the 'always asynchronous' feature by putting the synchronous:true\n                            // flag on your component configuration when you register it.\n                            if (completedAsync || isSynchronousComponent) {\n                                // Note that notifySubscribers ignores any dependencies read within the callback.\n                                // See comment in loaderRegistryBehaviors.js for reasoning\n                                subscribable['notifySubscribers'](definition);\n                            } else {\n                                ko.tasks.schedule(function() {\n                                    subscribable['notifySubscribers'](definition);\n                                });\n                            }\n                        });\n                        completedAsync = true;\n                    } else {\n                        subscribable.subscribe(callback);\n                    }\n                }\n\n                function beginLoadingComponent(componentName, callback) {\n                    getFirstResultFromLoaders('getConfig', [componentName], function(config) {\n                        if (config) {\n                            // We have a config, so now load its definition\n                            getFirstResultFromLoaders('loadComponent', [componentName, config], function(definition) {\n                                callback(definition, config);\n                            });\n                        } else {\n                            // The component has no config - it's unknown to all the loaders.\n                            // Note that this is not an error (e.g., a module loading error) - that would abort the\n                            // process and this callback would not run. For this callback to run, all loaders must\n                            // have confirmed they don't know about this component.\n                            callback(null, null);\n                        }\n                    });\n                }\n\n                function getFirstResultFromLoaders(methodName, argsExceptCallback, callback, candidateLoaders) {\n                    // On the first call in the stack, start with the full set of loaders\n                    if (!candidateLoaders) {\n                        candidateLoaders = ko.components['loaders'].slice(0); // Use a copy, because we'll be mutating this array\n                    }\n\n                    // Try the next candidate\n                    var currentCandidateLoader = candidateLoaders.shift();\n                    if (currentCandidateLoader) {\n                        var methodInstance = currentCandidateLoader[methodName];\n                        if (methodInstance) {\n                            var wasAborted = false,\n                                synchronousReturnValue = methodInstance.apply(currentCandidateLoader, argsExceptCallback.concat(function(result) {\n                                    if (wasAborted) {\n                                        callback(null);\n                                    } else if (result !== null) {\n                                        // This candidate returned a value. Use it.\n                                        callback(result);\n                                    } else {\n                                        // Try the next candidate\n                                        getFirstResultFromLoaders(methodName, argsExceptCallback, callback, candidateLoaders);\n                                    }\n                                }));\n\n                            // Currently, loaders may not return anything synchronously. This leaves open the possibility\n                            // that we'll extend the API to support synchronous return values in the future. It won't be\n                            // a breaking change, because currently no loader is allowed to return anything except undefined.\n                            if (synchronousReturnValue !== undefined) {\n                                wasAborted = true;\n\n                                // Method to suppress exceptions will remain undocumented. This is only to keep\n                                // KO's specs running tidily, since we can observe the loading got aborted without\n                                // having exceptions cluttering up the console too.\n                                if (!currentCandidateLoader['suppressLoaderExceptions']) {\n                                    throw new Error('Component loaders must supply values by invoking the callback, not by returning values synchronously.');\n                                }\n                            }\n                        } else {\n                            // This candidate doesn't have the relevant handler. Synchronously move on to the next one.\n                            getFirstResultFromLoaders(methodName, argsExceptCallback, callback, candidateLoaders);\n                        }\n                    } else {\n                        // No candidates returned a value\n                        callback(null);\n                    }\n                }\n\n                // Reference the loaders via string name so it's possible for developers\n                // to replace the whole array by assigning to ko.components.loaders\n                ko.components['loaders'] = [];\n\n                ko.exportSymbol('components', ko.components);\n                ko.exportSymbol('components.get', ko.components.get);\n                ko.exportSymbol('components.clearCachedDefinition', ko.components.clearCachedDefinition);\n            })();\n            (function(undefined) {\n\n                // The default loader is responsible for two things:\n                // 1. Maintaining the default in-memory registry of component configuration objects\n                //    (i.e., the thing you're writing to when you call ko.components.register(someName, ...))\n                // 2. Answering requests for components by fetching configuration objects\n                //    from that default in-memory registry and resolving them into standard\n                //    component definition objects (of the form { createViewModel: ..., template: ... })\n                // Custom loaders may override either of these facilities, i.e.,\n                // 1. To supply configuration objects from some other source (e.g., conventions)\n                // 2. Or, to resolve configuration objects by loading viewmodels/templates via arbitrary logic.\n\n                var defaultConfigRegistry = {};\n\n                ko.components.register = function(componentName, config) {\n                    if (!config) {\n                        throw new Error('Invalid configuration for ' + componentName);\n                    }\n\n                    if (ko.components.isRegistered(componentName)) {\n                        throw new Error('Component ' + componentName + ' is already registered');\n                    }\n\n                    defaultConfigRegistry[componentName] = config;\n                };\n\n                ko.components.isRegistered = function(componentName) {\n                    return Object.prototype.hasOwnProperty.call(defaultConfigRegistry, componentName);\n                };\n\n                ko.components.unregister = function(componentName) {\n                    delete defaultConfigRegistry[componentName];\n                    ko.components.clearCachedDefinition(componentName);\n                };\n\n                ko.components.defaultLoader = {\n                    'getConfig': function(componentName, callback) {\n                        var result = ko.components.isRegistered(componentName)\n                            ? defaultConfigRegistry[componentName]\n                            : null;\n                        callback(result);\n                    },\n\n                    'loadComponent': function(componentName, config, callback) {\n                        var errorCallback = makeErrorCallback(componentName);\n                        possiblyGetConfigFromAmd(errorCallback, config, function(loadedConfig) {\n                            resolveConfig(componentName, errorCallback, loadedConfig, callback);\n                        });\n                    },\n\n                    'loadTemplate': function(componentName, templateConfig, callback) {\n                        resolveTemplate(makeErrorCallback(componentName), templateConfig, callback);\n                    },\n\n                    'loadViewModel': function(componentName, viewModelConfig, callback) {\n                        resolveViewModel(makeErrorCallback(componentName), viewModelConfig, callback);\n                    }\n                };\n\n                var createViewModelKey = 'createViewModel';\n\n                // Takes a config object of the form { template: ..., viewModel: ... }, and asynchronously convert it\n                // into the standard component definition format:\n                //    { template: <ArrayOfDomNodes>, createViewModel: function(params, componentInfo) { ... } }.\n                // Since both template and viewModel may need to be resolved asynchronously, both tasks are performed\n                // in parallel, and the results joined when both are ready. We don't depend on any promises infrastructure,\n                // so this is implemented manually below.\n                function resolveConfig(componentName, errorCallback, config, callback) {\n                    var result = {},\n                        makeCallBackWhenZero = 2,\n                        tryIssueCallback = function() {\n                            if (--makeCallBackWhenZero === 0) {\n                                callback(result);\n                            }\n                        },\n                        templateConfig = config['template'],\n                        viewModelConfig = config['viewModel'];\n\n                    if (templateConfig) {\n                        possiblyGetConfigFromAmd(errorCallback, templateConfig, function(loadedConfig) {\n                            ko.components._getFirstResultFromLoaders('loadTemplate', [componentName, loadedConfig], function(resolvedTemplate) {\n                                result['template'] = resolvedTemplate;\n                                tryIssueCallback();\n                            });\n                        });\n                    } else {\n                        tryIssueCallback();\n                    }\n\n                    if (viewModelConfig) {\n                        possiblyGetConfigFromAmd(errorCallback, viewModelConfig, function(loadedConfig) {\n                            ko.components._getFirstResultFromLoaders('loadViewModel', [componentName, loadedConfig], function(resolvedViewModel) {\n                                result[createViewModelKey] = resolvedViewModel;\n                                tryIssueCallback();\n                            });\n                        });\n                    } else {\n                        tryIssueCallback();\n                    }\n                }\n\n                function resolveTemplate(errorCallback, templateConfig, callback) {\n                    if (typeof templateConfig === 'string') {\n                        // Markup - parse it\n                        callback(ko.utils.parseHtmlFragment(templateConfig));\n                    } else if (templateConfig instanceof Array) {\n                        // Assume already an array of DOM nodes - pass through unchanged\n                        callback(templateConfig);\n                    } else if (isDocumentFragment(templateConfig)) {\n                        // Document fragment - use its child nodes\n                        callback(ko.utils.makeArray(templateConfig.childNodes));\n                    } else if (templateConfig['element']) {\n                        var element = templateConfig['element'];\n                        if (isDomElement(element)) {\n                            // Element instance - copy its child nodes\n                            callback(cloneNodesFromTemplateSourceElement(element));\n                        } else if (typeof element === 'string') {\n                            // Element ID - find it, then copy its child nodes\n                            var elemInstance = document.getElementById(element);\n                            if (elemInstance) {\n                                callback(cloneNodesFromTemplateSourceElement(elemInstance));\n                            } else {\n                                errorCallback('Cannot find element with ID ' + element);\n                            }\n                        } else {\n                            errorCallback('Unknown element type: ' + element);\n                        }\n                    } else {\n                        errorCallback('Unknown template value: ' + templateConfig);\n                    }\n                }\n\n                function resolveViewModel(errorCallback, viewModelConfig, callback) {\n                    if (typeof viewModelConfig === 'function') {\n                        // Constructor - convert to standard factory function format\n                        // By design, this does *not* supply componentInfo to the constructor, as the intent is that\n                        // componentInfo contains non-viewmodel data (e.g., the component's element) that should only\n                        // be used in factory functions, not viewmodel constructors.\n                        callback(function (params /*, componentInfo */) {\n                            return new viewModelConfig(params);\n                        });\n                    } else if (typeof viewModelConfig[createViewModelKey] === 'function') {\n                        // Already a factory function - use it as-is\n                        callback(viewModelConfig[createViewModelKey]);\n                    } else if ('instance' in viewModelConfig) {\n                        // Fixed object instance - promote to createViewModel format for API consistency\n                        var fixedInstance = viewModelConfig['instance'];\n                        callback(function (params, componentInfo) {\n                            return fixedInstance;\n                        });\n                    } else if ('viewModel' in viewModelConfig) {\n                        // Resolved AMD module whose value is of the form { viewModel: ... }\n                        resolveViewModel(errorCallback, viewModelConfig['viewModel'], callback);\n                    } else {\n                        errorCallback('Unknown viewModel value: ' + viewModelConfig);\n                    }\n                }\n\n                function cloneNodesFromTemplateSourceElement(elemInstance) {\n                    switch (ko.utils.tagNameLower(elemInstance)) {\n                        case 'script':\n                            return ko.utils.parseHtmlFragment(elemInstance.text);\n                        case 'textarea':\n                            return ko.utils.parseHtmlFragment(elemInstance.value);\n                        case 'template':\n                            // For browsers with proper <template> element support (i.e., where the .content property\n                            // gives a document fragment), use that document fragment.\n                            if (isDocumentFragment(elemInstance.content)) {\n                                return ko.utils.cloneNodes(elemInstance.content.childNodes);\n                            }\n                    }\n\n                    // Regular elements such as <div>, and <template> elements on old browsers that don't really\n                    // understand <template> and just treat it as a regular container\n                    return ko.utils.cloneNodes(elemInstance.childNodes);\n                }\n\n                function isDomElement(obj) {\n                    if (window['HTMLElement']) {\n                        return obj instanceof HTMLElement;\n                    } else {\n                        return obj && obj.tagName && obj.nodeType === 1;\n                    }\n                }\n\n                function isDocumentFragment(obj) {\n                    if (window['DocumentFragment']) {\n                        return obj instanceof DocumentFragment;\n                    } else {\n                        return obj && obj.nodeType === 11;\n                    }\n                }\n\n                function possiblyGetConfigFromAmd(errorCallback, config, callback) {\n                    if (typeof config['require'] === 'string') {\n                        // The config is the value of an AMD module\n                        if (amdRequire || window['require']) {\n                            (amdRequire || window['require'])([config['require']], function (module) {\n                                if (module && typeof module === 'object' && module.__esModule && module.default) {\n                                    module = module.default;\n                                }\n                                callback(module);\n                            });\n                        } else {\n                            errorCallback('Uses require, but no AMD loader is present');\n                        }\n                    } else {\n                        callback(config);\n                    }\n                }\n\n                function makeErrorCallback(componentName) {\n                    return function (message) {\n                        throw new Error('Component \\'' + componentName + '\\': ' + message);\n                    };\n                }\n\n                ko.exportSymbol('components.register', ko.components.register);\n                ko.exportSymbol('components.isRegistered', ko.components.isRegistered);\n                ko.exportSymbol('components.unregister', ko.components.unregister);\n\n                // Expose the default loader so that developers can directly ask it for configuration\n                // or to resolve configuration\n                ko.exportSymbol('components.defaultLoader', ko.components.defaultLoader);\n\n                // By default, the default loader is the only registered component loader\n                ko.components['loaders'].push(ko.components.defaultLoader);\n\n                // Privately expose the underlying config registry for use in old-IE shim\n                ko.components._allRegisteredComponents = defaultConfigRegistry;\n            })();\n            (function (undefined) {\n                // Overridable API for determining which component name applies to a given node. By overriding this,\n                // you can for example map specific tagNames to components that are not preregistered.\n                ko.components['getComponentNameForNode'] = function(node) {\n                    var tagNameLower = ko.utils.tagNameLower(node);\n                    if (ko.components.isRegistered(tagNameLower)) {\n                        // Try to determine that this node can be considered a *custom* element; see https://github.com/knockout/knockout/issues/1603\n                        if (tagNameLower.indexOf('-') != -1 || ('' + node) == \"[object HTMLUnknownElement]\" || (ko.utils.ieVersion <= 8 && node.tagName === tagNameLower)) {\n                            return tagNameLower;\n                        }\n                    }\n                };\n\n                ko.components.addBindingsForCustomElement = function(allBindings, node, bindingContext, valueAccessors) {\n                    // Determine if it's really a custom element matching a component\n                    if (node.nodeType === 1) {\n                        var componentName = ko.components['getComponentNameForNode'](node);\n                        if (componentName) {\n                            // It does represent a component, so add a component binding for it\n                            allBindings = allBindings || {};\n\n                            if (allBindings['component']) {\n                                // Avoid silently overwriting some other 'component' binding that may already be on the element\n                                throw new Error('Cannot use the \"component\" binding on a custom element matching a component');\n                            }\n\n                            var componentBindingValue = { 'name': componentName, 'params': getComponentParamsFromCustomElement(node, bindingContext) };\n\n                            allBindings['component'] = valueAccessors\n                                ? function() { return componentBindingValue; }\n                                : componentBindingValue;\n                        }\n                    }\n\n                    return allBindings;\n                }\n\n                var nativeBindingProviderInstance = new ko.bindingProvider();\n\n                function getComponentParamsFromCustomElement(elem, bindingContext) {\n                    var paramsAttribute = elem.getAttribute('params');\n\n                    if (paramsAttribute) {\n                        var params = nativeBindingProviderInstance['parseBindingsString'](paramsAttribute, bindingContext, elem, { 'valueAccessors': true, 'bindingParams': true }),\n                            rawParamComputedValues = ko.utils.objectMap(params, function(paramValue, paramName) {\n                                return ko.computed(paramValue, null, { disposeWhenNodeIsRemoved: elem });\n                            }),\n                            result = ko.utils.objectMap(rawParamComputedValues, function(paramValueComputed, paramName) {\n                                var paramValue = paramValueComputed.peek();\n                                // Does the evaluation of the parameter value unwrap any observables?\n                                if (!paramValueComputed.isActive()) {\n                                    // No it doesn't, so there's no need for any computed wrapper. Just pass through the supplied value directly.\n                                    // Example: \"someVal: firstName, age: 123\" (whether or not firstName is an observable/computed)\n                                    return paramValue;\n                                } else {\n                                    // Yes it does. Supply a computed property that unwraps both the outer (binding expression)\n                                    // level of observability, and any inner (resulting model value) level of observability.\n                                    // This means the component doesn't have to worry about multiple unwrapping. If the value is a\n                                    // writable observable, the computed will also be writable and pass the value on to the observable.\n                                    return ko.computed({\n                                        'read': function() {\n                                            return ko.utils.unwrapObservable(paramValueComputed());\n                                        },\n                                        'write': ko.isWriteableObservable(paramValue) && function(value) {\n                                            paramValueComputed()(value);\n                                        },\n                                        disposeWhenNodeIsRemoved: elem\n                                    });\n                                }\n                            });\n\n                        // Give access to the raw computeds, as long as that wouldn't overwrite any custom param also called '$raw'\n                        // This is in case the developer wants to react to outer (binding) observability separately from inner\n                        // (model value) observability, or in case the model value observable has subobservables.\n                        if (!Object.prototype.hasOwnProperty.call(result, '$raw')) {\n                            result['$raw'] = rawParamComputedValues;\n                        }\n\n                        return result;\n                    } else {\n                        // For consistency, absence of a \"params\" attribute is treated the same as the presence of\n                        // any empty one. Otherwise component viewmodels need special code to check whether or not\n                        // 'params' or 'params.$raw' is null/undefined before reading subproperties, which is annoying.\n                        return { '$raw': {} };\n                    }\n                }\n\n                // --------------------------------------------------------------------------------\n                // Compatibility code for older (pre-HTML5) IE browsers\n\n                if (ko.utils.ieVersion < 9) {\n                    // Whenever you preregister a component, enable it as a custom element in the current document\n                    ko.components['register'] = (function(originalFunction) {\n                        return function(componentName) {\n                            document.createElement(componentName); // Allows IE<9 to parse markup containing the custom element\n                            return originalFunction.apply(this, arguments);\n                        }\n                    })(ko.components['register']);\n\n                    // Whenever you create a document fragment, enable all preregistered component names as custom elements\n                    // This is needed to make innerShiv/jQuery HTML parsing correctly handle the custom elements\n                    document.createDocumentFragment = (function(originalFunction) {\n                        return function() {\n                            var newDocFrag = originalFunction(),\n                                allComponents = ko.components._allRegisteredComponents;\n                            for (var componentName in allComponents) {\n                                if (Object.prototype.hasOwnProperty.call(allComponents, componentName)) {\n                                    newDocFrag.createElement(componentName);\n                                }\n                            }\n                            return newDocFrag;\n                        };\n                    })(document.createDocumentFragment);\n                }\n            })();(function(undefined) {\n                var componentLoadingOperationUniqueId = 0;\n\n                ko.bindingHandlers['component'] = {\n                    'init': function(element, valueAccessor, ignored1, ignored2, bindingContext) {\n                        var currentViewModel,\n                            currentLoadingOperationId,\n                            afterRenderSub,\n                            disposeAssociatedComponentViewModel = function () {\n                                var currentViewModelDispose = currentViewModel && currentViewModel['dispose'];\n                                if (typeof currentViewModelDispose === 'function') {\n                                    currentViewModelDispose.call(currentViewModel);\n                                }\n                                if (afterRenderSub) {\n                                    afterRenderSub.dispose();\n                                }\n                                afterRenderSub = null;\n                                currentViewModel = null;\n                                // Any in-flight loading operation is no longer relevant, so make sure we ignore its completion\n                                currentLoadingOperationId = null;\n                            },\n                            originalChildNodes = ko.utils.makeArray(ko.virtualElements.childNodes(element));\n\n                        ko.virtualElements.emptyNode(element);\n                        ko.utils.domNodeDisposal.addDisposeCallback(element, disposeAssociatedComponentViewModel);\n\n                        ko.computed(function () {\n                            var value = ko.utils.unwrapObservable(valueAccessor()),\n                                componentName, componentParams;\n\n                            if (typeof value === 'string') {\n                                componentName = value;\n                            } else {\n                                componentName = ko.utils.unwrapObservable(value['name']);\n                                componentParams = ko.utils.unwrapObservable(value['params']);\n                            }\n\n                            if (!componentName) {\n                                throw new Error('No component name specified');\n                            }\n\n                            var asyncContext = ko.bindingEvent.startPossiblyAsyncContentBinding(element, bindingContext);\n\n                            var loadingOperationId = currentLoadingOperationId = ++componentLoadingOperationUniqueId;\n                            ko.components.get(componentName, function(componentDefinition) {\n                                // If this is not the current load operation for this element, ignore it.\n                                if (currentLoadingOperationId !== loadingOperationId) {\n                                    return;\n                                }\n\n                                // Clean up previous state\n                                disposeAssociatedComponentViewModel();\n\n                                // Instantiate and bind new component. Implicitly this cleans any old DOM nodes.\n                                if (!componentDefinition) {\n                                    throw new Error('Unknown component \\'' + componentName + '\\'');\n                                }\n                                cloneTemplateIntoElement(componentName, componentDefinition, element);\n\n                                var componentInfo = {\n                                    'element': element,\n                                    'templateNodes': originalChildNodes\n                                };\n\n                                var componentViewModel = createViewModel(componentDefinition, componentParams, componentInfo),\n                                    childBindingContext = asyncContext['createChildContext'](componentViewModel, {\n                                        'extend': function(ctx) {\n                                            ctx['$component'] = componentViewModel;\n                                            ctx['$componentTemplateNodes'] = originalChildNodes;\n                                        }\n                                    });\n\n                                if (componentViewModel && componentViewModel['koDescendantsComplete']) {\n                                    afterRenderSub = ko.bindingEvent.subscribe(element, ko.bindingEvent.descendantsComplete, componentViewModel['koDescendantsComplete'], componentViewModel);\n                                }\n\n                                currentViewModel = componentViewModel;\n                                ko.applyBindingsToDescendants(childBindingContext, element);\n                            });\n                        }, null, { disposeWhenNodeIsRemoved: element });\n\n                        return { 'controlsDescendantBindings': true };\n                    }\n                };\n\n                ko.virtualElements.allowedBindings['component'] = true;\n\n                function cloneTemplateIntoElement(componentName, componentDefinition, element) {\n                    var template = componentDefinition['template'];\n                    if (!template) {\n                        throw new Error('Component \\'' + componentName + '\\' has no template');\n                    }\n\n                    var clonedNodesArray = ko.utils.cloneNodes(template);\n                    ko.virtualElements.setDomNodeChildren(element, clonedNodesArray);\n                }\n\n                function createViewModel(componentDefinition, componentParams, componentInfo) {\n                    var componentViewModelFactory = componentDefinition['createViewModel'];\n                    return componentViewModelFactory\n                        ? componentViewModelFactory.call(componentDefinition, componentParams, componentInfo)\n                        : componentParams; // Template-only component\n                }\n\n            })();\n            var attrHtmlToJavaScriptMap = { 'class': 'className', 'for': 'htmlFor' };\n            ko.bindingHandlers['attr'] = {\n                'update': function(element, valueAccessor, allBindings) {\n                    var value = ko.utils.unwrapObservable(valueAccessor()) || {};\n                    ko.utils.objectForEach(value, function(attrName, attrValue) {\n                        attrValue = ko.utils.unwrapObservable(attrValue);\n\n                        // Find the namespace of this attribute, if any.\n                        var prefixLen = attrName.indexOf(':');\n                        var namespace = \"lookupNamespaceURI\" in element && prefixLen > 0 && element.lookupNamespaceURI(attrName.substr(0, prefixLen));\n\n                        // To cover cases like \"attr: { checked:someProp }\", we want to remove the attribute entirely\n                        // when someProp is a \"no value\"-like value (strictly null, false, or undefined)\n                        // (because the absence of the \"checked\" attr is how to mark an element as not checked, etc.)\n                        var toRemove = (attrValue === false) || (attrValue === null) || (attrValue === undefined);\n                        if (toRemove) {\n                            namespace ? element.removeAttributeNS(namespace, attrName) : element.removeAttribute(attrName);\n                        } else {\n                            attrValue = attrValue.toString();\n                        }\n\n                        // In IE <= 7 and IE8 Quirks Mode, you have to use the JavaScript property name instead of the\n                        // HTML attribute name for certain attributes. IE8 Standards Mode supports the correct behavior,\n                        // but instead of figuring out the mode, we'll just set the attribute through the JavaScript\n                        // property for IE <= 8.\n                        if (ko.utils.ieVersion <= 8 && attrName in attrHtmlToJavaScriptMap) {\n                            attrName = attrHtmlToJavaScriptMap[attrName];\n                            if (toRemove)\n                                element.removeAttribute(attrName);\n                            else\n                                element[attrName] = attrValue;\n                        } else if (!toRemove) {\n                            namespace ? element.setAttributeNS(namespace, attrName, attrValue) : element.setAttribute(attrName, attrValue);\n                        }\n\n                        // Treat \"name\" specially - although you can think of it as an attribute, it also needs\n                        // special handling on older versions of IE (https://github.com/SteveSanderson/knockout/pull/333)\n                        // Deliberately being case-sensitive here because XHTML would regard \"Name\" as a different thing\n                        // entirely, and there's no strong reason to allow for such casing in HTML.\n                        if (attrName === \"name\") {\n                            ko.utils.setElementName(element, toRemove ? \"\" : attrValue);\n                        }\n                    });\n                }\n            };\n            (function() {\n\n                ko.bindingHandlers['checked'] = {\n                    'after': ['value', 'attr'],\n                    'init': function (element, valueAccessor, allBindings) {\n                        var checkedValue = ko.pureComputed(function() {\n                            // Treat \"value\" like \"checkedValue\" when it is included with \"checked\" binding\n                            if (allBindings['has']('checkedValue')) {\n                                return ko.utils.unwrapObservable(allBindings.get('checkedValue'));\n                            } else if (useElementValue) {\n                                if (allBindings['has']('value')) {\n                                    return ko.utils.unwrapObservable(allBindings.get('value'));\n                                } else {\n                                    return element.value;\n                                }\n                            }\n                        });\n\n                        function updateModel() {\n                            // This updates the model value from the view value.\n                            // It runs in response to DOM events (click) and changes in checkedValue.\n                            var isChecked = element.checked,\n                                elemValue = checkedValue();\n\n                            // When we're first setting up this computed, don't change any model state.\n                            if (ko.computedContext.isInitial()) {\n                                return;\n                            }\n\n                            // We can ignore unchecked radio buttons, because some other radio\n                            // button will be checked, and that one can take care of updating state.\n                            // Also ignore value changes to an already unchecked checkbox.\n                            if (!isChecked && (isRadio || ko.computedContext.getDependenciesCount())) {\n                                return;\n                            }\n\n                            var modelValue = ko.dependencyDetection.ignore(valueAccessor);\n                            if (valueIsArray) {\n                                var writableValue = rawValueIsNonArrayObservable ? modelValue.peek() : modelValue,\n                                    saveOldValue = oldElemValue;\n                                oldElemValue = elemValue;\n\n                                if (saveOldValue !== elemValue) {\n                                    // When we're responding to the checkedValue changing, and the element is\n                                    // currently checked, replace the old elem value with the new elem value\n                                    // in the model array.\n                                    if (isChecked) {\n                                        ko.utils.addOrRemoveItem(writableValue, elemValue, true);\n                                        ko.utils.addOrRemoveItem(writableValue, saveOldValue, false);\n                                    }\n                                } else {\n                                    // When we're responding to the user having checked/unchecked a checkbox,\n                                    // add/remove the element value to the model array.\n                                    ko.utils.addOrRemoveItem(writableValue, elemValue, isChecked);\n                                }\n\n                                if (rawValueIsNonArrayObservable && ko.isWriteableObservable(modelValue)) {\n                                    modelValue(writableValue);\n                                }\n                            } else {\n                                if (isCheckbox) {\n                                    if (elemValue === undefined) {\n                                        elemValue = isChecked;\n                                    } else if (!isChecked) {\n                                        elemValue = undefined;\n                                    }\n                                }\n                                ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'checked', elemValue, true);\n                            }\n                        };\n\n                        function updateView() {\n                            // This updates the view value from the model value.\n                            // It runs in response to changes in the bound (checked) value.\n                            var modelValue = ko.utils.unwrapObservable(valueAccessor()),\n                                elemValue = checkedValue();\n\n                            if (valueIsArray) {\n                                // When a checkbox is bound to an array, being checked represents its value being present in that array\n                                element.checked = ko.utils.arrayIndexOf(modelValue, elemValue) >= 0;\n                                oldElemValue = elemValue;\n                            } else if (isCheckbox && elemValue === undefined) {\n                                // When a checkbox is bound to any other value (not an array) and \"checkedValue\" is not defined,\n                                // being checked represents the value being trueish\n                                element.checked = !!modelValue;\n                            } else {\n                                // Otherwise, being checked means that the checkbox or radio button's value corresponds to the model value\n                                element.checked = (checkedValue() === modelValue);\n                            }\n                        };\n\n                        var isCheckbox = element.type == \"checkbox\",\n                            isRadio = element.type == \"radio\";\n\n                        // Only bind to check boxes and radio buttons\n                        if (!isCheckbox && !isRadio) {\n                            return;\n                        }\n\n                        var rawValue = valueAccessor(),\n                            valueIsArray = isCheckbox && (ko.utils.unwrapObservable(rawValue) instanceof Array),\n                            rawValueIsNonArrayObservable = !(valueIsArray && rawValue.push && rawValue.splice),\n                            useElementValue = isRadio || valueIsArray,\n                            oldElemValue = valueIsArray ? checkedValue() : undefined;\n\n                        // IE 6 won't allow radio buttons to be selected unless they have a name\n                        if (isRadio && !element.name)\n                            ko.bindingHandlers['uniqueName']['init'](element, function() { return true });\n\n                        // Set up two computeds to update the binding:\n\n                        // The first responds to changes in the checkedValue value and to element clicks\n                        ko.computed(updateModel, null, { disposeWhenNodeIsRemoved: element });\n                        ko.utils.registerEventHandler(element, \"click\", updateModel);\n\n                        // The second responds to changes in the model value (the one associated with the checked binding)\n                        ko.computed(updateView, null, { disposeWhenNodeIsRemoved: element });\n\n                        rawValue = undefined;\n                    }\n                };\n                ko.expressionRewriting.twoWayBindings['checked'] = true;\n\n                ko.bindingHandlers['checkedValue'] = {\n                    'update': function (element, valueAccessor) {\n                        element.value = ko.utils.unwrapObservable(valueAccessor());\n                    }\n                };\n\n            })();var classesWrittenByBindingKey = '__ko__cssValue';\n            ko.bindingHandlers['class'] = {\n                'update': function (element, valueAccessor) {\n                    var value = ko.utils.stringTrim(ko.utils.unwrapObservable(valueAccessor()));\n                    ko.utils.toggleDomNodeCssClass(element, element[classesWrittenByBindingKey], false);\n                    element[classesWrittenByBindingKey] = value;\n                    ko.utils.toggleDomNodeCssClass(element, value, true);\n                }\n            };\n\n            ko.bindingHandlers['css'] = {\n                'update': function (element, valueAccessor) {\n                    var value = ko.utils.unwrapObservable(valueAccessor());\n                    if (value !== null && typeof value == \"object\") {\n                        ko.utils.objectForEach(value, function(className, shouldHaveClass) {\n                            shouldHaveClass = ko.utils.unwrapObservable(shouldHaveClass);\n                            ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);\n                        });\n                    } else {\n                        ko.bindingHandlers['class']['update'](element, valueAccessor);\n                    }\n                }\n            };\n            ko.bindingHandlers['enable'] = {\n                'update': function (element, valueAccessor) {\n                    var value = ko.utils.unwrapObservable(valueAccessor());\n                    if (value && element.disabled)\n                        element.removeAttribute(\"disabled\");\n                    else if ((!value) && (!element.disabled))\n                        element.disabled = true;\n                }\n            };\n\n            ko.bindingHandlers['disable'] = {\n                'update': function (element, valueAccessor) {\n                    ko.bindingHandlers['enable']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });\n                }\n            };\n// For certain common events (currently just 'click'), allow a simplified data-binding syntax\n// e.g. click:handler instead of the usual full-length event:{click:handler}\n            function makeEventHandlerShortcut(eventName) {\n                ko.bindingHandlers[eventName] = {\n                    'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {\n                        var newValueAccessor = function () {\n                            var result = {};\n                            result[eventName] = valueAccessor();\n                            return result;\n                        };\n                        return ko.bindingHandlers['event']['init'].call(this, element, newValueAccessor, allBindings, viewModel, bindingContext);\n                    }\n                }\n            }\n\n            ko.bindingHandlers['event'] = {\n                'init' : function (element, valueAccessor, allBindings, viewModel, bindingContext) {\n                    var eventsToHandle = valueAccessor() || {};\n                    ko.utils.objectForEach(eventsToHandle, function(eventName) {\n                        if (typeof eventName == \"string\") {\n                            ko.utils.registerEventHandler(element, eventName, function (event) {\n                                var handlerReturnValue;\n                                var handlerFunction = valueAccessor()[eventName];\n                                if (!handlerFunction)\n                                    return;\n\n                                try {\n                                    // Take all the event args, and prefix with the viewmodel\n                                    var argsForHandler = ko.utils.makeArray(arguments);\n                                    viewModel = bindingContext['$data'];\n                                    argsForHandler.unshift(viewModel);\n                                    handlerReturnValue = handlerFunction.apply(viewModel, argsForHandler);\n                                } finally {\n                                    if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.\n                                        if (event.preventDefault)\n                                            event.preventDefault();\n                                        else\n                                            event.returnValue = false;\n                                    }\n                                }\n\n                                var bubble = allBindings.get(eventName + 'Bubble') !== false;\n                                if (!bubble) {\n                                    event.cancelBubble = true;\n                                    if (event.stopPropagation)\n                                        event.stopPropagation();\n                                }\n                            });\n                        }\n                    });\n                }\n            };\n// \"foreach: someExpression\" is equivalent to \"template: { foreach: someExpression }\"\n// \"foreach: { data: someExpression, afterAdd: myfn }\" is equivalent to \"template: { foreach: someExpression, afterAdd: myfn }\"\n            ko.bindingHandlers['foreach'] = {\n                makeTemplateValueAccessor: function(valueAccessor) {\n                    return function() {\n                        var modelValue = valueAccessor(),\n                            unwrappedValue = ko.utils.peekObservable(modelValue);    // Unwrap without setting a dependency here\n\n                        // If unwrappedValue is the array, pass in the wrapped value on its own\n                        // The value will be unwrapped and tracked within the template binding\n                        // (See https://github.com/SteveSanderson/knockout/issues/523)\n                        if ((!unwrappedValue) || typeof unwrappedValue.length == \"number\")\n                            return { 'foreach': modelValue, 'templateEngine': ko.nativeTemplateEngine.instance };\n\n                        // If unwrappedValue.data is the array, preserve all relevant options and unwrap again value so we get updates\n                        ko.utils.unwrapObservable(modelValue);\n                        return {\n                            'foreach': unwrappedValue['data'],\n                            'as': unwrappedValue['as'],\n                            'noChildContext': unwrappedValue['noChildContext'],\n                            'includeDestroyed': unwrappedValue['includeDestroyed'],\n                            'afterAdd': unwrappedValue['afterAdd'],\n                            'beforeRemove': unwrappedValue['beforeRemove'],\n                            'afterRender': unwrappedValue['afterRender'],\n                            'beforeMove': unwrappedValue['beforeMove'],\n                            'afterMove': unwrappedValue['afterMove'],\n                            'templateEngine': ko.nativeTemplateEngine.instance\n                        };\n                    };\n                },\n                'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {\n                    return ko.bindingHandlers['template']['init'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor));\n                },\n                'update': function(element, valueAccessor, allBindings, viewModel, bindingContext) {\n                    return ko.bindingHandlers['template']['update'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor), allBindings, viewModel, bindingContext);\n                }\n            };\n            ko.expressionRewriting.bindingRewriteValidators['foreach'] = false; // Can't rewrite control flow bindings\n            ko.virtualElements.allowedBindings['foreach'] = true;\n            var hasfocusUpdatingProperty = '__ko_hasfocusUpdating';\n            var hasfocusLastValue = '__ko_hasfocusLastValue';\n            ko.bindingHandlers['hasfocus'] = {\n                'init': function(element, valueAccessor, allBindings) {\n                    var handleElementFocusChange = function(isFocused) {\n                        // Where possible, ignore which event was raised and determine focus state using activeElement,\n                        // as this avoids phantom focus/blur events raised when changing tabs in modern browsers.\n                        // However, not all KO-targeted browsers (Firefox 2) support activeElement. For those browsers,\n                        // prevent a loss of focus when changing tabs/windows by setting a flag that prevents hasfocus\n                        // from calling 'blur()' on the element when it loses focus.\n                        // Discussion at https://github.com/SteveSanderson/knockout/pull/352\n                        element[hasfocusUpdatingProperty] = true;\n                        var ownerDoc = element.ownerDocument;\n                        if (\"activeElement\" in ownerDoc) {\n                            var active;\n                            try {\n                                active = ownerDoc.activeElement;\n                            } catch(e) {\n                                // IE9 throws if you access activeElement during page load (see issue #703)\n                                active = ownerDoc.body;\n                            }\n                            isFocused = (active === element);\n                        }\n                        var modelValue = valueAccessor();\n                        ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'hasfocus', isFocused, true);\n\n                        //cache the latest value, so we can avoid unnecessarily calling focus/blur in the update function\n                        element[hasfocusLastValue] = isFocused;\n                        element[hasfocusUpdatingProperty] = false;\n                    };\n                    var handleElementFocusIn = handleElementFocusChange.bind(null, true);\n                    var handleElementFocusOut = handleElementFocusChange.bind(null, false);\n\n                    ko.utils.registerEventHandler(element, \"focus\", handleElementFocusIn);\n                    ko.utils.registerEventHandler(element, \"focusin\", handleElementFocusIn); // For IE\n                    ko.utils.registerEventHandler(element, \"blur\",  handleElementFocusOut);\n                    ko.utils.registerEventHandler(element, \"focusout\",  handleElementFocusOut); // For IE\n\n                    // Assume element is not focused (prevents \"blur\" being called initially)\n                    element[hasfocusLastValue] = false;\n                },\n                'update': function(element, valueAccessor) {\n                    var value = !!ko.utils.unwrapObservable(valueAccessor());\n\n                    if (!element[hasfocusUpdatingProperty] && element[hasfocusLastValue] !== value) {\n                        value ? element.focus() : element.blur();\n\n                        // In IE, the blur method doesn't always cause the element to lose focus (for example, if the window is not in focus).\n                        // Setting focus to the body element does seem to be reliable in IE, but should only be used if we know that the current\n                        // element was focused already.\n                        if (!value && element[hasfocusLastValue]) {\n                            element.ownerDocument.body.focus();\n                        }\n\n                        // For IE, which doesn't reliably fire \"focus\" or \"blur\" events synchronously\n                        ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, value ? \"focusin\" : \"focusout\"]);\n                    }\n                }\n            };\n            ko.expressionRewriting.twoWayBindings['hasfocus'] = true;\n\n            ko.bindingHandlers['hasFocus'] = ko.bindingHandlers['hasfocus']; // Make \"hasFocus\" an alias\n            ko.expressionRewriting.twoWayBindings['hasFocus'] = 'hasfocus';\n            ko.bindingHandlers['html'] = {\n                'init': function() {\n                    // Prevent binding on the dynamically-injected HTML (as developers are unlikely to expect that, and it has security implications)\n                    return { 'controlsDescendantBindings': true };\n                },\n                'update': function (element, valueAccessor) {\n                    // setHtml will unwrap the value if needed\n                    ko.utils.setHtml(element, valueAccessor());\n                }\n            };\n            (function () {\n\n// Makes a binding like with or if\n                function makeWithIfBinding(bindingKey, isWith, isNot) {\n                    ko.bindingHandlers[bindingKey] = {\n                        'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {\n                            var didDisplayOnLastUpdate, savedNodes, contextOptions = {}, completeOnRender, needAsyncContext, renderOnEveryChange;\n\n                            if (isWith) {\n                                var as = allBindings.get('as'), noChildContext = allBindings.get('noChildContext');\n                                renderOnEveryChange = !(as && noChildContext);\n                                contextOptions = { 'as': as, 'noChildContext': noChildContext, 'exportDependencies': renderOnEveryChange };\n                            }\n\n                            completeOnRender = allBindings.get(\"completeOn\") == \"render\";\n                            needAsyncContext = completeOnRender || allBindings['has'](ko.bindingEvent.descendantsComplete);\n\n                            ko.computed(function() {\n                                var value = ko.utils.unwrapObservable(valueAccessor()),\n                                    shouldDisplay = !isNot !== !value, // equivalent to isNot ? !value : !!value,\n                                    isInitial = !savedNodes,\n                                    childContext;\n\n                                if (!renderOnEveryChange && shouldDisplay === didDisplayOnLastUpdate) {\n                                    return;\n                                }\n\n                                if (needAsyncContext) {\n                                    bindingContext = ko.bindingEvent.startPossiblyAsyncContentBinding(element, bindingContext);\n                                }\n\n                                if (shouldDisplay) {\n                                    if (!isWith || renderOnEveryChange) {\n                                        contextOptions['dataDependency'] = ko.computedContext.computed();\n                                    }\n\n                                    if (isWith) {\n                                        childContext = bindingContext['createChildContext'](typeof value == \"function\" ? value : valueAccessor, contextOptions);\n                                    } else if (ko.computedContext.getDependenciesCount()) {\n                                        childContext = bindingContext['extend'](null, contextOptions);\n                                    } else {\n                                        childContext = bindingContext;\n                                    }\n                                }\n\n                                // Save a copy of the inner nodes on the initial update, but only if we have dependencies.\n                                if (isInitial && ko.computedContext.getDependenciesCount()) {\n                                    savedNodes = ko.utils.cloneNodes(ko.virtualElements.childNodes(element), true /* shouldCleanNodes */);\n                                }\n\n                                if (shouldDisplay) {\n                                    if (!isInitial) {\n                                        ko.virtualElements.setDomNodeChildren(element, ko.utils.cloneNodes(savedNodes));\n                                    }\n\n                                    ko.applyBindingsToDescendants(childContext, element);\n                                } else {\n                                    ko.virtualElements.emptyNode(element);\n\n                                    if (!completeOnRender) {\n                                        ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);\n                                    }\n                                }\n\n                                didDisplayOnLastUpdate = shouldDisplay;\n\n                            }, null, { disposeWhenNodeIsRemoved: element });\n\n                            return { 'controlsDescendantBindings': true };\n                        }\n                    };\n                    ko.expressionRewriting.bindingRewriteValidators[bindingKey] = false; // Can't rewrite control flow bindings\n                    ko.virtualElements.allowedBindings[bindingKey] = true;\n                }\n\n// Construct the actual binding handlers\n                makeWithIfBinding('if');\n                makeWithIfBinding('ifnot', false /* isWith */, true /* isNot */);\n                makeWithIfBinding('with', true /* isWith */);\n\n            })();ko.bindingHandlers['let'] = {\n                'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {\n                    // Make a modified binding context, with extra properties, and apply it to descendant elements\n                    var innerContext = bindingContext['extend'](valueAccessor);\n                    ko.applyBindingsToDescendants(innerContext, element);\n\n                    return { 'controlsDescendantBindings': true };\n                }\n            };\n            ko.virtualElements.allowedBindings['let'] = true;\n            var captionPlaceholder = {};\n            ko.bindingHandlers['options'] = {\n                'init': function(element) {\n                    if (ko.utils.tagNameLower(element) !== \"select\")\n                        throw new Error(\"options binding applies only to SELECT elements\");\n\n                    // Remove all existing <option>s.\n                    while (element.length > 0) {\n                        element.remove(0);\n                    }\n\n                    // Ensures that the binding processor doesn't try to bind the options\n                    return { 'controlsDescendantBindings': true };\n                },\n                'update': function (element, valueAccessor, allBindings) {\n                    function selectedOptions() {\n                        return ko.utils.arrayFilter(element.options, function (node) { return node.selected; });\n                    }\n\n                    var selectWasPreviouslyEmpty = element.length == 0,\n                        multiple = element.multiple,\n                        previousScrollTop = (!selectWasPreviouslyEmpty && multiple) ? element.scrollTop : null,\n                        unwrappedArray = ko.utils.unwrapObservable(valueAccessor()),\n                        valueAllowUnset = allBindings.get('valueAllowUnset') && allBindings['has']('value'),\n                        includeDestroyed = allBindings.get('optionsIncludeDestroyed'),\n                        arrayToDomNodeChildrenOptions = {},\n                        captionValue,\n                        filteredArray,\n                        previousSelectedValues = [];\n\n                    if (!valueAllowUnset) {\n                        if (multiple) {\n                            previousSelectedValues = ko.utils.arrayMap(selectedOptions(), ko.selectExtensions.readValue);\n                        } else if (element.selectedIndex >= 0) {\n                            previousSelectedValues.push(ko.selectExtensions.readValue(element.options[element.selectedIndex]));\n                        }\n                    }\n\n                    if (unwrappedArray) {\n                        if (typeof unwrappedArray.length == \"undefined\") // Coerce single value into array\n                            unwrappedArray = [unwrappedArray];\n\n                        // Filter out any entries marked as destroyed\n                        filteredArray = ko.utils.arrayFilter(unwrappedArray, function(item) {\n                            return includeDestroyed || item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);\n                        });\n\n                        // If caption is included, add it to the array\n                        if (allBindings['has']('optionsCaption')) {\n                            captionValue = ko.utils.unwrapObservable(allBindings.get('optionsCaption'));\n                            // If caption value is null or undefined, don't show a caption\n                            if (captionValue !== null && captionValue !== undefined) {\n                                filteredArray.unshift(captionPlaceholder);\n                            }\n                        }\n                    } else {\n                        // If a falsy value is provided (e.g. null), we'll simply empty the select element\n                    }\n\n                    function applyToObject(object, predicate, defaultValue) {\n                        var predicateType = typeof predicate;\n                        if (predicateType == \"function\")    // Given a function; run it against the data value\n                            return predicate(object);\n                        else if (predicateType == \"string\") // Given a string; treat it as a property name on the data value\n                            return object[predicate];\n                        else                                // Given no optionsText arg; use the data value itself\n                            return defaultValue;\n                    }\n\n                    // The following functions can run at two different times:\n                    // The first is when the whole array is being updated directly from this binding handler.\n                    // The second is when an observable value for a specific array entry is updated.\n                    // oldOptions will be empty in the first case, but will be filled with the previously generated option in the second.\n                    var itemUpdate = false;\n                    function optionForArrayItem(arrayEntry, index, oldOptions) {\n                        if (oldOptions.length) {\n                            previousSelectedValues = !valueAllowUnset && oldOptions[0].selected ? [ ko.selectExtensions.readValue(oldOptions[0]) ] : [];\n                            itemUpdate = true;\n                        }\n                        var option = element.ownerDocument.createElement(\"option\");\n                        if (arrayEntry === captionPlaceholder) {\n                            ko.utils.setTextContent(option, allBindings.get('optionsCaption'));\n                            ko.selectExtensions.writeValue(option, undefined);\n                        } else {\n                            // Apply a value to the option element\n                            var optionValue = applyToObject(arrayEntry, allBindings.get('optionsValue'), arrayEntry);\n                            ko.selectExtensions.writeValue(option, ko.utils.unwrapObservable(optionValue));\n\n                            // Apply some text to the option element\n                            var optionText = applyToObject(arrayEntry, allBindings.get('optionsText'), optionValue);\n                            ko.utils.setTextContent(option, optionText);\n                        }\n                        return [option];\n                    }\n\n                    // By using a beforeRemove callback, we delay the removal until after new items are added. This fixes a selection\n                    // problem in IE<=8 and Firefox. See https://github.com/knockout/knockout/issues/1208\n                    arrayToDomNodeChildrenOptions['beforeRemove'] =\n                        function (option) {\n                            element.removeChild(option);\n                        };\n\n                    function setSelectionCallback(arrayEntry, newOptions) {\n                        if (itemUpdate && valueAllowUnset) {\n                            // The model value is authoritative, so make sure its value is the one selected\n                            ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);\n                        } else if (previousSelectedValues.length) {\n                            // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.\n                            // That's why we first added them without selection. Now it's time to set the selection.\n                            var isSelected = ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[0])) >= 0;\n                            ko.utils.setOptionNodeSelectionState(newOptions[0], isSelected);\n\n                            // If this option was changed from being selected during a single-item update, notify the change\n                            if (itemUpdate && !isSelected) {\n                                ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, \"change\"]);\n                            }\n                        }\n                    }\n\n                    var callback = setSelectionCallback;\n                    if (allBindings['has']('optionsAfterRender') && typeof allBindings.get('optionsAfterRender') == \"function\") {\n                        callback = function(arrayEntry, newOptions) {\n                            setSelectionCallback(arrayEntry, newOptions);\n                            ko.dependencyDetection.ignore(allBindings.get('optionsAfterRender'), null, [newOptions[0], arrayEntry !== captionPlaceholder ? arrayEntry : undefined]);\n                        }\n                    }\n\n                    ko.utils.setDomNodeChildrenFromArrayMapping(element, filteredArray, optionForArrayItem, arrayToDomNodeChildrenOptions, callback);\n\n                    if (!valueAllowUnset) {\n                        // Determine if the selection has changed as a result of updating the options list\n                        var selectionChanged;\n                        if (multiple) {\n                            // For a multiple-select box, compare the new selection count to the previous one\n                            // But if nothing was selected before, the selection can't have changed\n                            selectionChanged = previousSelectedValues.length && selectedOptions().length < previousSelectedValues.length;\n                        } else {\n                            // For a single-select box, compare the current value to the previous value\n                            // But if nothing was selected before or nothing is selected now, just look for a change in selection\n                            selectionChanged = (previousSelectedValues.length && element.selectedIndex >= 0)\n                                ? (ko.selectExtensions.readValue(element.options[element.selectedIndex]) !== previousSelectedValues[0])\n                                : (previousSelectedValues.length || element.selectedIndex >= 0);\n                        }\n\n                        // Ensure consistency between model value and selected option.\n                        // If the dropdown was changed so that selection is no longer the same,\n                        // notify the value or selectedOptions binding.\n                        if (selectionChanged) {\n                            ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, \"change\"]);\n                        }\n                    }\n\n                    if (valueAllowUnset || ko.computedContext.isInitial()) {\n                        ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);\n                    }\n\n                    // Workaround for IE bug\n                    ko.utils.ensureSelectElementIsRenderedCorrectly(element);\n\n                    if (previousScrollTop && Math.abs(previousScrollTop - element.scrollTop) > 20)\n                        element.scrollTop = previousScrollTop;\n                }\n            };\n            ko.bindingHandlers['options'].optionValueDomDataKey = ko.utils.domData.nextKey();\n            ko.bindingHandlers['selectedOptions'] = {\n                'init': function (element, valueAccessor, allBindings) {\n                    function updateFromView() {\n                        var value = valueAccessor(), valueToWrite = [];\n                        ko.utils.arrayForEach(element.getElementsByTagName(\"option\"), function(node) {\n                            if (node.selected)\n                                valueToWrite.push(ko.selectExtensions.readValue(node));\n                        });\n                        ko.expressionRewriting.writeValueToProperty(value, allBindings, 'selectedOptions', valueToWrite);\n                    }\n\n                    function updateFromModel() {\n                        var newValue = ko.utils.unwrapObservable(valueAccessor()),\n                            previousScrollTop = element.scrollTop;\n\n                        if (newValue && typeof newValue.length == \"number\") {\n                            ko.utils.arrayForEach(element.getElementsByTagName(\"option\"), function(node) {\n                                var isSelected = ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0;\n                                if (node.selected != isSelected) {      // This check prevents flashing of the select element in IE\n                                    ko.utils.setOptionNodeSelectionState(node, isSelected);\n                                }\n                            });\n                        }\n\n                        element.scrollTop = previousScrollTop;\n                    }\n\n                    if (ko.utils.tagNameLower(element) != \"select\") {\n                        throw new Error(\"selectedOptions binding applies only to SELECT elements\");\n                    }\n\n                    var updateFromModelComputed;\n                    ko.bindingEvent.subscribe(element, ko.bindingEvent.childrenComplete, function () {\n                        if (!updateFromModelComputed) {\n                            ko.utils.registerEventHandler(element, \"change\", updateFromView);\n                            updateFromModelComputed = ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });\n                        } else {\n                            updateFromView();\n                        }\n                    }, null, { 'notifyImmediately': true });\n                },\n                'update': function() {} // Keep for backwards compatibility with code that may have wrapped binding\n            };\n            ko.expressionRewriting.twoWayBindings['selectedOptions'] = true;\n            ko.bindingHandlers['style'] = {\n                'update': function (element, valueAccessor) {\n                    var value = ko.utils.unwrapObservable(valueAccessor() || {});\n                    ko.utils.objectForEach(value, function(styleName, styleValue) {\n                        styleValue = ko.utils.unwrapObservable(styleValue);\n\n                        if (styleValue === null || styleValue === undefined || styleValue === false) {\n                            // Empty string removes the value, whereas null/undefined have no effect\n                            styleValue = \"\";\n                        }\n\n                        if (jQueryInstance) {\n                            jQueryInstance(element)['css'](styleName, styleValue);\n                        } else if (/^--/.test(styleName)) {\n                            // Is styleName a custom CSS property?\n                            element.style.setProperty(styleName, styleValue);\n                        } else {\n                            styleName = styleName.replace(/-(\\w)/g, function (all, letter) {\n                                return letter.toUpperCase();\n                            });\n\n                            var previousStyle = element.style[styleName];\n                            element.style[styleName] = styleValue;\n\n                            if (styleValue !== previousStyle && element.style[styleName] == previousStyle && !isNaN(styleValue)) {\n                                element.style[styleName] = styleValue + \"px\";\n                            }\n                        }\n                    });\n                }\n            };\n            ko.bindingHandlers['submit'] = {\n                'init': function (element, valueAccessor, allBindings, viewModel, bindingContext) {\n                    if (typeof valueAccessor() != \"function\")\n                        throw new Error(\"The value for a submit binding must be a function\");\n                    ko.utils.registerEventHandler(element, \"submit\", function (event) {\n                        var handlerReturnValue;\n                        var value = valueAccessor();\n                        try { handlerReturnValue = value.call(bindingContext['$data'], element); }\n                        finally {\n                            if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.\n                                if (event.preventDefault)\n                                    event.preventDefault();\n                                else\n                                    event.returnValue = false;\n                            }\n                        }\n                    });\n                }\n            };\n            ko.bindingHandlers['text'] = {\n                'init': function() {\n                    // Prevent binding on the dynamically-injected text node (as developers are unlikely to expect that, and it has security implications).\n                    // It should also make things faster, as we no longer have to consider whether the text node might be bindable.\n                    return { 'controlsDescendantBindings': true };\n                },\n                'update': function (element, valueAccessor) {\n                    ko.utils.setTextContent(element, valueAccessor());\n                }\n            };\n            ko.virtualElements.allowedBindings['text'] = true;\n            (function () {\n\n                if (window && window.navigator) {\n                    var parseVersion = function (matches) {\n                        if (matches) {\n                            return parseFloat(matches[1]);\n                        }\n                    };\n\n                    // Detect various browser versions because some old versions don't fully support the 'input' event\n                    var userAgent = window.navigator.userAgent,\n                        operaVersion, chromeVersion, safariVersion, firefoxVersion, ieVersion, edgeVersion;\n\n                    (operaVersion = window.opera && window.opera.version && parseInt(window.opera.version()))\n                    || (edgeVersion = parseVersion(userAgent.match(/Edge\\/([^ ]+)$/)))\n                    || (chromeVersion = parseVersion(userAgent.match(/Chrome\\/([^ ]+)/)))\n                    || (safariVersion = parseVersion(userAgent.match(/Version\\/([^ ]+) Safari/)))\n                    || (firefoxVersion = parseVersion(userAgent.match(/Firefox\\/([^ ]+)/)))\n                    || (ieVersion = ko.utils.ieVersion || parseVersion(userAgent.match(/MSIE ([^ ]+)/)))      // Detects up to IE 10\n                    || (ieVersion = parseVersion(userAgent.match(/rv:([^ )]+)/)));      // Detects IE 11\n                }\n\n// IE 8 and 9 have bugs that prevent the normal events from firing when the value changes.\n// But it does fire the 'selectionchange' event on many of those, presumably because the\n// cursor is moving and that counts as the selection changing. The 'selectionchange' event is\n// fired at the document level only and doesn't directly indicate which element changed. We\n// set up just one event handler for the document and use 'activeElement' to determine which\n// element was changed.\n                if (ieVersion >= 8 && ieVersion < 10) {\n                    var selectionChangeRegisteredName = ko.utils.domData.nextKey(),\n                        selectionChangeHandlerName = ko.utils.domData.nextKey();\n                    var selectionChangeHandler = function(event) {\n                        var target = this.activeElement,\n                            handler = target && ko.utils.domData.get(target, selectionChangeHandlerName);\n                        if (handler) {\n                            handler(event);\n                        }\n                    };\n                    var registerForSelectionChangeEvent = function (element, handler) {\n                        var ownerDoc = element.ownerDocument;\n                        if (!ko.utils.domData.get(ownerDoc, selectionChangeRegisteredName)) {\n                            ko.utils.domData.set(ownerDoc, selectionChangeRegisteredName, true);\n                            ko.utils.registerEventHandler(ownerDoc, 'selectionchange', selectionChangeHandler);\n                        }\n                        ko.utils.domData.set(element, selectionChangeHandlerName, handler);\n                    };\n                }\n\n                ko.bindingHandlers['textInput'] = {\n                    'init': function (element, valueAccessor, allBindings) {\n\n                        var previousElementValue = element.value,\n                            timeoutHandle,\n                            elementValueBeforeEvent;\n\n                        var updateModel = function (event) {\n                            clearTimeout(timeoutHandle);\n                            elementValueBeforeEvent = timeoutHandle = undefined;\n\n                            var elementValue = element.value;\n                            if (previousElementValue !== elementValue) {\n                                // Provide a way for tests to know exactly which event was processed\n                                if (DEBUG && event) element['_ko_textInputProcessedEvent'] = event.type;\n                                previousElementValue = elementValue;\n                                ko.expressionRewriting.writeValueToProperty(valueAccessor(), allBindings, 'textInput', elementValue);\n                            }\n                        };\n\n                        var deferUpdateModel = function (event) {\n                            if (!timeoutHandle) {\n                                // The elementValueBeforeEvent variable is set *only* during the brief gap between an\n                                // event firing and the updateModel function running. This allows us to ignore model\n                                // updates that are from the previous state of the element, usually due to techniques\n                                // such as rateLimit. Such updates, if not ignored, can cause keystrokes to be lost.\n                                elementValueBeforeEvent = element.value;\n                                var handler = DEBUG ? updateModel.bind(element, {type: event.type}) : updateModel;\n                                timeoutHandle = ko.utils.setTimeout(handler, 4);\n                            }\n                        };\n\n                        // IE9 will mess up the DOM if you handle events synchronously which results in DOM changes (such as other bindings);\n                        // so we'll make sure all updates are asynchronous\n                        var ieUpdateModel = ko.utils.ieVersion == 9 ? deferUpdateModel : updateModel,\n                            ourUpdate = false;\n\n                        var updateView = function () {\n                            var modelValue = ko.utils.unwrapObservable(valueAccessor());\n\n                            if (modelValue === null || modelValue === undefined) {\n                                modelValue = '';\n                            }\n\n                            if (elementValueBeforeEvent !== undefined && modelValue === elementValueBeforeEvent) {\n                                ko.utils.setTimeout(updateView, 4);\n                                return;\n                            }\n\n                            // Update the element only if the element and model are different. On some browsers, updating the value\n                            // will move the cursor to the end of the input, which would be bad while the user is typing.\n                            if (element.value !== modelValue) {\n                                ourUpdate = true;  // Make sure we ignore events (propertychange) that result from updating the value\n                                element.value = modelValue;\n                                ourUpdate = false;\n                                previousElementValue = element.value; // In case the browser changes the value (see #2281)\n                            }\n                        };\n\n                        var onEvent = function (event, handler) {\n                            ko.utils.registerEventHandler(element, event, handler);\n                        };\n\n                        if (DEBUG && ko.bindingHandlers['textInput']['_forceUpdateOn']) {\n                            // Provide a way for tests to specify exactly which events are bound\n                            ko.utils.arrayForEach(ko.bindingHandlers['textInput']['_forceUpdateOn'], function(eventName) {\n                                if (eventName.slice(0,5) == 'after') {\n                                    onEvent(eventName.slice(5), deferUpdateModel);\n                                } else {\n                                    onEvent(eventName, updateModel);\n                                }\n                            });\n                        } else {\n                            if (ieVersion) {\n                                // All versions (including 11) of Internet Explorer have a bug that they don't generate an input or propertychange event when ESC is pressed\n                                onEvent('keypress', updateModel);\n                            }\n                            if (ieVersion < 11) {\n                                // Internet Explorer <= 8 doesn't support the 'input' event, but does include 'propertychange' that fires whenever\n                                // any property of an element changes. Unlike 'input', it also fires if a property is changed from JavaScript code,\n                                // but that's an acceptable compromise for this binding. IE 9 and 10 support 'input', but since they don't always\n                                // fire it when using autocomplete, we'll use 'propertychange' for them also.\n                                onEvent('propertychange', function(event) {\n                                    if (!ourUpdate && event.propertyName === 'value') {\n                                        ieUpdateModel(event);\n                                    }\n                                });\n                            }\n                            if (ieVersion == 8) {\n                                // IE 8 has a bug where it fails to fire 'propertychange' on the first update following a value change from\n                                // JavaScript code. It also doesn't fire if you clear the entire value. To fix this, we bind to the following\n                                // events too.\n                                onEvent('keyup', updateModel);      // A single keystoke\n                                onEvent('keydown', updateModel);    // The first character when a key is held down\n                            }\n                            if (registerForSelectionChangeEvent) {\n                                // Internet Explorer 9 doesn't fire the 'input' event when deleting text, including using\n                                // the backspace, delete, or ctrl-x keys, clicking the 'x' to clear the input, dragging text\n                                // out of the field, and cutting or deleting text using the context menu. 'selectionchange'\n                                // can detect all of those except dragging text out of the field, for which we use 'dragend'.\n                                // These are also needed in IE8 because of the bug described above.\n                                registerForSelectionChangeEvent(element, ieUpdateModel);  // 'selectionchange' covers cut, paste, drop, delete, etc.\n                                onEvent('dragend', deferUpdateModel);\n                            }\n\n                            if (!ieVersion || ieVersion >= 9) {\n                                // All other supported browsers support the 'input' event, which fires whenever the content of the element is changed\n                                // through the user interface.\n                                onEvent('input', ieUpdateModel);\n                            }\n\n                            if (safariVersion < 5 && ko.utils.tagNameLower(element) === \"textarea\") {\n                                // Safari <5 doesn't fire the 'input' event for <textarea> elements (it does fire 'textInput'\n                                // but only when typing). So we'll just catch as much as we can with keydown, cut, and paste.\n                                onEvent('keydown', deferUpdateModel);\n                                onEvent('paste', deferUpdateModel);\n                                onEvent('cut', deferUpdateModel);\n                            } else if (operaVersion < 11) {\n                                // Opera 10 doesn't always fire the 'input' event for cut, paste, undo & drop operations.\n                                // We can try to catch some of those using 'keydown'.\n                                onEvent('keydown', deferUpdateModel);\n                            } else if (firefoxVersion < 4.0) {\n                                // Firefox <= 3.6 doesn't fire the 'input' event when text is filled in through autocomplete\n                                onEvent('DOMAutoComplete', updateModel);\n\n                                // Firefox <=3.5 doesn't fire the 'input' event when text is dropped into the input.\n                                onEvent('dragdrop', updateModel);       // <3.5\n                                onEvent('drop', updateModel);           // 3.5\n                            } else if (edgeVersion && element.type === \"number\") {\n                                // Microsoft Edge doesn't fire 'input' or 'change' events for number inputs when\n                                // the value is changed via the up / down arrow keys\n                                onEvent('keydown', deferUpdateModel);\n                            }\n                        }\n\n                        // Bind to the change event so that we can catch programmatic updates of the value that fire this event.\n                        onEvent('change', updateModel);\n\n                        // To deal with browsers that don't notify any kind of event for some changes (IE, Safari, etc.)\n                        onEvent('blur', updateModel);\n\n                        ko.computed(updateView, null, { disposeWhenNodeIsRemoved: element });\n                    }\n                };\n                ko.expressionRewriting.twoWayBindings['textInput'] = true;\n\n// textinput is an alias for textInput\n                ko.bindingHandlers['textinput'] = {\n                    // preprocess is the only way to set up a full alias\n                    'preprocess': function (value, name, addBinding) {\n                        addBinding('textInput', value);\n                    }\n                };\n\n            })();ko.bindingHandlers['uniqueName'] = {\n                'init': function (element, valueAccessor) {\n                    if (valueAccessor()) {\n                        var name = \"ko_unique_\" + (++ko.bindingHandlers['uniqueName'].currentIndex);\n                        ko.utils.setElementName(element, name);\n                    }\n                }\n            };\n            ko.bindingHandlers['uniqueName'].currentIndex = 0;\n            ko.bindingHandlers['using'] = {\n                'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {\n                    var options;\n\n                    if (allBindings['has']('as')) {\n                        options = { 'as': allBindings.get('as'), 'noChildContext': allBindings.get('noChildContext') };\n                    }\n\n                    var innerContext = bindingContext['createChildContext'](valueAccessor, options);\n                    ko.applyBindingsToDescendants(innerContext, element);\n\n                    return { 'controlsDescendantBindings': true };\n                }\n            };\n            ko.virtualElements.allowedBindings['using'] = true;\n            ko.bindingHandlers['value'] = {\n                'init': function (element, valueAccessor, allBindings) {\n                    var tagName = ko.utils.tagNameLower(element),\n                        isInputElement = tagName == \"input\";\n\n                    // If the value binding is placed on a radio/checkbox, then just pass through to checkedValue and quit\n                    if (isInputElement && (element.type == \"checkbox\" || element.type == \"radio\")) {\n                        ko.applyBindingAccessorsToNode(element, { 'checkedValue': valueAccessor });\n                        return;\n                    }\n\n                    var eventsToCatch = [];\n                    var requestedEventsToCatch = allBindings.get(\"valueUpdate\");\n                    var propertyChangedFired = false;\n                    var elementValueBeforeEvent = null;\n\n                    if (requestedEventsToCatch) {\n                        // Allow both individual event names, and arrays of event names\n                        if (typeof requestedEventsToCatch == \"string\") {\n                            eventsToCatch = [requestedEventsToCatch];\n                        } else {\n                            eventsToCatch = ko.utils.arrayGetDistinctValues(requestedEventsToCatch);\n                        }\n                        ko.utils.arrayRemoveItem(eventsToCatch, \"change\");  // We'll subscribe to \"change\" events later\n                    }\n\n                    var valueUpdateHandler = function() {\n                        elementValueBeforeEvent = null;\n                        propertyChangedFired = false;\n                        var modelValue = valueAccessor();\n                        var elementValue = ko.selectExtensions.readValue(element);\n                        ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'value', elementValue);\n                    }\n\n                    // Workaround for https://github.com/SteveSanderson/knockout/issues/122\n                    // IE doesn't fire \"change\" events on textboxes if the user selects a value from its autocomplete list\n                    var ieAutoCompleteHackNeeded = ko.utils.ieVersion && isInputElement && element.type == \"text\"\n                        && element.autocomplete != \"off\" && (!element.form || element.form.autocomplete != \"off\");\n                    if (ieAutoCompleteHackNeeded && ko.utils.arrayIndexOf(eventsToCatch, \"propertychange\") == -1) {\n                        ko.utils.registerEventHandler(element, \"propertychange\", function () { propertyChangedFired = true });\n                        ko.utils.registerEventHandler(element, \"focus\", function () { propertyChangedFired = false });\n                        ko.utils.registerEventHandler(element, \"blur\", function() {\n                            if (propertyChangedFired) {\n                                valueUpdateHandler();\n                            }\n                        });\n                    }\n\n                    ko.utils.arrayForEach(eventsToCatch, function(eventName) {\n                        // The syntax \"after<eventname>\" means \"run the handler asynchronously after the event\"\n                        // This is useful, for example, to catch \"keydown\" events after the browser has updated the control\n                        // (otherwise, ko.selectExtensions.readValue(this) will receive the control's value *before* the key event)\n                        var handler = valueUpdateHandler;\n                        if (ko.utils.stringStartsWith(eventName, \"after\")) {\n                            handler = function() {\n                                // The elementValueBeforeEvent variable is non-null *only* during the brief gap between\n                                // a keyX event firing and the valueUpdateHandler running, which is scheduled to happen\n                                // at the earliest asynchronous opportunity. We store this temporary information so that\n                                // if, between keyX and valueUpdateHandler, the underlying model value changes separately,\n                                // we can overwrite that model value change with the value the user just typed. Otherwise,\n                                // techniques like rateLimit can trigger model changes at critical moments that will\n                                // override the user's inputs, causing keystrokes to be lost.\n                                elementValueBeforeEvent = ko.selectExtensions.readValue(element);\n                                ko.utils.setTimeout(valueUpdateHandler, 0);\n                            };\n                            eventName = eventName.substring(\"after\".length);\n                        }\n                        ko.utils.registerEventHandler(element, eventName, handler);\n                    });\n\n                    var updateFromModel;\n\n                    if (isInputElement && element.type == \"file\") {\n                        // For file input elements, can only write the empty string\n                        updateFromModel = function () {\n                            var newValue = ko.utils.unwrapObservable(valueAccessor());\n                            if (newValue === null || newValue === undefined || newValue === \"\") {\n                                element.value = \"\";\n                            } else {\n                                ko.dependencyDetection.ignore(valueUpdateHandler);  // reset the model to match the element\n                            }\n                        }\n                    } else {\n                        updateFromModel = function () {\n                            var newValue = ko.utils.unwrapObservable(valueAccessor());\n                            var elementValue = ko.selectExtensions.readValue(element);\n\n                            if (elementValueBeforeEvent !== null && newValue === elementValueBeforeEvent) {\n                                ko.utils.setTimeout(updateFromModel, 0);\n                                return;\n                            }\n\n                            var valueHasChanged = newValue !== elementValue;\n\n                            if (valueHasChanged || elementValue === undefined) {\n                                if (tagName === \"select\") {\n                                    var allowUnset = allBindings.get('valueAllowUnset');\n                                    ko.selectExtensions.writeValue(element, newValue, allowUnset);\n                                    if (!allowUnset && newValue !== ko.selectExtensions.readValue(element)) {\n                                        // If you try to set a model value that can't be represented in an already-populated dropdown, reject that change,\n                                        // because you're not allowed to have a model value that disagrees with a visible UI selection.\n                                        ko.dependencyDetection.ignore(valueUpdateHandler);\n                                    }\n                                } else {\n                                    ko.selectExtensions.writeValue(element, newValue);\n                                }\n                            }\n                        };\n                    }\n\n                    if (tagName === \"select\") {\n                        var updateFromModelComputed;\n                        ko.bindingEvent.subscribe(element, ko.bindingEvent.childrenComplete, function () {\n                            if (!updateFromModelComputed) {\n                                ko.utils.registerEventHandler(element, \"change\", valueUpdateHandler);\n                                updateFromModelComputed = ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });\n                            } else if (allBindings.get('valueAllowUnset')) {\n                                updateFromModel();\n                            } else {\n                                valueUpdateHandler();\n                            }\n                        }, null, { 'notifyImmediately': true });\n                    } else {\n                        ko.utils.registerEventHandler(element, \"change\", valueUpdateHandler);\n                        ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });\n                    }\n                },\n                'update': function() {} // Keep for backwards compatibility with code that may have wrapped value binding\n            };\n            ko.expressionRewriting.twoWayBindings['value'] = true;\n            ko.bindingHandlers['visible'] = {\n                'update': function (element, valueAccessor) {\n                    var value = ko.utils.unwrapObservable(valueAccessor());\n                    var isCurrentlyVisible = !(element.style.display == \"none\");\n                    if (value && !isCurrentlyVisible)\n                        element.style.display = \"\";\n                    else if ((!value) && isCurrentlyVisible)\n                        element.style.display = \"none\";\n                }\n            };\n\n            ko.bindingHandlers['hidden'] = {\n                'update': function (element, valueAccessor) {\n                    ko.bindingHandlers['visible']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });\n                }\n            };\n// 'click' is just a shorthand for the usual full-length event:{click:handler}\n            makeEventHandlerShortcut('click');\n// If you want to make a custom template engine,\n//\n// [1] Inherit from this class (like ko.nativeTemplateEngine does)\n// [2] Override 'renderTemplateSource', supplying a function with this signature:\n//\n//        function (templateSource, bindingContext, options) {\n//            // - templateSource.text() is the text of the template you should render\n//            // - bindingContext.$data is the data you should pass into the template\n//            //   - you might also want to make bindingContext.$parent, bindingContext.$parents,\n//            //     and bindingContext.$root available in the template too\n//            // - options gives you access to any other properties set on \"data-bind: { template: options }\"\n//            // - templateDocument is the document object of the template\n//            //\n//            // Return value: an array of DOM nodes\n//        }\n//\n// [3] Override 'createJavaScriptEvaluatorBlock', supplying a function with this signature:\n//\n//        function (script) {\n//            // Return value: Whatever syntax means \"Evaluate the JavaScript statement 'script' and output the result\"\n//            //               For example, the jquery.tmpl template engine converts 'someScript' to '${ someScript }'\n//        }\n//\n//     This is only necessary if you want to allow data-bind attributes to reference arbitrary template variables.\n//     If you don't want to allow that, you can set the property 'allowTemplateRewriting' to false (like ko.nativeTemplateEngine does)\n//     and then you don't need to override 'createJavaScriptEvaluatorBlock'.\n\n            ko.templateEngine = function () { };\n\n            ko.templateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options, templateDocument) {\n                throw new Error(\"Override renderTemplateSource\");\n            };\n\n            ko.templateEngine.prototype['createJavaScriptEvaluatorBlock'] = function (script) {\n                throw new Error(\"Override createJavaScriptEvaluatorBlock\");\n            };\n\n            ko.templateEngine.prototype['makeTemplateSource'] = function(template, templateDocument) {\n                // Named template\n                if (typeof template == \"string\") {\n                    templateDocument = templateDocument || document;\n                    var elem = templateDocument.getElementById(template);\n                    if (!elem)\n                        throw new Error(\"Cannot find template with ID \" + template);\n                    return new ko.templateSources.domElement(elem);\n                } else if ((template.nodeType == 1) || (template.nodeType == 8)) {\n                    // Anonymous template\n                    return new ko.templateSources.anonymousTemplate(template);\n                } else\n                    throw new Error(\"Unknown template type: \" + template);\n            };\n\n            ko.templateEngine.prototype['renderTemplate'] = function (template, bindingContext, options, templateDocument) {\n                var templateSource = this['makeTemplateSource'](template, templateDocument);\n                return this['renderTemplateSource'](templateSource, bindingContext, options, templateDocument);\n            };\n\n            ko.templateEngine.prototype['isTemplateRewritten'] = function (template, templateDocument) {\n                // Skip rewriting if requested\n                if (this['allowTemplateRewriting'] === false)\n                    return true;\n                return this['makeTemplateSource'](template, templateDocument)['data'](\"isRewritten\");\n            };\n\n            ko.templateEngine.prototype['rewriteTemplate'] = function (template, rewriterCallback, templateDocument) {\n                var templateSource = this['makeTemplateSource'](template, templateDocument);\n                var rewritten = rewriterCallback(templateSource['text']());\n                templateSource['text'](rewritten);\n                templateSource['data'](\"isRewritten\", true);\n            };\n\n            ko.exportSymbol('templateEngine', ko.templateEngine);\n\n            ko.templateRewriting = (function () {\n                var memoizeDataBindingAttributeSyntaxRegex = /(<([a-z]+\\d*)(?:\\s+(?!data-bind\\s*=\\s*)[a-z0-9\\-]+(?:=(?:\\\"[^\\\"]*\\\"|\\'[^\\']*\\'|[^>]*))?)*\\s+)data-bind\\s*=\\s*([\"'])([\\s\\S]*?)\\3/gi;\n                var memoizeVirtualContainerBindingSyntaxRegex = /<!--\\s*ko\\b\\s*([\\s\\S]*?)\\s*-->/g;\n\n                function validateDataBindValuesForRewriting(keyValueArray) {\n                    var allValidators = ko.expressionRewriting.bindingRewriteValidators;\n                    for (var i = 0; i < keyValueArray.length; i++) {\n                        var key = keyValueArray[i]['key'];\n                        if (Object.prototype.hasOwnProperty.call(allValidators, key)) {\n                            var validator = allValidators[key];\n\n                            if (typeof validator === \"function\") {\n                                var possibleErrorMessage = validator(keyValueArray[i]['value']);\n                                if (possibleErrorMessage)\n                                    throw new Error(possibleErrorMessage);\n                            } else if (!validator) {\n                                throw new Error(\"This template engine does not support the '\" + key + \"' binding within its templates\");\n                            }\n                        }\n                    }\n                }\n\n                function constructMemoizedTagReplacement(dataBindAttributeValue, tagToRetain, nodeName, templateEngine) {\n                    var dataBindKeyValueArray = ko.expressionRewriting.parseObjectLiteral(dataBindAttributeValue);\n                    validateDataBindValuesForRewriting(dataBindKeyValueArray);\n                    var rewrittenDataBindAttributeValue = ko.expressionRewriting.preProcessBindings(dataBindKeyValueArray, {'valueAccessors':true});\n\n                    // For no obvious reason, Opera fails to evaluate rewrittenDataBindAttributeValue unless it's wrapped in an additional\n                    // anonymous function, even though Opera's built-in debugger can evaluate it anyway. No other browser requires this\n                    // extra indirection.\n                    var applyBindingsToNextSiblingScript =\n                        \"ko.__tr_ambtns(function($context,$element){return(function(){return{ \" + rewrittenDataBindAttributeValue + \" } })()},'\" + nodeName.toLowerCase() + \"')\";\n                    return templateEngine['createJavaScriptEvaluatorBlock'](applyBindingsToNextSiblingScript) + tagToRetain;\n                }\n\n                return {\n                    ensureTemplateIsRewritten: function (template, templateEngine, templateDocument) {\n                        if (!templateEngine['isTemplateRewritten'](template, templateDocument))\n                            templateEngine['rewriteTemplate'](template, function (htmlString) {\n                                return ko.templateRewriting.memoizeBindingAttributeSyntax(htmlString, templateEngine);\n                            }, templateDocument);\n                    },\n\n                    memoizeBindingAttributeSyntax: function (htmlString, templateEngine) {\n                        return htmlString.replace(memoizeDataBindingAttributeSyntaxRegex, function () {\n                            return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[4], /* tagToRetain: */ arguments[1], /* nodeName: */ arguments[2], templateEngine);\n                        }).replace(memoizeVirtualContainerBindingSyntaxRegex, function() {\n                            return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[1], /* tagToRetain: */ \"<!-- ko -->\", /* nodeName: */ \"#comment\", templateEngine);\n                        });\n                    },\n\n                    applyMemoizedBindingsToNextSibling: function (bindings, nodeName) {\n                        return ko.memoization.memoize(function (domNode, bindingContext) {\n                            var nodeToBind = domNode.nextSibling;\n                            if (nodeToBind && nodeToBind.nodeName.toLowerCase() === nodeName) {\n                                ko.applyBindingAccessorsToNode(nodeToBind, bindings, bindingContext);\n                            }\n                        });\n                    }\n                }\n            })();\n\n\n// Exported only because it has to be referenced by string lookup from within rewritten template\n            ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextSibling);\n            (function() {\n                // A template source represents a read/write way of accessing a template. This is to eliminate the need for template loading/saving\n                // logic to be duplicated in every template engine (and means they can all work with anonymous templates, etc.)\n                //\n                // Two are provided by default:\n                //  1. ko.templateSources.domElement       - reads/writes the text content of an arbitrary DOM element\n                //  2. ko.templateSources.anonymousElement - uses ko.utils.domData to read/write text *associated* with the DOM element, but\n                //                                           without reading/writing the actual element text content, since it will be overwritten\n                //                                           with the rendered template output.\n                // You can implement your own template source if you want to fetch/store templates somewhere other than in DOM elements.\n                // Template sources need to have the following functions:\n                //   text() \t\t\t- returns the template text from your storage location\n                //   text(value)\t\t- writes the supplied template text to your storage location\n                //   data(key)\t\t\t- reads values stored using data(key, value) - see below\n                //   data(key, value)\t- associates \"value\" with this template and the key \"key\". Is used to store information like \"isRewritten\".\n                //\n                // Optionally, template sources can also have the following functions:\n                //   nodes()            - returns a DOM element containing the nodes of this template, where available\n                //   nodes(value)       - writes the given DOM element to your storage location\n                // If a DOM element is available for a given template source, template engines are encouraged to use it in preference over text()\n                // for improved speed. However, all templateSources must supply text() even if they don't supply nodes().\n                //\n                // Once you've implemented a templateSource, make your template engine use it by subclassing whatever template engine you were\n                // using and overriding \"makeTemplateSource\" to return an instance of your custom template source.\n\n                ko.templateSources = {};\n\n                // ---- ko.templateSources.domElement -----\n\n                // template types\n                var templateScript = 1,\n                    templateTextArea = 2,\n                    templateTemplate = 3,\n                    templateElement = 4;\n\n                ko.templateSources.domElement = function(element) {\n                    this.domElement = element;\n\n                    if (element) {\n                        var tagNameLower = ko.utils.tagNameLower(element);\n                        this.templateType =\n                            tagNameLower === \"script\" ? templateScript :\n                                tagNameLower === \"textarea\" ? templateTextArea :\n                                    // For browsers with proper <template> element support, where the .content property gives a document fragment\n                                    tagNameLower == \"template\" && element.content && element.content.nodeType === 11 ? templateTemplate :\n                                        templateElement;\n                    }\n                }\n\n                ko.templateSources.domElement.prototype['text'] = function(/* valueToWrite */) {\n                    var elemContentsProperty = this.templateType === templateScript ? \"text\"\n                        : this.templateType === templateTextArea ? \"value\"\n                            : \"innerHTML\";\n\n                    if (arguments.length == 0) {\n                        return this.domElement[elemContentsProperty];\n                    } else {\n                        var valueToWrite = arguments[0];\n                        if (elemContentsProperty === \"innerHTML\")\n                            ko.utils.setHtml(this.domElement, valueToWrite);\n                        else\n                            this.domElement[elemContentsProperty] = valueToWrite;\n                    }\n                };\n\n                var dataDomDataPrefix = ko.utils.domData.nextKey() + \"_\";\n                ko.templateSources.domElement.prototype['data'] = function(key /*, valueToWrite */) {\n                    if (arguments.length === 1) {\n                        return ko.utils.domData.get(this.domElement, dataDomDataPrefix + key);\n                    } else {\n                        ko.utils.domData.set(this.domElement, dataDomDataPrefix + key, arguments[1]);\n                    }\n                };\n\n                var templatesDomDataKey = ko.utils.domData.nextKey();\n                function getTemplateDomData(element) {\n                    return ko.utils.domData.get(element, templatesDomDataKey) || {};\n                }\n                function setTemplateDomData(element, data) {\n                    ko.utils.domData.set(element, templatesDomDataKey, data);\n                }\n\n                ko.templateSources.domElement.prototype['nodes'] = function(/* valueToWrite */) {\n                    var element = this.domElement;\n                    if (arguments.length == 0) {\n                        var templateData = getTemplateDomData(element),\n                            nodes = templateData.containerData || (\n                                this.templateType === templateTemplate ? element.content :\n                                    this.templateType === templateElement ? element :\n                                        undefined);\n                        if (!nodes || templateData.alwaysCheckText) {\n                            // If the template is associated with an element that stores the template as text,\n                            // parse and cache the nodes whenever there's new text content available. This allows\n                            // the user to update the template content by updating the text of template node.\n                            var text = this['text']();\n                            if (text && text !== templateData.textData) {\n                                nodes = ko.utils.parseHtmlForTemplateNodes(text, element.ownerDocument);\n                                setTemplateDomData(element, {containerData: nodes, textData: text, alwaysCheckText: true});\n                            }\n                        }\n                        return nodes;\n                    } else {\n                        var valueToWrite = arguments[0];\n                        if (this.templateType !== undefined) {\n                            this['text'](\"\");   // clear the text from the node\n                        }\n                        setTemplateDomData(element, {containerData: valueToWrite});\n                    }\n                };\n\n                // ---- ko.templateSources.anonymousTemplate -----\n                // Anonymous templates are normally saved/retrieved as DOM nodes through \"nodes\".\n                // For compatibility, you can also read \"text\"; it will be serialized from the nodes on demand.\n                // Writing to \"text\" is still supported, but then the template data will not be available as DOM nodes.\n\n                ko.templateSources.anonymousTemplate = function(element) {\n                    this.domElement = element;\n                }\n                ko.templateSources.anonymousTemplate.prototype = new ko.templateSources.domElement();\n                ko.templateSources.anonymousTemplate.prototype.constructor = ko.templateSources.anonymousTemplate;\n                ko.templateSources.anonymousTemplate.prototype['text'] = function(/* valueToWrite */) {\n                    if (arguments.length == 0) {\n                        var templateData = getTemplateDomData(this.domElement);\n                        if (templateData.textData === undefined && templateData.containerData)\n                            templateData.textData = templateData.containerData.innerHTML;\n                        return templateData.textData;\n                    } else {\n                        var valueToWrite = arguments[0];\n                        setTemplateDomData(this.domElement, {textData: valueToWrite});\n                    }\n                };\n\n                ko.exportSymbol('templateSources', ko.templateSources);\n                ko.exportSymbol('templateSources.domElement', ko.templateSources.domElement);\n                ko.exportSymbol('templateSources.anonymousTemplate', ko.templateSources.anonymousTemplate);\n            })();\n            (function () {\n                var _templateEngine;\n                ko.setTemplateEngine = function (templateEngine) {\n                    if ((templateEngine != undefined) && !(templateEngine instanceof ko.templateEngine))\n                        throw new Error(\"templateEngine must inherit from ko.templateEngine\");\n                    _templateEngine = templateEngine;\n                }\n\n                function invokeForEachNodeInContinuousRange(firstNode, lastNode, action) {\n                    var node, nextInQueue = firstNode, firstOutOfRangeNode = ko.virtualElements.nextSibling(lastNode);\n                    while (nextInQueue && ((node = nextInQueue) !== firstOutOfRangeNode)) {\n                        nextInQueue = ko.virtualElements.nextSibling(node);\n                        action(node, nextInQueue);\n                    }\n                }\n\n                function activateBindingsOnContinuousNodeArray(continuousNodeArray, bindingContext) {\n                    // To be used on any nodes that have been rendered by a template and have been inserted into some parent element\n                    // Walks through continuousNodeArray (which *must* be continuous, i.e., an uninterrupted sequence of sibling nodes, because\n                    // the algorithm for walking them relies on this), and for each top-level item in the virtual-element sense,\n                    // (1) Does a regular \"applyBindings\" to associate bindingContext with this node and to activate any non-memoized bindings\n                    // (2) Unmemoizes any memos in the DOM subtree (e.g., to activate bindings that had been memoized during template rewriting)\n\n                    if (continuousNodeArray.length) {\n                        var firstNode = continuousNodeArray[0],\n                            lastNode = continuousNodeArray[continuousNodeArray.length - 1],\n                            parentNode = firstNode.parentNode,\n                            provider = ko.bindingProvider['instance'],\n                            preprocessNode = provider['preprocessNode'];\n\n                        if (preprocessNode) {\n                            invokeForEachNodeInContinuousRange(firstNode, lastNode, function(node, nextNodeInRange) {\n                                var nodePreviousSibling = node.previousSibling;\n                                var newNodes = preprocessNode.call(provider, node);\n                                if (newNodes) {\n                                    if (node === firstNode)\n                                        firstNode = newNodes[0] || nextNodeInRange;\n                                    if (node === lastNode)\n                                        lastNode = newNodes[newNodes.length - 1] || nodePreviousSibling;\n                                }\n                            });\n\n                            // Because preprocessNode can change the nodes, including the first and last nodes, update continuousNodeArray to match.\n                            // We need the full set, including inner nodes, because the unmemoize step might remove the first node (and so the real\n                            // first node needs to be in the array).\n                            continuousNodeArray.length = 0;\n                            if (!firstNode) { // preprocessNode might have removed all the nodes, in which case there's nothing left to do\n                                return;\n                            }\n                            if (firstNode === lastNode) {\n                                continuousNodeArray.push(firstNode);\n                            } else {\n                                continuousNodeArray.push(firstNode, lastNode);\n                                ko.utils.fixUpContinuousNodeArray(continuousNodeArray, parentNode);\n                            }\n                        }\n\n                        // Need to applyBindings *before* unmemoziation, because unmemoization might introduce extra nodes (that we don't want to re-bind)\n                        // whereas a regular applyBindings won't introduce new memoized nodes\n                        invokeForEachNodeInContinuousRange(firstNode, lastNode, function(node) {\n                            if (node.nodeType === 1 || node.nodeType === 8)\n                                ko.applyBindings(bindingContext, node);\n                        });\n                        invokeForEachNodeInContinuousRange(firstNode, lastNode, function(node) {\n                            if (node.nodeType === 1 || node.nodeType === 8)\n                                ko.memoization.unmemoizeDomNodeAndDescendants(node, [bindingContext]);\n                        });\n\n                        // Make sure any changes done by applyBindings or unmemoize are reflected in the array\n                        ko.utils.fixUpContinuousNodeArray(continuousNodeArray, parentNode);\n                    }\n                }\n\n                function getFirstNodeFromPossibleArray(nodeOrNodeArray) {\n                    return nodeOrNodeArray.nodeType ? nodeOrNodeArray\n                        : nodeOrNodeArray.length > 0 ? nodeOrNodeArray[0]\n                            : null;\n                }\n\n                function executeTemplate(targetNodeOrNodeArray, renderMode, template, bindingContext, options) {\n                    options = options || {};\n                    var firstTargetNode = targetNodeOrNodeArray && getFirstNodeFromPossibleArray(targetNodeOrNodeArray);\n                    var templateDocument = (firstTargetNode || template || {}).ownerDocument;\n                    var templateEngineToUse = (options['templateEngine'] || _templateEngine);\n                    ko.templateRewriting.ensureTemplateIsRewritten(template, templateEngineToUse, templateDocument);\n                    var renderedNodesArray = templateEngineToUse['renderTemplate'](template, bindingContext, options, templateDocument);\n\n                    // Loosely check result is an array of DOM nodes\n                    if ((typeof renderedNodesArray.length != \"number\") || (renderedNodesArray.length > 0 && typeof renderedNodesArray[0].nodeType != \"number\"))\n                        throw new Error(\"Template engine must return an array of DOM nodes\");\n\n                    var haveAddedNodesToParent = false;\n                    switch (renderMode) {\n                        case \"replaceChildren\":\n                            ko.virtualElements.setDomNodeChildren(targetNodeOrNodeArray, renderedNodesArray);\n                            haveAddedNodesToParent = true;\n                            break;\n                        case \"replaceNode\":\n                            ko.utils.replaceDomNodes(targetNodeOrNodeArray, renderedNodesArray);\n                            haveAddedNodesToParent = true;\n                            break;\n                        case \"ignoreTargetNode\": break;\n                        default:\n                            throw new Error(\"Unknown renderMode: \" + renderMode);\n                    }\n\n                    if (haveAddedNodesToParent) {\n                        activateBindingsOnContinuousNodeArray(renderedNodesArray, bindingContext);\n                        if (options['afterRender']) {\n                            ko.dependencyDetection.ignore(options['afterRender'], null, [renderedNodesArray, bindingContext[options['as'] || '$data']]);\n                        }\n                        if (renderMode == \"replaceChildren\") {\n                            ko.bindingEvent.notify(targetNodeOrNodeArray, ko.bindingEvent.childrenComplete);\n                        }\n                    }\n\n                    return renderedNodesArray;\n                }\n\n                function resolveTemplateName(template, data, context) {\n                    // The template can be specified as:\n                    if (ko.isObservable(template)) {\n                        // 1. An observable, with string value\n                        return template();\n                    } else if (typeof template === 'function') {\n                        // 2. A function of (data, context) returning a string\n                        return template(data, context);\n                    } else {\n                        // 3. A string\n                        return template;\n                    }\n                }\n\n                ko.renderTemplate = function (template, dataOrBindingContext, options, targetNodeOrNodeArray, renderMode) {\n                    options = options || {};\n                    if ((options['templateEngine'] || _templateEngine) == undefined)\n                        throw new Error(\"Set a template engine before calling renderTemplate\");\n                    renderMode = renderMode || \"replaceChildren\";\n\n                    if (targetNodeOrNodeArray) {\n                        var firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);\n\n                        var whenToDispose = function () { return (!firstTargetNode) || !ko.utils.domNodeIsAttachedToDocument(firstTargetNode); }; // Passive disposal (on next evaluation)\n                        var activelyDisposeWhenNodeIsRemoved = (firstTargetNode && renderMode == \"replaceNode\") ? firstTargetNode.parentNode : firstTargetNode;\n\n                        return ko.dependentObservable( // So the DOM is automatically updated when any dependency changes\n                            function () {\n                                // Ensure we've got a proper binding context to work with\n                                var bindingContext = (dataOrBindingContext && (dataOrBindingContext instanceof ko.bindingContext))\n                                    ? dataOrBindingContext\n                                    : new ko.bindingContext(dataOrBindingContext, null, null, null, { \"exportDependencies\": true });\n\n                                var templateName = resolveTemplateName(template, bindingContext['$data'], bindingContext),\n                                    renderedNodesArray = executeTemplate(targetNodeOrNodeArray, renderMode, templateName, bindingContext, options);\n\n                                if (renderMode == \"replaceNode\") {\n                                    targetNodeOrNodeArray = renderedNodesArray;\n                                    firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);\n                                }\n                            },\n                            null,\n                            { disposeWhen: whenToDispose, disposeWhenNodeIsRemoved: activelyDisposeWhenNodeIsRemoved }\n                        );\n                    } else {\n                        // We don't yet have a DOM node to evaluate, so use a memo and render the template later when there is a DOM node\n                        return ko.memoization.memoize(function (domNode) {\n                            ko.renderTemplate(template, dataOrBindingContext, options, domNode, \"replaceNode\");\n                        });\n                    }\n                };\n\n                ko.renderTemplateForEach = function (template, arrayOrObservableArray, options, targetNode, parentBindingContext) {\n                    // Since setDomNodeChildrenFromArrayMapping always calls executeTemplateForArrayItem and then\n                    // activateBindingsCallback for added items, we can store the binding context in the former to use in the latter.\n                    var arrayItemContext, asName = options['as'];\n\n                    // This will be called by setDomNodeChildrenFromArrayMapping to get the nodes to add to targetNode\n                    var executeTemplateForArrayItem = function (arrayValue, index) {\n                        // Support selecting template as a function of the data being rendered\n                        arrayItemContext = parentBindingContext['createChildContext'](arrayValue, {\n                            'as': asName,\n                            'noChildContext': options['noChildContext'],\n                            'extend': function(context) {\n                                context['$index'] = index;\n                                if (asName) {\n                                    context[asName + \"Index\"] = index;\n                                }\n                            }\n                        });\n\n                        var templateName = resolveTemplateName(template, arrayValue, arrayItemContext);\n                        return executeTemplate(targetNode, \"ignoreTargetNode\", templateName, arrayItemContext, options);\n                    };\n\n                    // This will be called whenever setDomNodeChildrenFromArrayMapping has added nodes to targetNode\n                    var activateBindingsCallback = function(arrayValue, addedNodesArray, index) {\n                        activateBindingsOnContinuousNodeArray(addedNodesArray, arrayItemContext);\n                        if (options['afterRender'])\n                            options['afterRender'](addedNodesArray, arrayValue);\n\n                        // release the \"cache\" variable, so that it can be collected by\n                        // the GC when its value isn't used from within the bindings anymore.\n                        arrayItemContext = null;\n                    };\n\n                    var setDomNodeChildrenFromArrayMapping = function (newArray, changeList) {\n                        // Call setDomNodeChildrenFromArrayMapping, ignoring any observables unwrapped within (most likely from a callback function).\n                        // If the array items are observables, though, they will be unwrapped in executeTemplateForArrayItem and managed within setDomNodeChildrenFromArrayMapping.\n                        ko.dependencyDetection.ignore(ko.utils.setDomNodeChildrenFromArrayMapping, null, [targetNode, newArray, executeTemplateForArrayItem, options, activateBindingsCallback, changeList]);\n                        ko.bindingEvent.notify(targetNode, ko.bindingEvent.childrenComplete);\n                    };\n\n                    var shouldHideDestroyed = (options['includeDestroyed'] === false) || (ko.options['foreachHidesDestroyed'] && !options['includeDestroyed']);\n\n                    if (!shouldHideDestroyed && !options['beforeRemove'] && ko.isObservableArray(arrayOrObservableArray)) {\n                        setDomNodeChildrenFromArrayMapping(arrayOrObservableArray.peek());\n\n                        var subscription = arrayOrObservableArray.subscribe(function (changeList) {\n                            setDomNodeChildrenFromArrayMapping(arrayOrObservableArray(), changeList);\n                        }, null, \"arrayChange\");\n                        subscription.disposeWhenNodeIsRemoved(targetNode);\n\n                        return subscription;\n                    } else {\n                        return ko.dependentObservable(function () {\n                            var unwrappedArray = ko.utils.unwrapObservable(arrayOrObservableArray) || [];\n                            if (typeof unwrappedArray.length == \"undefined\") // Coerce single value into array\n                                unwrappedArray = [unwrappedArray];\n\n                            if (shouldHideDestroyed) {\n                                // Filter out any entries marked as destroyed\n                                unwrappedArray = ko.utils.arrayFilter(unwrappedArray, function(item) {\n                                    return item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);\n                                });\n                            }\n                            setDomNodeChildrenFromArrayMapping(unwrappedArray);\n\n                        }, null, { disposeWhenNodeIsRemoved: targetNode });\n                    }\n                };\n\n                var templateComputedDomDataKey = ko.utils.domData.nextKey();\n                function disposeOldComputedAndStoreNewOne(element, newComputed) {\n                    var oldComputed = ko.utils.domData.get(element, templateComputedDomDataKey);\n                    if (oldComputed && (typeof(oldComputed.dispose) == 'function'))\n                        oldComputed.dispose();\n                    ko.utils.domData.set(element, templateComputedDomDataKey, (newComputed && (!newComputed.isActive || newComputed.isActive())) ? newComputed : undefined);\n                }\n\n                var cleanContainerDomDataKey = ko.utils.domData.nextKey();\n                ko.bindingHandlers['template'] = {\n                    'init': function(element, valueAccessor) {\n                        // Support anonymous templates\n                        var bindingValue = ko.utils.unwrapObservable(valueAccessor());\n                        if (typeof bindingValue == \"string\" || 'name' in bindingValue) {\n                            // It's a named template - clear the element\n                            ko.virtualElements.emptyNode(element);\n                        } else if ('nodes' in bindingValue) {\n                            // We've been given an array of DOM nodes. Save them as the template source.\n                            // There is no known use case for the node array being an observable array (if the output\n                            // varies, put that behavior *into* your template - that's what templates are for), and\n                            // the implementation would be a mess, so assert that it's not observable.\n                            var nodes = bindingValue['nodes'] || [];\n                            if (ko.isObservable(nodes)) {\n                                throw new Error('The \"nodes\" option must be a plain, non-observable array.');\n                            }\n\n                            // If the nodes are already attached to a KO-generated container, we reuse that container without moving the\n                            // elements to a new one (we check only the first node, as the nodes are always moved together)\n                            var container = nodes[0] && nodes[0].parentNode;\n                            if (!container || !ko.utils.domData.get(container, cleanContainerDomDataKey)) {\n                                container = ko.utils.moveCleanedNodesToContainerElement(nodes);\n                                ko.utils.domData.set(container, cleanContainerDomDataKey, true);\n                            }\n\n                            new ko.templateSources.anonymousTemplate(element)['nodes'](container);\n                        } else {\n                            // It's an anonymous template - store the element contents, then clear the element\n                            var templateNodes = ko.virtualElements.childNodes(element);\n                            if (templateNodes.length > 0) {\n                                var container = ko.utils.moveCleanedNodesToContainerElement(templateNodes); // This also removes the nodes from their current parent\n                                new ko.templateSources.anonymousTemplate(element)['nodes'](container);\n                            } else {\n                                throw new Error(\"Anonymous template defined, but no template content was provided\");\n                            }\n                        }\n                        return { 'controlsDescendantBindings': true };\n                    },\n                    'update': function (element, valueAccessor, allBindings, viewModel, bindingContext) {\n                        var value = valueAccessor(),\n                            options = ko.utils.unwrapObservable(value),\n                            shouldDisplay = true,\n                            templateComputed = null,\n                            template;\n\n                        if (typeof options == \"string\") {\n                            template = value;\n                            options = {};\n                        } else {\n                            template = 'name' in options ? options['name'] : element;\n\n                            // Support \"if\"/\"ifnot\" conditions\n                            if ('if' in options)\n                                shouldDisplay = ko.utils.unwrapObservable(options['if']);\n                            if (shouldDisplay && 'ifnot' in options)\n                                shouldDisplay = !ko.utils.unwrapObservable(options['ifnot']);\n\n                            // Don't show anything if an empty name is given (see #2446)\n                            if (shouldDisplay && !template) {\n                                shouldDisplay = false;\n                            }\n                        }\n\n                        if ('foreach' in options) {\n                            // Render once for each data point (treating data set as empty if shouldDisplay==false)\n                            var dataArray = (shouldDisplay && options['foreach']) || [];\n                            templateComputed = ko.renderTemplateForEach(template, dataArray, options, element, bindingContext);\n                        } else if (!shouldDisplay) {\n                            ko.virtualElements.emptyNode(element);\n                        } else {\n                            // Render once for this single data point (or use the viewModel if no data was provided)\n                            var innerBindingContext = bindingContext;\n                            if ('data' in options) {\n                                innerBindingContext = bindingContext['createChildContext'](options['data'], {\n                                    'as': options['as'],\n                                    'noChildContext': options['noChildContext'],\n                                    'exportDependencies': true\n                                });\n                            }\n                            templateComputed = ko.renderTemplate(template, innerBindingContext, options, element);\n                        }\n\n                        // It only makes sense to have a single template computed per element (otherwise which one should have its output displayed?)\n                        disposeOldComputedAndStoreNewOne(element, templateComputed);\n                    }\n                };\n\n                // Anonymous templates can't be rewritten. Give a nice error message if you try to do it.\n                ko.expressionRewriting.bindingRewriteValidators['template'] = function(bindingValue) {\n                    var parsedBindingValue = ko.expressionRewriting.parseObjectLiteral(bindingValue);\n\n                    if ((parsedBindingValue.length == 1) && parsedBindingValue[0]['unknown'])\n                        return null; // It looks like a string literal, not an object literal, so treat it as a named template (which is allowed for rewriting)\n\n                    if (ko.expressionRewriting.keyValueArrayContainsKey(parsedBindingValue, \"name\"))\n                        return null; // Named templates can be rewritten, so return \"no error\"\n                    return \"This template engine does not support anonymous templates nested within its templates\";\n                };\n\n                ko.virtualElements.allowedBindings['template'] = true;\n            })();\n\n            ko.exportSymbol('setTemplateEngine', ko.setTemplateEngine);\n            ko.exportSymbol('renderTemplate', ko.renderTemplate);\n// Go through the items that have been added and deleted and try to find matches between them.\n            ko.utils.findMovesInArrayComparison = function (left, right, limitFailedCompares) {\n                if (left.length && right.length) {\n                    var failedCompares, l, r, leftItem, rightItem;\n                    for (failedCompares = l = 0; (!limitFailedCompares || failedCompares < limitFailedCompares) && (leftItem = left[l]); ++l) {\n                        for (r = 0; rightItem = right[r]; ++r) {\n                            if (leftItem['value'] === rightItem['value']) {\n                                leftItem['moved'] = rightItem['index'];\n                                rightItem['moved'] = leftItem['index'];\n                                right.splice(r, 1);         // This item is marked as moved; so remove it from right list\n                                failedCompares = r = 0;     // Reset failed compares count because we're checking for consecutive failures\n                                break;\n                            }\n                        }\n                        failedCompares += r;\n                    }\n                }\n            };\n\n            ko.utils.compareArrays = (function () {\n                var statusNotInOld = 'added', statusNotInNew = 'deleted';\n\n                // Simple calculation based on Levenshtein distance.\n                function compareArrays(oldArray, newArray, options) {\n                    // For backward compatibility, if the third arg is actually a bool, interpret\n                    // it as the old parameter 'dontLimitMoves'. Newer code should use { dontLimitMoves: true }.\n                    options = (typeof options === 'boolean') ? { 'dontLimitMoves': options } : (options || {});\n                    oldArray = oldArray || [];\n                    newArray = newArray || [];\n\n                    if (oldArray.length < newArray.length)\n                        return compareSmallArrayToBigArray(oldArray, newArray, statusNotInOld, statusNotInNew, options);\n                    else\n                        return compareSmallArrayToBigArray(newArray, oldArray, statusNotInNew, statusNotInOld, options);\n                }\n\n                function compareSmallArrayToBigArray(smlArray, bigArray, statusNotInSml, statusNotInBig, options) {\n                    var myMin = Math.min,\n                        myMax = Math.max,\n                        editDistanceMatrix = [],\n                        smlIndex, smlIndexMax = smlArray.length,\n                        bigIndex, bigIndexMax = bigArray.length,\n                        compareRange = (bigIndexMax - smlIndexMax) || 1,\n                        maxDistance = smlIndexMax + bigIndexMax + 1,\n                        thisRow, lastRow,\n                        bigIndexMaxForRow, bigIndexMinForRow;\n\n                    for (smlIndex = 0; smlIndex <= smlIndexMax; smlIndex++) {\n                        lastRow = thisRow;\n                        editDistanceMatrix.push(thisRow = []);\n                        bigIndexMaxForRow = myMin(bigIndexMax, smlIndex + compareRange);\n                        bigIndexMinForRow = myMax(0, smlIndex - 1);\n                        for (bigIndex = bigIndexMinForRow; bigIndex <= bigIndexMaxForRow; bigIndex++) {\n                            if (!bigIndex)\n                                thisRow[bigIndex] = smlIndex + 1;\n                            else if (!smlIndex)  // Top row - transform empty array into new array via additions\n                                thisRow[bigIndex] = bigIndex + 1;\n                            else if (smlArray[smlIndex - 1] === bigArray[bigIndex - 1])\n                                thisRow[bigIndex] = lastRow[bigIndex - 1];                  // copy value (no edit)\n                            else {\n                                var northDistance = lastRow[bigIndex] || maxDistance;       // not in big (deletion)\n                                var westDistance = thisRow[bigIndex - 1] || maxDistance;    // not in small (addition)\n                                thisRow[bigIndex] = myMin(northDistance, westDistance) + 1;\n                            }\n                        }\n                    }\n\n                    var editScript = [], meMinusOne, notInSml = [], notInBig = [];\n                    for (smlIndex = smlIndexMax, bigIndex = bigIndexMax; smlIndex || bigIndex;) {\n                        meMinusOne = editDistanceMatrix[smlIndex][bigIndex] - 1;\n                        if (bigIndex && meMinusOne === editDistanceMatrix[smlIndex][bigIndex-1]) {\n                            notInSml.push(editScript[editScript.length] = {     // added\n                                'status': statusNotInSml,\n                                'value': bigArray[--bigIndex],\n                                'index': bigIndex });\n                        } else if (smlIndex && meMinusOne === editDistanceMatrix[smlIndex - 1][bigIndex]) {\n                            notInBig.push(editScript[editScript.length] = {     // deleted\n                                'status': statusNotInBig,\n                                'value': smlArray[--smlIndex],\n                                'index': smlIndex });\n                        } else {\n                            --bigIndex;\n                            --smlIndex;\n                            if (!options['sparse']) {\n                                editScript.push({\n                                    'status': \"retained\",\n                                    'value': bigArray[bigIndex] });\n                            }\n                        }\n                    }\n\n                    // Set a limit on the number of consecutive non-matching comparisons; having it a multiple of\n                    // smlIndexMax keeps the time complexity of this algorithm linear.\n                    ko.utils.findMovesInArrayComparison(notInBig, notInSml, !options['dontLimitMoves'] && smlIndexMax * 10);\n\n                    return editScript.reverse();\n                }\n\n                return compareArrays;\n            })();\n\n            ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);\n            (function () {\n                // Objective:\n                // * Given an input array, a container DOM node, and a function from array elements to arrays of DOM nodes,\n                //   map the array elements to arrays of DOM nodes, concatenate together all these arrays, and use them to populate the container DOM node\n                // * Next time we're given the same combination of things (with the array possibly having mutated), update the container DOM node\n                //   so that its children is again the concatenation of the mappings of the array elements, but don't re-map any array elements that we\n                //   previously mapped - retain those nodes, and just insert/delete other ones\n\n                // \"callbackAfterAddingNodes\" will be invoked after any \"mapping\"-generated nodes are inserted into the container node\n                // You can use this, for example, to activate bindings on those nodes.\n\n                function mapNodeAndRefreshWhenChanged(containerNode, mapping, valueToMap, callbackAfterAddingNodes, index) {\n                    // Map this array value inside a dependentObservable so we re-map when any dependency changes\n                    var mappedNodes = [];\n                    var dependentObservable = ko.dependentObservable(function() {\n                        var newMappedNodes = mapping(valueToMap, index, ko.utils.fixUpContinuousNodeArray(mappedNodes, containerNode)) || [];\n\n                        // On subsequent evaluations, just replace the previously-inserted DOM nodes\n                        if (mappedNodes.length > 0) {\n                            ko.utils.replaceDomNodes(mappedNodes, newMappedNodes);\n                            if (callbackAfterAddingNodes)\n                                ko.dependencyDetection.ignore(callbackAfterAddingNodes, null, [valueToMap, newMappedNodes, index]);\n                        }\n\n                        // Replace the contents of the mappedNodes array, thereby updating the record\n                        // of which nodes would be deleted if valueToMap was itself later removed\n                        mappedNodes.length = 0;\n                        ko.utils.arrayPushAll(mappedNodes, newMappedNodes);\n                    }, null, { disposeWhenNodeIsRemoved: containerNode, disposeWhen: function() { return !ko.utils.anyDomNodeIsAttachedToDocument(mappedNodes); } });\n                    return { mappedNodes : mappedNodes, dependentObservable : (dependentObservable.isActive() ? dependentObservable : undefined) };\n                }\n\n                var lastMappingResultDomDataKey = ko.utils.domData.nextKey(),\n                    deletedItemDummyValue = ko.utils.domData.nextKey();\n\n                ko.utils.setDomNodeChildrenFromArrayMapping = function (domNode, array, mapping, options, callbackAfterAddingNodes, editScript) {\n                    array = array || [];\n                    if (typeof array.length == \"undefined\") // Coerce single value into array\n                        array = [array];\n\n                    options = options || {};\n                    var lastMappingResult = ko.utils.domData.get(domNode, lastMappingResultDomDataKey);\n                    var isFirstExecution = !lastMappingResult;\n\n                    // Build the new mapping result\n                    var newMappingResult = [];\n                    var lastMappingResultIndex = 0;\n                    var currentArrayIndex = 0;\n\n                    var nodesToDelete = [];\n                    var itemsToMoveFirstIndexes = [];\n                    var itemsForBeforeRemoveCallbacks = [];\n                    var itemsForMoveCallbacks = [];\n                    var itemsForAfterAddCallbacks = [];\n                    var mapData;\n                    var countWaitingForRemove = 0;\n\n                    function itemAdded(value) {\n                        mapData = { arrayEntry: value, indexObservable: ko.observable(currentArrayIndex++) };\n                        newMappingResult.push(mapData);\n                        if (!isFirstExecution) {\n                            itemsForAfterAddCallbacks.push(mapData);\n                        }\n                    }\n\n                    function itemMovedOrRetained(oldPosition) {\n                        mapData = lastMappingResult[oldPosition];\n                        if (currentArrayIndex !== mapData.indexObservable.peek())\n                            itemsForMoveCallbacks.push(mapData);\n                        // Since updating the index might change the nodes, do so before calling fixUpContinuousNodeArray\n                        mapData.indexObservable(currentArrayIndex++);\n                        ko.utils.fixUpContinuousNodeArray(mapData.mappedNodes, domNode);\n                        newMappingResult.push(mapData);\n                    }\n\n                    function callCallback(callback, items) {\n                        if (callback) {\n                            for (var i = 0, n = items.length; i < n; i++) {\n                                ko.utils.arrayForEach(items[i].mappedNodes, function(node) {\n                                    callback(node, i, items[i].arrayEntry);\n                                });\n                            }\n                        }\n                    }\n\n                    if (isFirstExecution) {\n                        ko.utils.arrayForEach(array, itemAdded);\n                    } else {\n                        if (!editScript || (lastMappingResult && lastMappingResult['_countWaitingForRemove'])) {\n                            // Compare the provided array against the previous one\n                            var lastArray = ko.utils.arrayMap(lastMappingResult, function (x) { return x.arrayEntry; }),\n                                compareOptions = {\n                                    'dontLimitMoves': options['dontLimitMoves'],\n                                    'sparse': true\n                                };\n                            editScript = ko.utils.compareArrays(lastArray, array, compareOptions);\n                        }\n\n                        for (var i = 0, editScriptItem, movedIndex, itemIndex; editScriptItem = editScript[i]; i++) {\n                            movedIndex = editScriptItem['moved'];\n                            itemIndex = editScriptItem['index'];\n                            switch (editScriptItem['status']) {\n                                case \"deleted\":\n                                    while (lastMappingResultIndex < itemIndex) {\n                                        itemMovedOrRetained(lastMappingResultIndex++);\n                                    }\n                                    if (movedIndex === undefined) {\n                                        mapData = lastMappingResult[lastMappingResultIndex];\n\n                                        // Stop tracking changes to the mapping for these nodes\n                                        if (mapData.dependentObservable) {\n                                            mapData.dependentObservable.dispose();\n                                            mapData.dependentObservable = undefined;\n                                        }\n\n                                        // Queue these nodes for later removal\n                                        if (ko.utils.fixUpContinuousNodeArray(mapData.mappedNodes, domNode).length) {\n                                            if (options['beforeRemove']) {\n                                                newMappingResult.push(mapData);\n                                                countWaitingForRemove++;\n                                                if (mapData.arrayEntry === deletedItemDummyValue) {\n                                                    mapData = null;\n                                                } else {\n                                                    itemsForBeforeRemoveCallbacks.push(mapData);\n                                                }\n                                            }\n                                            if (mapData) {\n                                                nodesToDelete.push.apply(nodesToDelete, mapData.mappedNodes);\n                                            }\n                                        }\n                                    }\n                                    lastMappingResultIndex++;\n                                    break;\n\n                                case \"added\":\n                                    while (currentArrayIndex < itemIndex) {\n                                        itemMovedOrRetained(lastMappingResultIndex++);\n                                    }\n                                    if (movedIndex !== undefined) {\n                                        itemsToMoveFirstIndexes.push(newMappingResult.length);\n                                        itemMovedOrRetained(movedIndex);\n                                    } else {\n                                        itemAdded(editScriptItem['value']);\n                                    }\n                                    break;\n                            }\n                        }\n\n                        while (currentArrayIndex < array.length) {\n                            itemMovedOrRetained(lastMappingResultIndex++);\n                        }\n\n                        // Record that the current view may still contain deleted items\n                        // because it means we won't be able to use a provided editScript.\n                        newMappingResult['_countWaitingForRemove'] = countWaitingForRemove;\n                    }\n\n                    // Store a copy of the array items we just considered so we can difference it next time\n                    ko.utils.domData.set(domNode, lastMappingResultDomDataKey, newMappingResult);\n\n                    // Call beforeMove first before any changes have been made to the DOM\n                    callCallback(options['beforeMove'], itemsForMoveCallbacks);\n\n                    // Next remove nodes for deleted items (or just clean if there's a beforeRemove callback)\n                    ko.utils.arrayForEach(nodesToDelete, options['beforeRemove'] ? ko.cleanNode : ko.removeNode);\n\n                    var i, j, lastNode, nodeToInsert, mappedNodes, activeElement;\n\n                    // Since most browsers remove the focus from an element when it's moved to another location,\n                    // save the focused element and try to restore it later.\n                    try {\n                        activeElement = domNode.ownerDocument.activeElement;\n                    } catch(e) {\n                        // IE9 throws if you access activeElement during page load (see issue #703)\n                    }\n\n                    // Try to reduce overall moved nodes by first moving the ones that were marked as moved by the edit script\n                    if (itemsToMoveFirstIndexes.length) {\n                        while ((i = itemsToMoveFirstIndexes.shift()) != undefined) {\n                            mapData = newMappingResult[i];\n                            for (lastNode = undefined; i; ) {\n                                if ((mappedNodes = newMappingResult[--i].mappedNodes) && mappedNodes.length) {\n                                    lastNode = mappedNodes[mappedNodes.length-1];\n                                    break;\n                                }\n                            }\n                            for (j = 0; nodeToInsert = mapData.mappedNodes[j]; lastNode = nodeToInsert, j++) {\n                                ko.virtualElements.insertAfter(domNode, nodeToInsert, lastNode);\n                            }\n                        }\n                    }\n\n                    // Next add/reorder the remaining items (will include deleted items if there's a beforeRemove callback)\n                    for (i = 0; mapData = newMappingResult[i]; i++) {\n                        // Get nodes for newly added items\n                        if (!mapData.mappedNodes)\n                            ko.utils.extend(mapData, mapNodeAndRefreshWhenChanged(domNode, mapping, mapData.arrayEntry, callbackAfterAddingNodes, mapData.indexObservable));\n\n                        // Put nodes in the right place if they aren't there already\n                        for (j = 0; nodeToInsert = mapData.mappedNodes[j]; lastNode = nodeToInsert, j++) {\n                            ko.virtualElements.insertAfter(domNode, nodeToInsert, lastNode);\n                        }\n\n                        // Run the callbacks for newly added nodes (for example, to apply bindings, etc.)\n                        if (!mapData.initialized && callbackAfterAddingNodes) {\n                            callbackAfterAddingNodes(mapData.arrayEntry, mapData.mappedNodes, mapData.indexObservable);\n                            mapData.initialized = true;\n                            lastNode = mapData.mappedNodes[mapData.mappedNodes.length - 1];     // get the last node again since it may have been changed by a preprocessor\n                        }\n                    }\n\n                    // Restore the focused element if it had lost focus\n                    if (activeElement && domNode.ownerDocument.activeElement != activeElement) {\n                        activeElement.focus();\n                    }\n\n                    // If there's a beforeRemove callback, call it after reordering.\n                    // Note that we assume that the beforeRemove callback will usually be used to remove the nodes using\n                    // some sort of animation, which is why we first reorder the nodes that will be removed. If the\n                    // callback instead removes the nodes right away, it would be more efficient to skip reordering them.\n                    // Perhaps we'll make that change in the future if this scenario becomes more common.\n                    callCallback(options['beforeRemove'], itemsForBeforeRemoveCallbacks);\n\n                    // Replace the stored values of deleted items with a dummy value. This provides two benefits: it marks this item\n                    // as already \"removed\" so we won't call beforeRemove for it again, and it ensures that the item won't match up\n                    // with an actual item in the array and appear as \"retained\" or \"moved\".\n                    for (i = 0; i < itemsForBeforeRemoveCallbacks.length; ++i) {\n                        itemsForBeforeRemoveCallbacks[i].arrayEntry = deletedItemDummyValue;\n                    }\n\n                    // Finally call afterMove and afterAdd callbacks\n                    callCallback(options['afterMove'], itemsForMoveCallbacks);\n                    callCallback(options['afterAdd'], itemsForAfterAddCallbacks);\n                }\n            })();\n\n            ko.exportSymbol('utils.setDomNodeChildrenFromArrayMapping', ko.utils.setDomNodeChildrenFromArrayMapping);\n            ko.nativeTemplateEngine = function () {\n                this['allowTemplateRewriting'] = false;\n            }\n\n            ko.nativeTemplateEngine.prototype = new ko.templateEngine();\n            ko.nativeTemplateEngine.prototype.constructor = ko.nativeTemplateEngine;\n            ko.nativeTemplateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options, templateDocument) {\n                var useNodesIfAvailable = !(ko.utils.ieVersion < 9), // IE<9 cloneNode doesn't work properly\n                    templateNodesFunc = useNodesIfAvailable ? templateSource['nodes'] : null,\n                    templateNodes = templateNodesFunc ? templateSource['nodes']() : null;\n\n                if (templateNodes) {\n                    return ko.utils.makeArray(templateNodes.cloneNode(true).childNodes);\n                } else {\n                    var templateText = templateSource['text']();\n                    return ko.utils.parseHtmlFragment(templateText, templateDocument);\n                }\n            };\n\n            ko.nativeTemplateEngine.instance = new ko.nativeTemplateEngine();\n            ko.setTemplateEngine(ko.nativeTemplateEngine.instance);\n\n            ko.exportSymbol('nativeTemplateEngine', ko.nativeTemplateEngine);\n            (function() {\n                ko.jqueryTmplTemplateEngine = function () {\n                    // Detect which version of jquery-tmpl you're using. Unfortunately jquery-tmpl\n                    // doesn't expose a version number, so we have to infer it.\n                    // Note that as of Knockout 1.3, we only support jQuery.tmpl 1.0.0pre and later,\n                    // which KO internally refers to as version \"2\", so older versions are no longer detected.\n                    var jQueryTmplVersion = this.jQueryTmplVersion = (function() {\n                        if (!jQueryInstance || !(jQueryInstance['tmpl']))\n                            return 0;\n                        // Since it exposes no official version number, we use our own numbering system. To be updated as jquery-tmpl evolves.\n                        try {\n                            if (jQueryInstance['tmpl']['tag']['tmpl']['open'].toString().indexOf('__') >= 0) {\n                                // Since 1.0.0pre, custom tags should append markup to an array called \"__\"\n                                return 2; // Final version of jquery.tmpl\n                            }\n                        } catch(ex) { /* Apparently not the version we were looking for */ }\n\n                        return 1; // Any older version that we don't support\n                    })();\n\n                    function ensureHasReferencedJQueryTemplates() {\n                        if (jQueryTmplVersion < 2)\n                            throw new Error(\"Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.\");\n                    }\n\n                    function executeTemplate(compiledTemplate, data, jQueryTemplateOptions) {\n                        return jQueryInstance['tmpl'](compiledTemplate, data, jQueryTemplateOptions);\n                    }\n\n                    this['renderTemplateSource'] = function(templateSource, bindingContext, options, templateDocument) {\n                        templateDocument = templateDocument || document;\n                        options = options || {};\n                        ensureHasReferencedJQueryTemplates();\n\n                        // Ensure we have stored a precompiled version of this template (don't want to reparse on every render)\n                        var precompiled = templateSource['data']('precompiled');\n                        if (!precompiled) {\n                            var templateText = templateSource['text']() || \"\";\n                            // Wrap in \"with($whatever.koBindingContext) { ... }\"\n                            templateText = \"{{ko_with $item.koBindingContext}}\" + templateText + \"{{/ko_with}}\";\n\n                            precompiled = jQueryInstance['template'](null, templateText);\n                            templateSource['data']('precompiled', precompiled);\n                        }\n\n                        var data = [bindingContext['$data']]; // Prewrap the data in an array to stop jquery.tmpl from trying to unwrap any arrays\n                        var jQueryTemplateOptions = jQueryInstance['extend']({ 'koBindingContext': bindingContext }, options['templateOptions']);\n\n                        var resultNodes = executeTemplate(precompiled, data, jQueryTemplateOptions);\n                        resultNodes['appendTo'](templateDocument.createElement(\"div\")); // Using \"appendTo\" forces jQuery/jQuery.tmpl to perform necessary cleanup work\n\n                        jQueryInstance['fragments'] = {}; // Clear jQuery's fragment cache to avoid a memory leak after a large number of template renders\n                        return resultNodes;\n                    };\n\n                    this['createJavaScriptEvaluatorBlock'] = function(script) {\n                        return \"{{ko_code ((function() { return \" + script + \" })()) }}\";\n                    };\n\n                    this['addTemplate'] = function(templateName, templateMarkup) {\n                        document.write(\"<script type='text/html' id='\" + templateName + \"'>\" + templateMarkup + \"<\" + \"/script>\");\n                    };\n\n                    if (jQueryTmplVersion > 0) {\n                        jQueryInstance['tmpl']['tag']['ko_code'] = {\n                            open: \"__.push($1 || '');\"\n                        };\n                        jQueryInstance['tmpl']['tag']['ko_with'] = {\n                            open: \"with($1) {\",\n                            close: \"} \"\n                        };\n                    }\n                };\n\n                ko.jqueryTmplTemplateEngine.prototype = new ko.templateEngine();\n                ko.jqueryTmplTemplateEngine.prototype.constructor = ko.jqueryTmplTemplateEngine;\n\n                // Use this one by default *only if jquery.tmpl is referenced*\n                var jqueryTmplTemplateEngineInstance = new ko.jqueryTmplTemplateEngine();\n                if (jqueryTmplTemplateEngineInstance.jQueryTmplVersion > 0)\n                    ko.setTemplateEngine(jqueryTmplTemplateEngineInstance);\n\n                ko.exportSymbol('jqueryTmplTemplateEngine', ko.jqueryTmplTemplateEngine);\n            })();\n        }));\n    }());\n})();\n","knockoutjs/knockout-fast-foreach.js":"/*!\n  Knockout Fast Foreach v0.4.1 (2015-07-17T14:06:15.974Z)\n  By: Brian M Hunt (C) 2015\n  License: MIT\n\n  Adds `fastForEach` to `ko.bindingHandlers`.\n*/\n(function (root, factory) {\n  if (typeof define === 'function' && define.amd) {\n    define(['knockout'], factory);\n  } else if (typeof exports === 'object') {\n    module.exports = factory(require('knockout'));\n  } else {\n    root.KnockoutFastForeach = factory(root.ko);\n  }\n}(this, function (ko) {\n  \"use strict\";\n// index.js\n// --------\n// Fast For Each\n//\n// Employing sound techniques to make a faster Knockout foreach binding.\n// --------\n\n//      Utilities\n\n// from https://github.com/jonschlinkert/is-plain-object\nfunction isPlainObject(o) {\n  return !!o && typeof o === 'object' && o.constructor === Object;\n}\n\n// From knockout/src/virtualElements.js\nvar commentNodesHaveTextProperty = document && document.createComment(\"test\").text === \"<!--test-->\";\nvar startCommentRegex = commentNodesHaveTextProperty ? /^<!--\\s*ko(?:\\s+([\\s\\S]+))?\\s*-->$/ : /^\\s*ko(?:\\s+([\\s\\S]+))?\\s*$/;\nvar supportsDocumentFragment = document && typeof document.createDocumentFragment === \"function\";\nfunction isVirtualNode(node) {\n  return (node.nodeType === 8) && startCommentRegex.test(commentNodesHaveTextProperty ? node.text : node.nodeValue);\n}\n\n\n// Get a copy of the (possibly virtual) child nodes of the given element,\n// put them into a container, then empty the given node.\nfunction makeTemplateNode(sourceNode) {\n  var container = document.createElement(\"div\");\n  var parentNode;\n  if (sourceNode.content) {\n    // For e.g. <template> tags\n    parentNode = sourceNode.content;\n  } else if (sourceNode.tagName === 'SCRIPT') {\n    parentNode = document.createElement(\"div\");\n    parentNode.innerHTML = sourceNode.text;\n  } else {\n    // Anything else e.g. <div>\n    parentNode = sourceNode;\n  }\n  ko.utils.arrayForEach(ko.virtualElements.childNodes(parentNode), function (child) {\n    // FIXME - This cloneNode could be expensive; we may prefer to iterate over the\n    // parentNode children in reverse (so as not to foul the indexes as childNodes are\n    // removed from parentNode when inserted into the container)\n    if (child) {\n      container.insertBefore(child.cloneNode(true), null);\n    }\n  });\n  return container;\n}\n\nfunction insertAllAfter(containerNode, nodeOrNodeArrayToInsert, insertAfterNode) {\n  var frag, len, i;\n  // poor man's node and array check, should be enough for this\n  if (typeof nodeOrNodeArrayToInsert.nodeType !== \"undefined\" && typeof nodeOrNodeArrayToInsert.length === \"undefined\") {\n    throw new Error(\"Expected a single node or a node array\");\n  }\n\n  if (typeof nodeOrNodeArrayToInsert.nodeType !== \"undefined\") {\n    ko.virtualElements.insertAfter(containerNode, nodeOrNodeArrayToInsert, insertAfterNode);\n    return;\n  }\n\n  if (nodeOrNodeArrayToInsert.length === 1) {\n    ko.virtualElements.insertAfter(containerNode, nodeOrNodeArrayToInsert[0], insertAfterNode);\n    return;\n  }\n\n  if (supportsDocumentFragment) {\n    frag = document.createDocumentFragment();\n\n    for (i = 0, len = nodeOrNodeArrayToInsert.length; i !== len; ++i) {\n      frag.appendChild(nodeOrNodeArrayToInsert[i]);\n    }\n    ko.virtualElements.insertAfter(containerNode, frag, insertAfterNode);\n  } else {\n    // Nodes are inserted in reverse order - pushed down immediately after\n    // the last node for the previous item or as the first node of element.\n    for (i = nodeOrNodeArrayToInsert.length - 1; i >= 0; --i) {\n      var child = nodeOrNodeArrayToInsert[i];\n      if (!child) {\n        return;\n      }\n      ko.virtualElements.insertAfter(containerNode, child, insertAfterNode);\n    }\n  }\n}\n\n// Mimic a KO change item 'add'\nfunction valueToChangeAddItem(value, index) {\n  return {\n    status: 'added',\n    value: value,\n    index: index\n  };\n}\n\nfunction isAdditionAdjacentToLast(changeIndex, arrayChanges) {\n  return changeIndex > 0 &&\n    changeIndex < arrayChanges.length &&\n    arrayChanges[changeIndex].status === \"added\" &&\n    arrayChanges[changeIndex - 1].status === \"added\" &&\n    arrayChanges[changeIndex - 1].index === arrayChanges[changeIndex].index - 1;\n}\n\nfunction FastForEach(spec) {\n  this.element = spec.element;\n  this.container = isVirtualNode(this.element) ?\n                   this.element.parentNode : this.element;\n  this.$context = spec.$context;\n  this.data = spec.data;\n  this.as = spec.as;\n  this.noContext = spec.noContext;\n  this.templateNode = makeTemplateNode(\n    spec.name ? document.getElementById(spec.name).cloneNode(true) : spec.element\n  );\n  this.afterQueueFlush = spec.afterQueueFlush;\n  this.beforeQueueFlush = spec.beforeQueueFlush;\n  this.changeQueue = [];\n  this.lastNodesList = [];\n  this.indexesToDelete = [];\n  this.rendering_queued = false;\n\n  // Remove existing content.\n  ko.virtualElements.emptyNode(this.element);\n\n  // Prime content\n  var primeData = ko.unwrap(this.data);\n  if (primeData.map) {\n    this.onArrayChange(primeData.map(valueToChangeAddItem));\n  }\n\n  // Watch for changes\n  if (ko.isObservable(this.data)) {\n    if (!this.data.indexOf) {\n      // Make sure the observable is trackable.\n      this.data = this.data.extend({trackArrayChanges: true});\n    }\n    this.changeSubs = this.data.subscribe(this.onArrayChange, this, 'arrayChange');\n  }\n}\n\n\nFastForEach.animateFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame ||\n  window.mozRequestAnimationFrame || window.msRequestAnimationFrame ||\n  function(cb) { return window.setTimeout(cb, 1000 / 60); };\n\n\nFastForEach.prototype.dispose = function () {\n  if (this.changeSubs) {\n    this.changeSubs.dispose();\n  }\n};\n\n\n// If the array changes we register the change.\nFastForEach.prototype.onArrayChange = function (changeSet) {\n  var self = this;\n  var changeMap = {\n    added: [],\n    deleted: []\n  };\n  for (var i = 0, len = changeSet.length; i < len; i++) {\n    // the change is appended to a last change info object when both are 'added' and have indexes next to each other\n    // here I presume that ko is sending changes in monotonic order (in index variable) which happens to be true, tested with push and splice with multiple pushed values\n    if (isAdditionAdjacentToLast(i, changeSet)) {\n      var batchValues = changeMap.added[changeMap.added.length - 1].values;\n      if (!batchValues) {\n        // transform the last addition into a batch addition object\n        var lastAddition = changeMap.added.pop();\n        var batchAddition = {\n          isBatch: true,\n          status: 'added',\n          index: lastAddition.index,\n          values: [lastAddition.value]\n        };\n        batchValues = batchAddition.values;\n        changeMap.added.push(batchAddition);\n      }\n      batchValues.push(changeSet[i].value);\n    } else {\n      changeMap[changeSet[i].status].push(changeSet[i]);\n    }\n  }\n  if (changeMap.deleted.length > 0) {\n    this.changeQueue.push.apply(this.changeQueue, changeMap.deleted);\n    this.changeQueue.push({status: 'clearDeletedIndexes'});\n  }\n  this.changeQueue.push.apply(this.changeQueue, changeMap.added);\n  // Once a change is registered, the ticking count-down starts for the processQueue.\n  if (this.changeQueue.length > 0 && !this.rendering_queued) {\n    this.rendering_queued = true;\n    FastForEach.animateFrame.call(window, function () { self.processQueue(); });\n  }\n};\n\n\n// Reflect all the changes in the queue in the DOM, then wipe the queue.\nFastForEach.prototype.processQueue = function () {\n  var self = this;\n\n  // Callback so folks can do things before the queue flush.\n  if (typeof this.beforeQueueFlush === 'function') {\n    this.beforeQueueFlush(this.changeQueue);\n  }\n\n  ko.utils.arrayForEach(this.changeQueue, function (changeItem) {\n    // console.log(self.data(), \"CI\", JSON.stringify(changeItem, null, 2), JSON.stringify($(self.element).text()))\n    self[changeItem.status](changeItem);\n    // console.log(\"  ==> \", JSON.stringify($(self.element).text()))\n  });\n  this.rendering_queued = false;\n  // Callback so folks can do things.\n  if (typeof this.afterQueueFlush === 'function') {\n    this.afterQueueFlush(this.changeQueue);\n  }\n  this.changeQueue = [];\n};\n\n\n// Process a changeItem with {status: 'added', ...}\nFastForEach.prototype.added = function (changeItem) {\n  var index = changeItem.index;\n  var valuesToAdd = changeItem.isBatch ? changeItem.values : [changeItem.value];\n  var referenceElement = this.lastNodesList[index - 1] || null;\n  // gather all childnodes for a possible batch insertion\n  var allChildNodes = [];\n\n  for (var i = 0, len = valuesToAdd.length; i < len; ++i) {\n    var templateClone = this.templateNode.cloneNode(true);\n    var childContext;\n\n    if (this.noContext) {\n      childContext = this.$context.extend({\n        '$item': valuesToAdd[i]\n      });\n    } else {\n      childContext = this.$context.createChildContext(valuesToAdd[i], this.as || null);\n    }\n\n    // apply bindings first, and then process child nodes, because bindings can add childnodes\n    ko.applyBindingsToDescendants(childContext, templateClone);\n\n    var childNodes = ko.virtualElements.childNodes(templateClone);\n    // Note discussion at https://github.com/angular/angular.js/issues/7851\n    allChildNodes.push.apply(allChildNodes, Array.prototype.slice.call(childNodes));\n    this.lastNodesList.splice(index + i, 0, childNodes[childNodes.length - 1]);\n  }\n\n  insertAllAfter(this.element, allChildNodes, referenceElement);\n};\n\n\n// Process a changeItem with {status: 'deleted', ...}\nFastForEach.prototype.deleted = function (changeItem) {\n  var index = changeItem.index;\n  var ptr = this.lastNodesList[index],\n      // We use this.element because that will be the last previous node\n      // for virtual element lists.\n      lastNode = this.lastNodesList[index - 1] || this.element;\n  do {\n    ptr = ptr.previousSibling;\n    ko.removeNode((ptr && ptr.nextSibling) || ko.virtualElements.firstChild(this.element));\n  } while (ptr && ptr !== lastNode);\n  // The \"last node\" in the DOM from which we begin our delets of the next adjacent node is\n  // now the sibling that preceded the first node of this item.\n  this.lastNodesList[index] = this.lastNodesList[index - 1];\n  this.indexesToDelete.push(index);\n};\n\n\n// We batch our deletion of item indexes in our parallel array.\n// See brianmhunt/knockout-fast-foreach#6/#8\nFastForEach.prototype.clearDeletedIndexes = function () {\n  // We iterate in reverse on the presumption (following the unit tests) that KO's diff engine\n  // processes diffs (esp. deletes) monotonically ascending i.e. from index 0 -> N.\n  for (var i = this.indexesToDelete.length - 1; i >= 0; --i) {\n    this.lastNodesList.splice(this.indexesToDelete[i], 1);\n  }\n  this.indexesToDelete = [];\n};\n\n\nko.bindingHandlers.fastForEach = {\n  // Valid valueAccessors:\n  //    []\n  //    ko.observable([])\n  //    ko.observableArray([])\n  //    ko.computed\n  //    {data: array, name: string, as: string}\n  init: function init(element, valueAccessor, bindings, vm, context) {\n    var value = valueAccessor(),\n        ffe;\n    if (isPlainObject(value)) {\n      value.element = value.element || element;\n      value.$context = context;\n      ffe = new FastForEach(value);\n    } else {\n      ffe = new FastForEach({\n        element: element,\n        data: ko.unwrap(context.$rawData) === value ? context.$rawData : value,\n        $context: context\n      });\n    }\n    ko.utils.domNodeDisposal.addDisposeCallback(element, function () {\n      ffe.dispose();\n    });\n    return {controlsDescendantBindings: true};\n  },\n\n  // Export for testing, debugging, and overloading.\n  FastForEach: FastForEach\n};\n\nko.virtualElements.allowedBindings.fastForEach = true;\n}));","knockoutjs/knockout-es5.js":"/*!\n * Knockout ES5 plugin - https://github.com/SteveSanderson/knockout-es5\n * Copyright (c) Steve Sanderson\n * MIT license\n */\n\n(function(global, undefined) {\n  'use strict';\n\n  var ko;\n\n  // Model tracking\n  // --------------\n  //\n  // This is the central feature of Knockout-ES5. We augment model objects by converting properties\n  // into ES5 getter/setter pairs that read/write an underlying Knockout observable. This means you can\n  // use plain JavaScript syntax to read/write the property while still getting the full benefits of\n  // Knockout's automatic dependency detection and notification triggering.\n  //\n  // For comparison, here's Knockout ES3-compatible syntax:\n  //\n  //     var firstNameLength = myModel.user().firstName().length; // Read\n  //     myModel.user().firstName('Bert'); // Write\n  //\n  // ... versus Knockout-ES5 syntax:\n  //\n  //     var firstNameLength = myModel.user.firstName.length; // Read\n  //     myModel.user.firstName = 'Bert'; // Write\n\n  // `ko.track(model)` converts each property on the given model object into a getter/setter pair that\n  // wraps a Knockout observable. Optionally specify an array of property names to wrap; otherwise we\n  // wrap all properties. If any of the properties are already observables, we replace them with\n  // ES5 getter/setter pairs that wrap your original observable instances. In the case of readonly\n  // ko.computed properties, we simply do not define a setter (so attempted writes will be ignored,\n  // which is how ES5 readonly properties normally behave).\n  //\n  // By design, this does *not* recursively walk child object properties, because making literally\n  // everything everywhere independently observable is usually unhelpful. When you do want to track\n  // child object properties independently, define your own class for those child objects and put\n  // a separate ko.track call into its constructor --- this gives you far more control.\n  /**\n   * @param {object} obj\n   * @param {object|array.<string>} propertyNamesOrSettings\n   * @param {boolean} propertyNamesOrSettings.deep Use deep track.\n   * @param {array.<string>} propertyNamesOrSettings.fields Array of property names to wrap.\n   * todo: @param {array.<string>} propertyNamesOrSettings.exclude Array of exclude property names to wrap.\n   * todo: @param {function(string, *):boolean} propertyNamesOrSettings.filter Function to filter property \n   *   names to wrap. A function that takes ... params\n   * @return {object}\n   */\n  function track(obj, propertyNamesOrSettings) {\n    if (!obj || typeof obj !== 'object') {\n      throw new Error('When calling ko.track, you must pass an object as the first parameter.');\n    }\n\n    var propertyNames;\n\n    if ( isPlainObject(propertyNamesOrSettings) ) {\n      // defaults\n      propertyNamesOrSettings.deep = propertyNamesOrSettings.deep || false;\n      propertyNamesOrSettings.fields = propertyNamesOrSettings.fields || Object.getOwnPropertyNames(obj);\n      propertyNamesOrSettings.lazy = propertyNamesOrSettings.lazy || false;\n\n      wrap(obj, propertyNamesOrSettings.fields, propertyNamesOrSettings);\n    } else {\n      propertyNames = propertyNamesOrSettings || Object.getOwnPropertyNames(obj);\n      wrap(obj, propertyNames, {});\n    }\n\n    return obj;\n  }\n\n  // fix for ie\n  var rFunctionName = /^function\\s*([^\\s(]+)/;\n  function getFunctionName( ctor ){\n    if (ctor.name) {\n      return ctor.name;\n    }\n    return (ctor.toString().trim().match( rFunctionName ) || [])[1];\n  }\n\n  function canTrack(obj) {\n    return obj && typeof obj === 'object' && getFunctionName(obj.constructor) === 'Object';\n  }\n\n  function createPropertyDescriptor(originalValue, prop, map) {\n    var isObservable = ko.isObservable(originalValue);\n    var isArray = !isObservable && Array.isArray(originalValue);\n    var observable = isObservable ? originalValue\n        : isArray ? ko.observableArray(originalValue)\n        : ko.observable(originalValue);\n\n    map[prop] = function () { return observable; };\n\n    // add check in case the object is already an observable array\n    if (isArray || (isObservable && 'push' in observable)) {\n      notifyWhenPresentOrFutureArrayValuesMutate(ko, observable);\n    }\n\n    return {\n      configurable: true,\n      enumerable: true,\n      get: observable,\n      set: ko.isWriteableObservable(observable) ? observable : undefined\n    };\n  }\n\n  function createLazyPropertyDescriptor(originalValue, prop, map) {\n    if (ko.isObservable(originalValue)) {\n      // no need to be lazy if we already have an observable\n      return createPropertyDescriptor(originalValue, prop, map);\n    }\n\n    var observable;\n\n    function getOrCreateObservable(value, writing) {\n      if (observable) {\n        return writing ? observable(value) : observable;\n      }\n\n      if (Array.isArray(value)) {\n        observable = ko.observableArray(value);\n        notifyWhenPresentOrFutureArrayValuesMutate(ko, observable);\n        return observable;\n      }\n\n      return (observable = ko.observable(value));\n    }\n\n    map[prop] = function () { return getOrCreateObservable(originalValue); };\n    return {\n      configurable: true,\n      enumerable: true,\n      get: function () { return getOrCreateObservable(originalValue)(); },\n      set: function (value) { getOrCreateObservable(value, true); }\n    };\n  }\n\n  function wrap(obj, props, options) {\n    if (!props.length) {\n      return;\n    }\n\n    var allObservablesForObject = getAllObservablesForObject(obj, true);\n    var descriptors = {};\n\n    props.forEach(function (prop) {\n      // Skip properties that are already tracked\n      if (prop in allObservablesForObject) {\n        return;\n      }\n\n      // Skip properties where descriptor can't be redefined\n      if (Object.getOwnPropertyDescriptor(obj, prop).configurable === false){\n        return;\n      }\n\n      var originalValue = obj[prop];\n      descriptors[prop] = (options.lazy ? createLazyPropertyDescriptor : createPropertyDescriptor)\n        (originalValue, prop, allObservablesForObject);\n\n      if (options.deep && canTrack(originalValue)) {\n        wrap(originalValue, Object.keys(originalValue), options);\n      }\n    });\n\n    Object.defineProperties(obj, descriptors);\n  }\n\n  function isPlainObject( obj ){\n    return !!obj && typeof obj === 'object' && obj.constructor === Object;\n  }\n\n  // Lazily created by `getAllObservablesForObject` below. Has to be created lazily because the\n  // WeakMap factory isn't available until the module has finished loading (may be async).\n  var objectToObservableMap;\n\n  // Gets or creates the hidden internal key-value collection of observables corresponding to\n  // properties on the model object.\n  function getAllObservablesForObject(obj, createIfNotDefined) {\n    if (!objectToObservableMap) {\n      objectToObservableMap = weakMapFactory();\n    }\n\n    var result = objectToObservableMap.get(obj);\n    if (!result && createIfNotDefined) {\n      result = {};\n      objectToObservableMap.set(obj, result);\n    }\n    return result;\n  }\n\n  // Removes the internal references to observables mapped to the specified properties\n  // or the entire object reference if no properties are passed in. This allows the\n  // observables to be replaced and tracked again.\n  function untrack(obj, propertyNames) {\n    if (!objectToObservableMap) {\n      return;\n    }\n\n    if (arguments.length === 1) {\n      objectToObservableMap['delete'](obj);\n    } else {\n      var allObservablesForObject = getAllObservablesForObject(obj, false);\n      if (allObservablesForObject) {\n        propertyNames.forEach(function(propertyName) {\n          delete allObservablesForObject[propertyName];\n        });\n      }\n    }\n  }\n\n  // Computed properties\n  // -------------------\n  //\n  // The preceding code is already sufficient to upgrade ko.computed model properties to ES5\n  // getter/setter pairs (or in the case of readonly ko.computed properties, just a getter).\n  // These then behave like a regular property with a getter function, except they are smarter:\n  // your evaluator is only invoked when one of its dependencies changes. The result is cached\n  // and used for all evaluations until the next time a dependency changes).\n  //\n  // However, instead of forcing developers to declare a ko.computed property explicitly, it's\n  // nice to offer a utility function that declares a computed getter directly.\n\n  // Implements `ko.defineProperty`\n  function defineComputedProperty(obj, propertyName, evaluatorOrOptions) {\n    var ko = this,\n      computedOptions = { owner: obj, deferEvaluation: true };\n\n    if (typeof evaluatorOrOptions === 'function') {\n      computedOptions.read = evaluatorOrOptions;\n    } else {\n      if ('value' in evaluatorOrOptions) {\n        throw new Error('For ko.defineProperty, you must not specify a \"value\" for the property. ' +\n                        'You must provide a \"get\" function.');\n      }\n\n      if (typeof evaluatorOrOptions.get !== 'function') {\n        throw new Error('For ko.defineProperty, the third parameter must be either an evaluator function, ' +\n                        'or an options object containing a function called \"get\".');\n      }\n\n      computedOptions.read = evaluatorOrOptions.get;\n      computedOptions.write = evaluatorOrOptions.set;\n    }\n\n    obj[propertyName] = ko.computed(computedOptions);\n    track.call(ko, obj, [propertyName]);\n    return obj;\n  }\n\n  // Array handling\n  // --------------\n  //\n  // Arrays are special, because unlike other property types, they have standard mutator functions\n  // (`push`/`pop`/`splice`/etc.) and it's desirable to trigger a change notification whenever one of\n  // those mutator functions is invoked.\n  //\n  // Traditionally, Knockout handles this by putting special versions of `push`/`pop`/etc. on observable\n  // arrays that mutate the underlying array and then trigger a notification. That approach doesn't\n  // work for Knockout-ES5 because properties now return the underlying arrays, so the mutator runs\n  // in the context of the underlying array, not any particular observable:\n  //\n  //     // Operates on the underlying array value\n  //     myModel.someCollection.push('New value');\n  //\n  // To solve this, Knockout-ES5 detects array values, and modifies them as follows:\n  //  1. Associates a hidden subscribable with each array instance that it encounters\n  //  2. Intercepts standard mutators (`push`/`pop`/etc.) and makes them trigger the subscribable\n  // Then, for model properties whose values are arrays, the property's underlying observable\n  // subscribes to the array subscribable, so it can trigger a change notification after mutation.\n\n  // Given an observable that underlies a model property, watch for any array value that might\n  // be assigned as the property value, and hook into its change events\n  function notifyWhenPresentOrFutureArrayValuesMutate(ko, observable) {\n    var watchingArraySubscription = null;\n    ko.computed(function () {\n      // Unsubscribe to any earlier array instance\n      if (watchingArraySubscription) {\n        watchingArraySubscription.dispose();\n        watchingArraySubscription = null;\n      }\n\n      // Subscribe to the new array instance\n      var newArrayInstance = observable();\n      if (newArrayInstance instanceof Array) {\n        watchingArraySubscription = startWatchingArrayInstance(ko, observable, newArrayInstance);\n      }\n    });\n  }\n\n  // Listens for array mutations, and when they happen, cause the observable to fire notifications.\n  // This is used to make model properties of type array fire notifications when the array changes.\n  // Returns a subscribable that can later be disposed.\n  function startWatchingArrayInstance(ko, observable, arrayInstance) {\n    var subscribable = getSubscribableForArray(ko, arrayInstance);\n    return subscribable.subscribe(observable);\n  }\n\n  // Lazily created by `getSubscribableForArray` below. Has to be created lazily because the\n  // WeakMap factory isn't available until the module has finished loading (may be async).\n  var arraySubscribablesMap;\n\n  // Gets or creates a subscribable that fires after each array mutation\n  function getSubscribableForArray(ko, arrayInstance) {\n    if (!arraySubscribablesMap) {\n      arraySubscribablesMap = weakMapFactory();\n    }\n\n    var subscribable = arraySubscribablesMap.get(arrayInstance);\n    if (!subscribable) {\n      subscribable = new ko.subscribable();\n      arraySubscribablesMap.set(arrayInstance, subscribable);\n\n      var notificationPauseSignal = {};\n      wrapStandardArrayMutators(arrayInstance, subscribable, notificationPauseSignal);\n      addKnockoutArrayMutators(ko, arrayInstance, subscribable, notificationPauseSignal);\n    }\n\n    return subscribable;\n  }\n\n  // After each array mutation, fires a notification on the given subscribable\n  function wrapStandardArrayMutators(arrayInstance, subscribable, notificationPauseSignal) {\n    ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'].forEach(function(fnName) {\n      var origMutator = arrayInstance[fnName];\n      arrayInstance[fnName] = function() {\n        var result = origMutator.apply(this, arguments);\n        if (notificationPauseSignal.pause !== true) {\n          subscribable.notifySubscribers(this);\n        }\n        return result;\n      };\n    });\n  }\n\n  // Adds Knockout's additional array mutation functions to the array\n  function addKnockoutArrayMutators(ko, arrayInstance, subscribable, notificationPauseSignal) {\n    ['remove', 'removeAll', 'destroy', 'destroyAll', 'replace'].forEach(function(fnName) {\n      // Make it a non-enumerable property for consistency with standard Array functions\n      Object.defineProperty(arrayInstance, fnName, {\n        enumerable: false,\n        value: function() {\n          var result;\n\n          // These additional array mutators are built using the underlying push/pop/etc.\n          // mutators, which are wrapped to trigger notifications. But we don't want to\n          // trigger multiple notifications, so pause the push/pop/etc. wrappers and\n          // delivery only one notification at the end of the process.\n          notificationPauseSignal.pause = true;\n          try {\n            // Creates a temporary observableArray that can perform the operation.\n            result = ko.observableArray.fn[fnName].apply(ko.observableArray(arrayInstance), arguments);\n          }\n          finally {\n            notificationPauseSignal.pause = false;\n          }\n          subscribable.notifySubscribers(arrayInstance);\n          return result;\n        }\n      });\n    });\n  }\n\n  // Static utility functions\n  // ------------------------\n  //\n  // Since Knockout-ES5 sets up properties that return values, not observables, you can't\n  // trivially subscribe to the underlying observables (e.g., `someProperty.subscribe(...)`),\n  // or tell them that object values have mutated, etc. To handle this, we set up some\n  // extra utility functions that can return or work with the underlying observables.\n\n  // Returns the underlying observable associated with a model property (or `null` if the\n  // model or property doesn't exist, or isn't associated with an observable). This means\n  // you can subscribe to the property, e.g.:\n  //\n  //     ko.getObservable(model, 'propertyName')\n  //       .subscribe(function(newValue) { ... });\n  function getObservable(obj, propertyName) {\n    if (!obj || typeof obj !== 'object') {\n      return null;\n    }\n\n    var allObservablesForObject = getAllObservablesForObject(obj, false);\n    if (allObservablesForObject && propertyName in allObservablesForObject) {\n      return allObservablesForObject[propertyName]();\n    }\n\n    return null;\n  }\n  \n  // Returns a boolean indicating whether the property on the object has an underlying\n  // observables. This does the check in a way not to create an observable if the\n  // object was created with lazily created observables\n  function isTracked(obj, propertyName) {\n    if (!obj || typeof obj !== 'object') {\n      return false;\n    }\n    \n    var allObservablesForObject = getAllObservablesForObject(obj, false);\n    return !!allObservablesForObject && propertyName in allObservablesForObject;\n  }\n\n  // Causes a property's associated observable to fire a change notification. Useful when\n  // the property value is a complex object and you've modified a child property.\n  function valueHasMutated(obj, propertyName) {\n    var observable = getObservable(obj, propertyName);\n\n    if (observable) {\n      observable.valueHasMutated();\n    }\n  }\n\n  // Module initialisation\n  // ---------------------\n  //\n  // When this script is first evaluated, it works out what kind of module loading scenario\n  // it is in (Node.js or a browser `<script>` tag), stashes a reference to its dependencies\n  // (currently that's just the WeakMap shim), and then finally attaches itself to whichever\n  // instance of Knockout.js it can find.\n\n  // A function that returns a new ES6-compatible WeakMap instance (using ES5 shim if needed).\n  // Instantiated by prepareExports, accounting for which module loader is being used.\n  var weakMapFactory;\n\n  // Extends a Knockout instance with Knockout-ES5 functionality\n  function attachToKo(ko) {\n    ko.track = track;\n    ko.untrack = untrack;\n    ko.getObservable = getObservable;\n    ko.valueHasMutated = valueHasMutated;\n    ko.defineProperty = defineComputedProperty;\n\n    // todo: test it, maybe added it to ko. directly\n    ko.es5 = {\n      getAllObservablesForObject: getAllObservablesForObject,\n      notifyWhenPresentOrFutureArrayValuesMutate: notifyWhenPresentOrFutureArrayValuesMutate,\n      isTracked: isTracked\n    };\n  }\n\n  // Determines which module loading scenario we're in, grabs dependencies, and attaches to KO\n  function prepareExports() {\n    if (typeof exports === 'object' && typeof module === 'object') {\n      // Node.js case - load KO and WeakMap modules synchronously\n      ko = require('knockout');\n      var WM = require('../lib/weakmap');\n      attachToKo(ko);\n      weakMapFactory = function() { return new WM(); };\n      module.exports = ko;\n    } else if (typeof define === 'function' && define.amd) {\n      define(['knockout'], function(koModule) {\n        ko = koModule;\n        attachToKo(koModule);\n        weakMapFactory = function() { return new global.WeakMap(); };\n        return koModule;\n      });\n    } else if ('ko' in global) {\n      // Non-module case - attach to the global instance, and assume a global WeakMap constructor\n      ko = global.ko;\n      attachToKo(global.ko);\n      weakMapFactory = function() { return new global.WeakMap(); };\n    }\n  }\n\n  prepareExports();\n\n})(this);","vimeo/vimeo-wrapper.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'vimeo'\n], function (Player) {\n    'use strict';\n\n    window.Vimeo = window.Vimeo || {\n        'Player': Player\n    };\n});\n","vimeo/player.min.js":"/*! @vimeo/player v2.16.4 | (c) 2022 Vimeo | MIT License | https://github.com/vimeo/player.js */\n!function(e,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=t():\"function\"==typeof define&&define.amd?define(t):((e=\"undefined\"!=typeof globalThis?globalThis:e||self).Vimeo=e.Vimeo||{},e.Vimeo.Player=t())}(this,function(){\"use strict\";function r(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,\"value\"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}var e=\"undefined\"!=typeof global&&\"[object global]\"==={}.toString.call(global);function i(e,t){return 0===e.indexOf(t.toLowerCase())?e:\"\".concat(t.toLowerCase()).concat(e.substr(0,1).toUpperCase()).concat(e.substr(1))}function l(e){return/^(https?:)?\\/\\/((player|www)\\.)?vimeo\\.com(?=$|\\/)/.test(e)}function u(e){var t=0<arguments.length&&void 0!==e?e:{},n=t.id,e=t.url,t=n||e;if(!t)throw new Error(\"An id or url must be passed, either in an options object or as a data-vimeo-id or data-vimeo-url attribute.\");if(e=t,!isNaN(parseFloat(e))&&isFinite(e)&&Math.floor(e)==e)return\"https://vimeo.com/\".concat(t);if(l(t))return t.replace(\"http:\",\"https:\");if(n)throw new TypeError(\"\u201c\".concat(n,\"\u201d is not a valid video id.\"));throw new TypeError(\"\u201c\".concat(t,\"\u201d is not a vimeo.com url.\"))}var t=void 0!==Array.prototype.indexOf,Player=\"undefined\"!=typeof window&&void 0!==window.postMessage;if(!(e||t&&Player))throw new Error(\"Sorry, the Vimeo Player API is not available in this browser.\");var n,o,a=\"undefined\"!=typeof globalThis?globalThis:\"undefined\"!=typeof window?window:\"undefined\"!=typeof global?global:\"undefined\"!=typeof self?self:{};function c(){if(void 0===this)throw new TypeError(\"Constructor WeakMap requires 'new'\");if(o(this,\"_id\",\"_WeakMap_\"+f()+\".\"+f()),0<arguments.length)throw new TypeError(\"WeakMap iterable is not supported\")}function s(e,t){if(!d(e)||!n.call(e,\"_id\"))throw new TypeError(t+\" method called on incompatible receiver \"+typeof e)}function f(){return Math.random().toString().substring(2)}function d(e){return Object(e)===e}(Player=\"undefined\"!=typeof self?self:\"undefined\"!=typeof window?window:a).WeakMap||(n=Object.prototype.hasOwnProperty,Player.WeakMap=((o=function(e,t,n){Object.defineProperty?Object.defineProperty(e,t,{configurable:!0,writable:!0,value:n}):e[t]=n})(c.prototype,\"delete\",function(e){if(s(this,\"delete\"),!d(e))return!1;var t=e[this._id];return!(!t||t[0]!==e)&&(delete e[this._id],!0)}),o(c.prototype,\"get\",function(e){if(s(this,\"get\"),d(e)){var t=e[this._id];return t&&t[0]===e?t[1]:void 0}}),o(c.prototype,\"has\",function(e){if(s(this,\"has\"),!d(e))return!1;var t=e[this._id];return!(!t||t[0]!==e)}),o(c.prototype,\"set\",function(e,t){if(s(this,\"set\"),!d(e))throw new TypeError(\"Invalid value used as weak map key\");var n=e[this._id];return n&&n[0]===e?n[1]=t:o(e,this._id,[e,t]),this}),o(c,\"_polyfill\",!0),c));var h,m=(function(e){var t,n,r;r=function(){var t,n,r,o,i,e=Object.prototype.toString,a=\"undefined\"!=typeof setImmediate?function(e){return setImmediate(e)}:setTimeout;try{Object.defineProperty({},\"x\",{}),t=function(e,t,n,r){return Object.defineProperty(e,t,{value:n,writable:!0,configurable:!1!==r})}}catch(e){t=function(e,t,n){return e[t]=n,e}}function u(e,t){this.fn=e,this.self=t,this.next=void 0}function l(e,t){y.add(e,t),n=n||a(y.drain)}function c(e){var t,n=typeof e;return\"function\"==typeof(t=null!=e&&(\"object\"==n||\"function\"==n)?e.then:t)&&t}function s(){for(var e=0;e<this.chain.length;e++)!function(e,t,n){var r,o;try{!1===t?n.reject(e.msg):(r=!0===t?e.msg:t.call(void 0,e.msg))===n.promise?n.reject(TypeError(\"Promise-chain cycle\")):(o=c(r))?o.call(r,n.resolve,n.reject):n.resolve(r)}catch(e){n.reject(e)}}(this,1===this.state?this.chain[e].success:this.chain[e].failure,this.chain[e]);this.chain.length=0}function f(e){var n,r=this;if(!r.triggered){r.triggered=!0,r.def&&(r=r.def);try{(n=c(e))?l(function(){var t=new m(r);try{n.call(e,function(){f.apply(t,arguments)},function(){d.apply(t,arguments)})}catch(e){d.call(t,e)}}):(r.msg=e,r.state=1,0<r.chain.length&&l(s,r))}catch(e){d.call(new m(r),e)}}}function d(e){var t=this;t.triggered||(t.triggered=!0,(t=t.def?t.def:t).msg=e,t.state=2,0<t.chain.length&&l(s,t))}function h(e,n,r,o){for(var t=0;t<n.length;t++)!function(t){e.resolve(n[t]).then(function(e){r(t,e)},o)}(t)}function m(e){this.def=e,this.triggered=!1}function v(e){this.promise=e,this.state=0,this.triggered=!1,this.chain=[],this.msg=void 0}function p(e){if(\"function\"!=typeof e)throw TypeError(\"Not a function\");if(0!==this.__NPO__)throw TypeError(\"Not a promise\");this.__NPO__=1;var r=new v(this);this.then=function(e,t){var n={success:\"function\"!=typeof e||e,failure:\"function\"==typeof t&&t};return n.promise=new this.constructor(function(e,t){if(\"function\"!=typeof e||\"function\"!=typeof t)throw TypeError(\"Not a function\");n.resolve=e,n.reject=t}),r.chain.push(n),0!==r.state&&l(s,r),n.promise},this.catch=function(e){return this.then(void 0,e)};try{e.call(void 0,function(e){f.call(r,e)},function(e){d.call(r,e)})}catch(e){d.call(r,e)}}var y={add:function(e,t){i=new u(e,t),o?o.next=i:r=i,o=i,i=void 0},drain:function(){var e=r;for(r=o=n=void 0;e;)e.fn.call(e.self),e=e.next}},g=t({},\"constructor\",p,!1);return t(p.prototype=g,\"__NPO__\",0,!1),t(p,\"resolve\",function(n){return n&&\"object\"==typeof n&&1===n.__NPO__?n:new this(function(e,t){if(\"function\"!=typeof e||\"function\"!=typeof t)throw TypeError(\"Not a function\");e(n)})}),t(p,\"reject\",function(n){return new this(function(e,t){if(\"function\"!=typeof e||\"function\"!=typeof t)throw TypeError(\"Not a function\");t(n)})}),t(p,\"all\",function(t){var a=this;return\"[object Array]\"!=e.call(t)?a.reject(TypeError(\"Not an array\")):0===t.length?a.resolve([]):new a(function(n,e){if(\"function\"!=typeof n||\"function\"!=typeof e)throw TypeError(\"Not a function\");var r=t.length,o=Array(r),i=0;h(a,t,function(e,t){o[e]=t,++i===r&&n(o)},e)})}),t(p,\"race\",function(t){var r=this;return\"[object Array]\"!=e.call(t)?r.reject(TypeError(\"Not an array\")):new r(function(n,e){if(\"function\"!=typeof n||\"function\"!=typeof e)throw TypeError(\"Not a function\");h(r,t,function(e,t){n(t)},e)})}),p},(n=a)[t=\"Promise\"]=n[t]||r(),e.exports&&(e.exports=n[t])}(h={exports:{}}),h.exports),v=new WeakMap;function p(e,t,n){var r=v.get(e.element)||{};t in r||(r[t]=[]),r[t].push(n),v.set(e.element,r)}function y(e,t){return(v.get(e.element)||{})[t]||[]}function g(e,t,n){var r=v.get(e.element)||{};if(!r[t])return!0;if(!n)return r[t]=[],v.set(e.element,r),!0;n=r[t].indexOf(n);return-1!==n&&r[t].splice(n,1),v.set(e.element,r),r[t]&&0===r[t].length}var w=[\"autopause\",\"autoplay\",\"background\",\"byline\",\"color\",\"controls\",\"dnt\",\"height\",\"id\",\"interactive_params\",\"keyboard\",\"loop\",\"maxheight\",\"maxwidth\",\"muted\",\"playsinline\",\"portrait\",\"responsive\",\"speed\",\"texttrack\",\"title\",\"transparent\",\"url\",\"width\"];function b(r,e){return w.reduce(function(e,t){var n=r.getAttribute(\"data-vimeo-\".concat(t));return!n&&\"\"!==n||(e[t]=\"\"===n?1:n),e},1<arguments.length&&void 0!==e?e:{})}function k(e,t){var n=e.html;if(!t)throw new TypeError(\"An element must be provided\");if(null!==t.getAttribute(\"data-vimeo-initialized\"))return t.querySelector(\"iframe\");e=document.createElement(\"div\");return e.innerHTML=n,t.appendChild(e.firstChild),t.setAttribute(\"data-vimeo-initialized\",\"true\"),t.querySelector(\"iframe\")}function E(i,e,t){var a=1<arguments.length&&void 0!==e?e:{},u=2<arguments.length?t:void 0;return new Promise(function(t,n){if(!l(i))throw new TypeError(\"\u201c\".concat(i,\"\u201d is not a vimeo.com url.\"));var e,r=\"https://vimeo.com/api/oembed.json?url=\".concat(encodeURIComponent(i));for(e in a)a.hasOwnProperty(e)&&(r+=\"&\".concat(e,\"=\").concat(encodeURIComponent(a[e])));var o=new(\"XDomainRequest\"in window?XDomainRequest:XMLHttpRequest);o.open(\"GET\",r,!0),o.onload=function(){if(404!==o.status)if(403!==o.status)try{var e=JSON.parse(o.responseText);if(403===e.domain_status_code)return k(e,u),void n(new Error(\"\u201c\".concat(i,\"\u201d is not embeddable.\")));t(e)}catch(e){n(e)}else n(new Error(\"\u201c\".concat(i,\"\u201d is not embeddable.\")));else n(new Error(\"\u201c\".concat(i,\"\u201d was not found.\")))},o.onerror=function(){var e=o.status?\" (\".concat(o.status,\")\"):\"\";n(new Error(\"There was an error fetching the embed code from Vimeo\".concat(e,\".\")))},o.send()})}function T(e){function n(e){\"console\"in window&&console.error&&console.error(\"There was an error creating an embed: \".concat(e))}e=0<arguments.length&&void 0!==e?e:document,e=[].slice.call(e.querySelectorAll(\"[data-vimeo-id], [data-vimeo-url]\"));e.forEach(function(t){try{if(null!==t.getAttribute(\"data-vimeo-defer\"))return;var e=b(t);E(u(e),e,t).then(function(e){return k(e,t)}).catch(n)}catch(e){n(e)}})}function P(e){if(\"string\"==typeof e)try{e=JSON.parse(e)}catch(e){return console.warn(e),{}}return e}function _(e,t,n){e.element.contentWindow&&e.element.contentWindow.postMessage&&(t={method:t},void 0!==n&&(t.value=n),8<=(n=parseFloat(navigator.userAgent.toLowerCase().replace(/^.*msie (\\d+).*$/,\"$1\")))&&n<10&&(t=JSON.stringify(t)),e.element.contentWindow.postMessage(t,e.origin))}function M(n,r){var t,e,o,i,a=[];(r=P(r)).event?(\"error\"===r.event&&y(n,r.data.method).forEach(function(e){var t=new Error(r.data.message);t.name=r.data.name,e.reject(t),g(n,r.data.method,e)}),a=y(n,\"event:\".concat(r.event)),t=r.data):r.method&&(e=n,o=r.method,(i=!((i=y(e,o)).length<1)&&(i=i.shift(),g(e,o,i),i))&&(a.push(i),t=r.value)),a.forEach(function(e){try{if(\"function\"==typeof e)return void e.call(n,t);e.resolve(t)}catch(e){}})}var N,F,x,C=new WeakMap,j=new WeakMap,A={},Player=function(){function Player(i){var e,a=this,t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};if(!function(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}(this,Player),window.jQuery&&i instanceof jQuery&&(1<i.length&&window.console&&console.warn&&console.warn(\"A jQuery object with multiple elements was passed, using the first element.\"),i=i[0]),\"undefined\"!=typeof document&&\"string\"==typeof i&&(i=document.getElementById(i)),e=i,!Boolean(e&&1===e.nodeType&&\"nodeName\"in e&&e.ownerDocument&&e.ownerDocument.defaultView))throw new TypeError(\"You must pass either a valid element or a valid id.\");if(\"IFRAME\"===i.nodeName||(r=i.querySelector(\"iframe\"))&&(i=r),\"IFRAME\"===i.nodeName&&!l(i.getAttribute(\"src\")||\"\"))throw new Error(\"The player element passed isn\u2019t a Vimeo embed.\");if(C.has(i))return C.get(i);this._window=i.ownerDocument.defaultView,this.element=i,this.origin=\"*\";var n,r=new m(function(r,o){var e;a._onMessage=function(e){if(l(e.origin)&&a.element.contentWindow===e.source){\"*\"===a.origin&&(a.origin=e.origin);var t=P(e.data);if(t&&\"error\"===t.event&&t.data&&\"ready\"===t.data.method){var n=new Error(t.data.message);return n.name=t.data.name,void o(n)}e=t&&\"ready\"===t.event,n=t&&\"ping\"===t.method;if(e||n)return a.element.setAttribute(\"data-ready\",\"true\"),void r();M(a,t)}},a._window.addEventListener(\"message\",a._onMessage),\"IFRAME\"!==a.element.nodeName&&E(u(e=b(i,t)),e,i).then(function(e){var t,n,r=k(e,i);return a.element=r,a._originalElement=i,t=i,n=r,r=v.get(t),v.set(n,r),v.delete(t),C.set(a.element,a),e}).catch(o)});return j.set(this,r),C.set(this.element,this),\"IFRAME\"===this.element.nodeName&&_(this,\"ping\"),A.isEnabled&&(n=function(){return A.exit()},this.fullscreenchangeHandler=function(){(A.isFullscreen?p:g)(a,\"event:exitFullscreen\",n),a.ready().then(function(){_(a,\"fullscreenchange\",A.isFullscreen)})},A.on(\"fullscreenchange\",this.fullscreenchangeHandler)),this}var e,t,n;return e=Player,(t=[{key:\"callMethod\",value:function(n){var r=this,o=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};return new m(function(e,t){return r.ready().then(function(){p(r,n,{resolve:e,reject:t}),_(r,n,o)}).catch(t)})}},{key:\"get\",value:function(n){var r=this;return new m(function(e,t){return n=i(n,\"get\"),r.ready().then(function(){p(r,n,{resolve:e,reject:t}),_(r,n)}).catch(t)})}},{key:\"set\",value:function(n,r){var o=this;return new m(function(e,t){if(n=i(n,\"set\"),null==r)throw new TypeError(\"There must be a value to set.\");return o.ready().then(function(){p(o,n,{resolve:e,reject:t}),_(o,n,r)}).catch(t)})}},{key:\"on\",value:function(e,t){if(!e)throw new TypeError(\"You must pass an event name.\");if(!t)throw new TypeError(\"You must pass a callback function.\");if(\"function\"!=typeof t)throw new TypeError(\"The callback must be a function.\");0===y(this,\"event:\".concat(e)).length&&this.callMethod(\"addEventListener\",e).catch(function(){}),p(this,\"event:\".concat(e),t)}},{key:\"off\",value:function(e,t){if(!e)throw new TypeError(\"You must pass an event name.\");if(t&&\"function\"!=typeof t)throw new TypeError(\"The callback must be a function.\");g(this,\"event:\".concat(e),t)&&this.callMethod(\"removeEventListener\",e).catch(function(e){})}},{key:\"loadVideo\",value:function(e){return this.callMethod(\"loadVideo\",e)}},{key:\"ready\",value:function(){var e=j.get(this)||new m(function(e,t){t(new Error(\"Unknown player. Probably unloaded.\"))});return m.resolve(e)}},{key:\"addCuePoint\",value:function(e){return this.callMethod(\"addCuePoint\",{time:e,data:1<arguments.length&&void 0!==arguments[1]?arguments[1]:{}})}},{key:\"removeCuePoint\",value:function(e){return this.callMethod(\"removeCuePoint\",e)}},{key:\"enableTextTrack\",value:function(e,t){if(!e)throw new TypeError(\"You must pass a language.\");return this.callMethod(\"enableTextTrack\",{language:e,kind:t})}},{key:\"disableTextTrack\",value:function(){return this.callMethod(\"disableTextTrack\")}},{key:\"pause\",value:function(){return this.callMethod(\"pause\")}},{key:\"play\",value:function(){return this.callMethod(\"play\")}},{key:\"requestFullscreen\",value:function(){return A.isEnabled?A.request(this.element):this.callMethod(\"requestFullscreen\")}},{key:\"exitFullscreen\",value:function(){return A.isEnabled?A.exit():this.callMethod(\"exitFullscreen\")}},{key:\"getFullscreen\",value:function(){return A.isEnabled?m.resolve(A.isFullscreen):this.get(\"fullscreen\")}},{key:\"requestPictureInPicture\",value:function(){return this.callMethod(\"requestPictureInPicture\")}},{key:\"exitPictureInPicture\",value:function(){return this.callMethod(\"exitPictureInPicture\")}},{key:\"getPictureInPicture\",value:function(){return this.get(\"pictureInPicture\")}},{key:\"unload\",value:function(){return this.callMethod(\"unload\")}},{key:\"destroy\",value:function(){var n=this;return new m(function(e){var t;j.delete(n),C.delete(n.element),n._originalElement&&(C.delete(n._originalElement),n._originalElement.removeAttribute(\"data-vimeo-initialized\")),n.element&&\"IFRAME\"===n.element.nodeName&&n.element.parentNode&&(n.element.parentNode.parentNode&&n._originalElement&&n._originalElement!==n.element.parentNode?n.element.parentNode.parentNode.removeChild(n.element.parentNode):n.element.parentNode.removeChild(n.element)),n.element&&\"DIV\"===n.element.nodeName&&n.element.parentNode&&(n.element.removeAttribute(\"data-vimeo-initialized\"),(t=n.element.querySelector(\"iframe\"))&&t.parentNode&&(t.parentNode.parentNode&&n._originalElement&&n._originalElement!==t.parentNode?t.parentNode.parentNode.removeChild(t.parentNode):t.parentNode.removeChild(t))),n._window.removeEventListener(\"message\",n._onMessage),A.isEnabled&&A.off(\"fullscreenchange\",n.fullscreenchangeHandler),e()})}},{key:\"getAutopause\",value:function(){return this.get(\"autopause\")}},{key:\"setAutopause\",value:function(e){return this.set(\"autopause\",e)}},{key:\"getBuffered\",value:function(){return this.get(\"buffered\")}},{key:\"getCameraProps\",value:function(){return this.get(\"cameraProps\")}},{key:\"setCameraProps\",value:function(e){return this.set(\"cameraProps\",e)}},{key:\"getChapters\",value:function(){return this.get(\"chapters\")}},{key:\"getCurrentChapter\",value:function(){return this.get(\"currentChapter\")}},{key:\"getColor\",value:function(){return this.get(\"color\")}},{key:\"setColor\",value:function(e){return this.set(\"color\",e)}},{key:\"getCuePoints\",value:function(){return this.get(\"cuePoints\")}},{key:\"getCurrentTime\",value:function(){return this.get(\"currentTime\")}},{key:\"setCurrentTime\",value:function(e){return this.set(\"currentTime\",e)}},{key:\"getDuration\",value:function(){return this.get(\"duration\")}},{key:\"getEnded\",value:function(){return this.get(\"ended\")}},{key:\"getLoop\",value:function(){return this.get(\"loop\")}},{key:\"setLoop\",value:function(e){return this.set(\"loop\",e)}},{key:\"setMuted\",value:function(e){return this.set(\"muted\",e)}},{key:\"getMuted\",value:function(){return this.get(\"muted\")}},{key:\"getPaused\",value:function(){return this.get(\"paused\")}},{key:\"getPlaybackRate\",value:function(){return this.get(\"playbackRate\")}},{key:\"setPlaybackRate\",value:function(e){return this.set(\"playbackRate\",e)}},{key:\"getPlayed\",value:function(){return this.get(\"played\")}},{key:\"getQualities\",value:function(){return this.get(\"qualities\")}},{key:\"getQuality\",value:function(){return this.get(\"quality\")}},{key:\"setQuality\",value:function(e){return this.set(\"quality\",e)}},{key:\"getSeekable\",value:function(){return this.get(\"seekable\")}},{key:\"getSeeking\",value:function(){return this.get(\"seeking\")}},{key:\"getTextTracks\",value:function(){return this.get(\"textTracks\")}},{key:\"getVideoEmbedCode\",value:function(){return this.get(\"videoEmbedCode\")}},{key:\"getVideoId\",value:function(){return this.get(\"videoId\")}},{key:\"getVideoTitle\",value:function(){return this.get(\"videoTitle\")}},{key:\"getVideoWidth\",value:function(){return this.get(\"videoWidth\")}},{key:\"getVideoHeight\",value:function(){return this.get(\"videoHeight\")}},{key:\"getVideoUrl\",value:function(){return this.get(\"videoUrl\")}},{key:\"getVolume\",value:function(){return this.get(\"volume\")}},{key:\"setVolume\",value:function(e){return this.set(\"volume\",e)}}])&&r(e.prototype,t),n&&r(e,n),Player}();return e||(N=function(){for(var e,t=[[\"requestFullscreen\",\"exitFullscreen\",\"fullscreenElement\",\"fullscreenEnabled\",\"fullscreenchange\",\"fullscreenerror\"],[\"webkitRequestFullscreen\",\"webkitExitFullscreen\",\"webkitFullscreenElement\",\"webkitFullscreenEnabled\",\"webkitfullscreenchange\",\"webkitfullscreenerror\"],[\"webkitRequestFullScreen\",\"webkitCancelFullScreen\",\"webkitCurrentFullScreenElement\",\"webkitCancelFullScreen\",\"webkitfullscreenchange\",\"webkitfullscreenerror\"],[\"mozRequestFullScreen\",\"mozCancelFullScreen\",\"mozFullScreenElement\",\"mozFullScreenEnabled\",\"mozfullscreenchange\",\"mozfullscreenerror\"],[\"msRequestFullscreen\",\"msExitFullscreen\",\"msFullscreenElement\",\"msFullscreenEnabled\",\"MSFullscreenChange\",\"MSFullscreenError\"]],n=0,r=t.length,o={};n<r;n++)if((e=t[n])&&e[1]in document){for(n=0;n<e.length;n++)o[t[0][n]]=e[n];return o}return!1}(),F={fullscreenchange:N.fullscreenchange,fullscreenerror:N.fullscreenerror},x={request:function(o){return new Promise(function(e,t){function n(){x.off(\"fullscreenchange\",n),e()}x.on(\"fullscreenchange\",n);var r=(o=o||document.documentElement)[N.requestFullscreen]();r instanceof Promise&&r.then(n).catch(t)})},exit:function(){return new Promise(function(t,e){var n,r;x.isFullscreen?(n=function e(){x.off(\"fullscreenchange\",e),t()},x.on(\"fullscreenchange\",n),(r=document[N.exitFullscreen]())instanceof Promise&&r.then(n).catch(e)):t()})},on:function(e,t){e=F[e];e&&document.addEventListener(e,t)},off:function(e,t){e=F[e];e&&document.removeEventListener(e,t)}},Object.defineProperties(x,{isFullscreen:{get:function(){return Boolean(document[N.fullscreenElement])}},element:{enumerable:!0,get:function(){return document[N.fullscreenElement]}},isEnabled:{enumerable:!0,get:function(){return Boolean(document[N.fullscreenEnabled])}}}),A=x,T(),function(e){var r=0<arguments.length&&void 0!==e?e:document;window.VimeoPlayerResizeEmbeds_||(window.VimeoPlayerResizeEmbeds_=!0,window.addEventListener(\"message\",function(e){if(l(e.origin)&&e.data&&\"spacechange\"===e.data.event)for(var t=r.querySelectorAll(\"iframe\"),n=0;n<t.length;n++)if(t[n].contentWindow===e.source){t[n].parentElement.style.paddingBottom=\"\".concat(e.data.data[0].bottom,\"px\");break}}))}()),Player});\n","Magento_UrlRewrite/js/url-rewrite-validation.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'mage/backend/form',\n    'mage/backend/validation'\n], function ($) {\n    'use strict';\n\n    return function (data, element) {\n\n        $(element).form().validation({\n            validationUrl: data.url\n        });\n    };\n});\n","jquery/jquery.metadata.js":"/*\n * Metadata - jQuery plugin for parsing metadata from elements\n *\n * Copyright (c) 2006 John Resig, Yehuda Katz, J\u00ef\u00bf\u00bd\u00c3\u00b6rn Zaefferer, Paul McLanahan\n *\n * Dual licensed under the MIT and GPL licenses:\n *   http://www.opensource.org/licenses/mit-license.php\n *   http://www.gnu.org/licenses/gpl.html\n *\n * Revision: $Id: jquery.metadata.js 3640 2007-10-11 18:34:38Z pmclanahan $\n *\n */\n\n/**\n * Sets the type of metadata to use. Metadata is encoded in JSON, and each property\n * in the JSON will become a property of the element itself.\n *\n * There are four supported types of metadata storage:\n *\n *   attr:  Inside an attribute. The name parameter indicates *which* attribute.\n *\n *   class: Inside the class attribute, wrapped in curly braces: { }\n *\n *   elem:  Inside a child element (e.g. a script tag). The\n *          name parameter indicates *which* element.\n *   html5: Values are stored in data-* attributes.\n *\n * The metadata for an element is loaded the first time the element is accessed via jQuery.\n *\n * As a result, you can define the metadata type, use $(expr) to load the metadata into the elements\n * matched by expr, then redefine the metadata type and run another $(expr) for other elements.\n *\n * @name $.metadata.setType\n *\n * @example <p id=\"one\" class=\"some_class {item_id: 1, item_label: 'Label'}\">This is a p</p>\n * @before $.metadata.setType(\"class\")\n * @after $(\"#one\").metadata().item_id == 1; $(\"#one\").metadata().item_label == \"Label\"\n * @desc Reads metadata from the class attribute\n *\n * @example <p id=\"one\" class=\"some_class\" data=\"{item_id: 1, item_label: 'Label'}\">This is a p</p>\n * @before $.metadata.setType(\"attr\", \"data\")\n * @after $(\"#one\").metadata().item_id == 1; $(\"#one\").metadata().item_label == \"Label\"\n * @desc Reads metadata from a \"data\" attribute\n *\n * @example <p id=\"one\" class=\"some_class\"><script>{item_id: 1, item_label: 'Label'}</script>This is a p</p>\n * @before $.metadata.setType(\"elem\", \"script\")\n * @after $(\"#one\").metadata().item_id == 1; $(\"#one\").metadata().item_label == \"Label\"\n * @desc Reads metadata from a nested script element\n *\n * @example <p id=\"one\" class=\"some_class\" data-item_id=\"1\" data-item_label=\"Label\">This is a p</p>\n * @before $.metadata.setType(\"html5\")\n * @after $(\"#one\").metadata().item_id == 1; $(\"#one\").metadata().item_label == \"Label\"\n * @desc Reads metadata from a series of data-* attributes\n *\n * @param String type The encoding type\n * @param String name The name of the attribute to be used to get metadata (optional)\n * @cat Plugins/Metadata\n * @descr Sets the type of encoding to be used when loading metadata for the first time\n * @type undefined\n * @see metadata()\n */\n(function (factory) {\n    if (typeof define === 'function' && define.amd) {\n        define([\"jquery\"], factory);\n    } else {\n        factory(jQuery);\n    }\n}(function ($) {\n\n\n    $.extend({\n        metadata : {\n            defaults : {\n                type: 'class',\n                name: 'metadata',\n                cre: /({.*})/,\n                single: 'metadata',\n                meta:'validate'\n            },\n            setType: function( type, name ){\n                this.defaults.type = type;\n                this.defaults.name = name;\n            },\n            get: function( elem, opts ){\n                var settings = $.extend({},this.defaults,opts);\n                // check for empty string in single property\n                if (!settings.single.length) {\n                    settings.single = 'metadata';\n                }\n                if (!settings.meta.length) {\n                    settings.meta = 'validate';\n                }\n\n                var data = $.data(elem, settings.single);\n                // returned cached data if it already exists\n                if ( data ) return data;\n\n                data = \"{}\";\n\n                var getData = function(data) {\n                    if(typeof data != \"string\") return data;\n\n                    if( data.indexOf('{') < 0 ) {\n                        data = eval(\"(\" + data + \")\");\n                    }\n                }\n\n                var getObject = function(data) {\n                    if(typeof data != \"string\") return data;\n\n                    data = eval(\"(\" + data + \")\");\n                    return data;\n                }\n\n                if ( settings.type == \"html5\" ) {\n                    var object = {};\n                    $( elem.attributes ).each(function() {\n                        var name = this.nodeName;\n                        if (name.indexOf('data-' + settings.meta) === 0) {\n                            name = name.replace(/^data-/, '');\n                        }\n                        else {\n                            return true;\n                        }\n                        object[name] = getObject(this.value);\n                    });\n                } else {\n                    if ( settings.type == \"class\" ) {\n                        var m = settings.cre.exec( elem.className );\n                        if ( m )\n                            data = m[1];\n                    } else if ( settings.type == \"elem\" ) {\n                        if( !elem.getElementsByTagName ) return;\n                        var e = elem.getElementsByTagName(settings.name);\n                        if ( e.length )\n                            data = $.trim(e[0].innerHTML);\n                    } else if ( elem.getAttribute != undefined ) {\n                        var attr = elem.getAttribute( settings.name );\n                        if ( attr )\n                            data = attr;\n                    }\n                    object = getObject(data.indexOf(\"{\") < 0 ? \"{\" + data + \"}\" : data);\n                }\n\n                $.data( elem, settings.single, object );\n                return object;\n            }\n        }\n    });\n\n    /**\n     * Returns the metadata object for the first member of the jQuery object.\n     *\n     * @name metadata\n     * @descr Returns element's metadata object\n     * @param Object opts An object contianing settings to override the defaults\n     * @type jQuery\n     * @cat Plugins/Metadata\n     */\n    $.fn.metadata = function( opts ){\n        return $.metadata.get( this[0], opts );\n    };\n\n}));","jquery/timepicker.js":"/*! jQuery Timepicker Addon - v1.6.3 - 2016-04-20\n* http://trentrichardson.com/examples/timepicker\n* Copyright (c) 2016 Trent Richardson; Licensed MIT */\n(function (factory) {\n    if (typeof define === 'function' && define.amd) {\n        define(['jquery', 'jquery-ui-modules/datepicker', 'jquery-ui-modules/slider'], factory);\n    } else {\n        factory(jQuery);\n    }\n}(function ($) {\n\n    /*\n    * Lets not redefine timepicker, Prevent \"Uncaught RangeError: Maximum call stack size exceeded\"\n    */\n    $.ui.timepicker = $.ui.timepicker || {};\n    if ($.ui.timepicker.version) {\n        return;\n    }\n\n    /*\n    * Extend jQueryUI, get it started with our version number\n    */\n    $.extend($.ui, {\n        timepicker: {\n            version: \"1.6.3\"\n        }\n    });\n\n    /*\n    * Timepicker manager.\n    * Use the singleton instance of this class, $.timepicker, to interact with the time picker.\n    * Settings for (groups of) time pickers are maintained in an instance object,\n    * allowing multiple different settings on the same page.\n    */\n    var Timepicker = function () {\n        this.regional = []; // Available regional settings, indexed by language code\n        this.regional[''] = { // Default regional settings\n            currentText: 'Now',\n            closeText: 'Done',\n            amNames: ['AM', 'A'],\n            pmNames: ['PM', 'P'],\n            timeFormat: 'HH:mm',\n            timeSuffix: '',\n            timeOnlyTitle: 'Choose Time',\n            timeText: 'Time',\n            hourText: 'Hour',\n            minuteText: 'Minute',\n            secondText: 'Second',\n            millisecText: 'Millisecond',\n            microsecText: 'Microsecond',\n            timezoneText: 'Time Zone',\n            isRTL: false\n        };\n        this._defaults = { // Global defaults for all the datetime picker instances\n            showButtonPanel: true,\n            timeOnly: false,\n            timeOnlyShowDate: false,\n            showHour: null,\n            showMinute: null,\n            showSecond: null,\n            showMillisec: null,\n            showMicrosec: null,\n            showTimezone: null,\n            showTime: true,\n            stepHour: 1,\n            stepMinute: 1,\n            stepSecond: 1,\n            stepMillisec: 1,\n            stepMicrosec: 1,\n            hour: 0,\n            minute: 0,\n            second: 0,\n            millisec: 0,\n            microsec: 0,\n            timezone: null,\n            hourMin: 0,\n            minuteMin: 0,\n            secondMin: 0,\n            millisecMin: 0,\n            microsecMin: 0,\n            hourMax: 23,\n            minuteMax: 59,\n            secondMax: 59,\n            millisecMax: 999,\n            microsecMax: 999,\n            minDateTime: null,\n            maxDateTime: null,\n            maxTime: null,\n            minTime: null,\n            onSelect: null,\n            hourGrid: 0,\n            minuteGrid: 0,\n            secondGrid: 0,\n            millisecGrid: 0,\n            microsecGrid: 0,\n            alwaysSetTime: true,\n            separator: ' ',\n            altFieldTimeOnly: true,\n            altTimeFormat: null,\n            altSeparator: null,\n            altTimeSuffix: null,\n            altRedirectFocus: true,\n            pickerTimeFormat: null,\n            pickerTimeSuffix: null,\n            showTimepicker: true,\n            timezoneList: null,\n            addSliderAccess: false,\n            sliderAccessArgs: null,\n            controlType: 'slider',\n            oneLine: false,\n            defaultValue: null,\n            parse: 'strict',\n            afterInject: null\n        };\n        $.extend(this._defaults, this.regional['']);\n    };\n\n    $.extend(Timepicker.prototype, {\n        $input: null,\n        $altInput: null,\n        $timeObj: null,\n        inst: null,\n        hour_slider: null,\n        minute_slider: null,\n        second_slider: null,\n        millisec_slider: null,\n        microsec_slider: null,\n        timezone_select: null,\n        maxTime: null,\n        minTime: null,\n        hour: 0,\n        minute: 0,\n        second: 0,\n        millisec: 0,\n        microsec: 0,\n        timezone: null,\n        hourMinOriginal: null,\n        minuteMinOriginal: null,\n        secondMinOriginal: null,\n        millisecMinOriginal: null,\n        microsecMinOriginal: null,\n        hourMaxOriginal: null,\n        minuteMaxOriginal: null,\n        secondMaxOriginal: null,\n        millisecMaxOriginal: null,\n        microsecMaxOriginal: null,\n        ampm: '',\n        formattedDate: '',\n        formattedTime: '',\n        formattedDateTime: '',\n        timezoneList: null,\n        units: ['hour', 'minute', 'second', 'millisec', 'microsec'],\n        support: {},\n        control: null,\n\n        /*\n        * Override the default settings for all instances of the time picker.\n        * @param  {Object} settings  object - the new settings to use as defaults (anonymous object)\n        * @return {Object} the manager object\n        */\n        setDefaults: function (settings) {\n            extendRemove(this._defaults, settings || {});\n            return this;\n        },\n\n        /*\n        * Create a new Timepicker instance\n        */\n        _newInst: function ($input, opts) {\n            var tp_inst = new Timepicker(),\n                inlineSettings = {},\n                fns = {},\n                overrides, i;\n\n            for (var attrName in this._defaults) {\n                if (this._defaults.hasOwnProperty(attrName)) {\n                    var attrValue = $input.attr('time:' + attrName);\n                    if (attrValue) {\n                        try {\n                            inlineSettings[attrName] = eval(attrValue);\n                        } catch (err) {\n                            inlineSettings[attrName] = attrValue;\n                        }\n                    }\n                }\n            }\n\n            overrides = {\n                beforeShow: function (input, dp_inst) {\n                    if ($.isFunction(tp_inst._defaults.evnts.beforeShow)) {\n                        return tp_inst._defaults.evnts.beforeShow.call($input[0], input, dp_inst, tp_inst);\n                    }\n                },\n                onChangeMonthYear: function (year, month, dp_inst) {\n                    // Update the time as well : this prevents the time from disappearing from the $input field.\n                    // tp_inst._updateDateTime(dp_inst);\n                    if ($.isFunction(tp_inst._defaults.evnts.onChangeMonthYear)) {\n                        tp_inst._defaults.evnts.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst);\n                    }\n                },\n                onClose: function (dateText, dp_inst) {\n                    if (tp_inst.timeDefined === true && $input.val() !== '') {\n                        tp_inst._updateDateTime(dp_inst);\n                    }\n                    if ($.isFunction(tp_inst._defaults.evnts.onClose)) {\n                        tp_inst._defaults.evnts.onClose.call($input[0], dateText, dp_inst, tp_inst);\n                    }\n                }\n            };\n            for (i in overrides) {\n                if (overrides.hasOwnProperty(i)) {\n                    fns[i] = opts[i] || this._defaults[i] || null;\n                }\n            }\n\n            tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, opts, overrides, {\n                evnts: fns,\n                timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker');\n            });\n            tp_inst.amNames = $.map(tp_inst._defaults.amNames, function (val) {\n                return val.toUpperCase();\n            });\n            tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function (val) {\n                return val.toUpperCase();\n            });\n\n            // detect which units are supported\n            tp_inst.support = detectSupport(\n                tp_inst._defaults.timeFormat +\n                (tp_inst._defaults.pickerTimeFormat ? tp_inst._defaults.pickerTimeFormat : '') +\n                (tp_inst._defaults.altTimeFormat ? tp_inst._defaults.altTimeFormat : ''));\n\n            // controlType is string - key to our this._controls\n            if (typeof(tp_inst._defaults.controlType) === 'string') {\n                if (tp_inst._defaults.controlType === 'slider' && typeof($.ui.slider) === 'undefined') {\n                    tp_inst._defaults.controlType = 'select';\n                }\n                tp_inst.control = tp_inst._controls[tp_inst._defaults.controlType];\n            }\n            // controlType is an object and must implement create, options, value methods\n            else {\n                tp_inst.control = tp_inst._defaults.controlType;\n            }\n\n            // prep the timezone options\n            var timezoneList = [-720, -660, -600, -570, -540, -480, -420, -360, -300, -270, -240, -210, -180, -120, -60,\n                0, 60, 120, 180, 210, 240, 270, 300, 330, 345, 360, 390, 420, 480, 525, 540, 570, 600, 630, 660, 690, 720, 765, 780, 840];\n            if (tp_inst._defaults.timezoneList !== null) {\n                timezoneList = tp_inst._defaults.timezoneList;\n            }\n            var tzl = timezoneList.length, tzi = 0, tzv = null;\n            if (tzl > 0 && typeof timezoneList[0] !== 'object') {\n                for (; tzi < tzl; tzi++) {\n                    tzv = timezoneList[tzi];\n                    timezoneList[tzi] = { value: tzv, label: $.timepicker.timezoneOffsetString(tzv, tp_inst.support.iso8601) };\n                }\n            }\n            tp_inst._defaults.timezoneList = timezoneList;\n\n            // set the default units\n            tp_inst.timezone = tp_inst._defaults.timezone !== null ? $.timepicker.timezoneOffsetNumber(tp_inst._defaults.timezone) :\n                ((new Date()).getTimezoneOffset() * -1);\n            tp_inst.hour = tp_inst._defaults.hour < tp_inst._defaults.hourMin ? tp_inst._defaults.hourMin :\n                tp_inst._defaults.hour > tp_inst._defaults.hourMax ? tp_inst._defaults.hourMax : tp_inst._defaults.hour;\n            tp_inst.minute = tp_inst._defaults.minute < tp_inst._defaults.minuteMin ? tp_inst._defaults.minuteMin :\n                tp_inst._defaults.minute > tp_inst._defaults.minuteMax ? tp_inst._defaults.minuteMax : tp_inst._defaults.minute;\n            tp_inst.second = tp_inst._defaults.second < tp_inst._defaults.secondMin ? tp_inst._defaults.secondMin :\n                tp_inst._defaults.second > tp_inst._defaults.secondMax ? tp_inst._defaults.secondMax : tp_inst._defaults.second;\n            tp_inst.millisec = tp_inst._defaults.millisec < tp_inst._defaults.millisecMin ? tp_inst._defaults.millisecMin :\n                tp_inst._defaults.millisec > tp_inst._defaults.millisecMax ? tp_inst._defaults.millisecMax : tp_inst._defaults.millisec;\n            tp_inst.microsec = tp_inst._defaults.microsec < tp_inst._defaults.microsecMin ? tp_inst._defaults.microsecMin :\n                tp_inst._defaults.microsec > tp_inst._defaults.microsecMax ? tp_inst._defaults.microsecMax : tp_inst._defaults.microsec;\n            tp_inst.ampm = '';\n            tp_inst.$input = $input;\n\n            if (tp_inst._defaults.altField) {\n                tp_inst.$altInput = $(tp_inst._defaults.altField);\n                if (tp_inst._defaults.altRedirectFocus === true) {\n                    tp_inst.$altInput.css({\n                        cursor: 'pointer'\n                    }).focus(function () {\n                        $input.trigger(\"focus\");\n                    });\n                }\n            }\n\n            if (tp_inst._defaults.minDate === 0 || tp_inst._defaults.minDateTime === 0) {\n                tp_inst._defaults.minDate = new Date();\n            }\n            if (tp_inst._defaults.maxDate === 0 || tp_inst._defaults.maxDateTime === 0) {\n                tp_inst._defaults.maxDate = new Date();\n            }\n\n            // datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime..\n            if (tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date) {\n                tp_inst._defaults.minDateTime = new Date(tp_inst._defaults.minDate.getTime());\n            }\n            if (tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date) {\n                tp_inst._defaults.minDate = new Date(tp_inst._defaults.minDateTime.getTime());\n            }\n            if (tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date) {\n                tp_inst._defaults.maxDateTime = new Date(tp_inst._defaults.maxDate.getTime());\n            }\n            if (tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date) {\n                tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime());\n            }\n            tp_inst.$input.bind('focus', function () {\n                tp_inst._onFocus();\n            });\n\n            return tp_inst;\n        },\n\n        /*\n        * add our sliders to the calendar\n        */\n        _addTimePicker: function (dp_inst) {\n            var currDT = $.trim((this.$altInput && this._defaults.altFieldTimeOnly) ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val());\n\n            this.timeDefined = this._parseTime(currDT);\n            this._limitMinMaxDateTime(dp_inst, false);\n            this._injectTimePicker();\n            this._afterInject();\n        },\n\n        /*\n        * parse the time string from input value or _setTime\n        */\n        _parseTime: function (timeString, withDate) {\n            if (!this.inst) {\n                this.inst = $.datepicker._getInst(this.$input[0]);\n            }\n\n            if (withDate || !this._defaults.timeOnly) {\n                var dp_dateFormat = $.datepicker._get(this.inst, 'dateFormat');\n                try {\n                    var parseRes = parseDateTimeInternal(dp_dateFormat, this._defaults.timeFormat, timeString, $.datepicker._getFormatConfig(this.inst), this._defaults);\n                    if (!parseRes.timeObj) {\n                        return false;\n                    }\n                    $.extend(this, parseRes.timeObj);\n                } catch (err) {\n                    $.timepicker.log(\"Error parsing the date/time string: \" + err +\n                        \"\\ndate/time string = \" + timeString +\n                        \"\\ntimeFormat = \" + this._defaults.timeFormat +\n                        \"\\ndateFormat = \" + dp_dateFormat);\n                    return false;\n                }\n                return true;\n            } else {\n                var timeObj = $.datepicker.parseTime(this._defaults.timeFormat, timeString, this._defaults);\n                if (!timeObj) {\n                    return false;\n                }\n                $.extend(this, timeObj);\n                return true;\n            }\n        },\n\n        /*\n        * Handle callback option after injecting timepicker\n        */\n        _afterInject: function() {\n            var o = this.inst.settings;\n            if ($.isFunction(o.afterInject)) {\n                o.afterInject.call(this);\n            }\n        },\n\n        /*\n        * generate and inject html for timepicker into ui datepicker\n        */\n        _injectTimePicker: function () {\n            var $dp = this.inst.dpDiv,\n                o = this.inst.settings,\n                tp_inst = this,\n                litem = '',\n                uitem = '',\n                show = null,\n                max = {},\n                gridSize = {},\n                size = null,\n                i = 0,\n                l = 0;\n\n            // Prevent displaying twice\n            if ($dp.find(\"div.ui-timepicker-div\").length === 0 && o.showTimepicker) {\n                var noDisplay = ' ui_tpicker_unit_hide',\n                    html = '<div class=\"ui-timepicker-div' + (o.isRTL ? ' ui-timepicker-rtl' : '') + (o.oneLine && o.controlType === 'select' ? ' ui-timepicker-oneLine' : '') + '\"><dl>' + '<dt class=\"ui_tpicker_time_label' + ((o.showTime) ? '' : noDisplay) + '\">' + o.timeText + '</dt>' +\n                        '<dd class=\"ui_tpicker_time '+ ((o.showTime) ? '' : noDisplay) + '\"><input class=\"ui_tpicker_time_input\" ' + (o.timeInput ? '' : 'disabled') + '/></dd>';\n\n                // Create the markup\n                for (i = 0, l = this.units.length; i < l; i++) {\n                    litem = this.units[i];\n                    uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1);\n                    show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem];\n\n                    // Added by Peter Medeiros:\n                    // - Figure out what the hour/minute/second max should be based on the step values.\n                    // - Example: if stepMinute is 15, then minMax is 45.\n                    max[litem] = parseInt((o[litem + 'Max'] - ((o[litem + 'Max'] - o[litem + 'Min']) % o['step' + uitem])), 10);\n                    gridSize[litem] = 0;\n\n                    html += '<dt class=\"ui_tpicker_' + litem + '_label' + (show ? '' : noDisplay) + '\">' + o[litem + 'Text'] + '</dt>' +\n                        '<dd class=\"ui_tpicker_' + litem + (show ? '' : noDisplay) + '\"><div class=\"ui_tpicker_' + litem + '_slider' + (show ? '' : noDisplay) + '\"></div>';\n\n                    if (show && o[litem + 'Grid'] > 0) {\n                        html += '<div style=\"padding-left: 1px\"><table class=\"ui-tpicker-grid-label\"><tr>';\n\n                        if (litem === 'hour') {\n                            for (var h = o[litem + 'Min']; h <= max[litem]; h += parseInt(o[litem + 'Grid'], 10)) {\n                                gridSize[litem]++;\n                                var tmph = $.datepicker.formatTime(this.support.ampm ? 'hht' : 'HH', {hour: h}, o);\n                                html += '<td data-for=\"' + litem + '\">' + tmph + '</td>';\n                            }\n                        }\n                        else {\n                            for (var m = o[litem + 'Min']; m <= max[litem]; m += parseInt(o[litem + 'Grid'], 10)) {\n                                gridSize[litem]++;\n                                html += '<td data-for=\"' + litem + '\">' + ((m < 10) ? '0' : '') + m + '</td>';\n                            }\n                        }\n\n                        html += '</tr></table></div>';\n                    }\n                    html += '</dd>';\n                }\n\n                // Timezone\n                var showTz = o.showTimezone !== null ? o.showTimezone : this.support.timezone;\n                html += '<dt class=\"ui_tpicker_timezone_label' + (showTz ? '' : noDisplay) + '\">' + o.timezoneText + '</dt>';\n                html += '<dd class=\"ui_tpicker_timezone' + (showTz ? '' : noDisplay) + '\"></dd>';\n\n                // Create the elements from string\n                html += '</dl></div>';\n                var $tp = $(html);\n\n                // if we only want time picker...\n                if (o.timeOnly === true) {\n                    $tp.prepend('<div class=\"ui-widget-header ui-helper-clearfix ui-corner-all\">' + '<div class=\"ui-datepicker-title\">' + o.timeOnlyTitle + '</div>' + '</div>');\n                    $dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide();\n                }\n\n                // add sliders, adjust grids, add events\n                for (i = 0, l = tp_inst.units.length; i < l; i++) {\n                    litem = tp_inst.units[i];\n                    uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1);\n                    show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem];\n\n                    // add the slider\n                    tp_inst[litem + '_slider'] = tp_inst.control.create(tp_inst, $tp.find('.ui_tpicker_' + litem + '_slider'), litem, tp_inst[litem], o[litem + 'Min'], max[litem], o['step' + uitem]);\n\n                    // adjust the grid and add click event\n                    if (show && o[litem + 'Grid'] > 0) {\n                        size = 100 * gridSize[litem] * o[litem + 'Grid'] / (max[litem] - o[litem + 'Min']);\n                        $tp.find('.ui_tpicker_' + litem + ' table').css({\n                            width: size + \"%\",\n                            marginLeft: o.isRTL ? '0' : ((size / (-2 * gridSize[litem])) + \"%\"),\n                            marginRight: o.isRTL ? ((size / (-2 * gridSize[litem])) + \"%\") : '0',\n                            borderCollapse: 'collapse'\n                        }).find(\"td\").click(function (e) {\n                            var $t = $(this),\n                                h = $t.html(),\n                                n = parseInt(h.replace(/[^0-9]/g), 10),\n                                ap = h.replace(/[^apm]/ig),\n                                f = $t.data('for'); // loses scope, so we use data-for\n\n                            if (f === 'hour') {\n                                if (ap.indexOf('p') !== -1 && n < 12) {\n                                    n += 12;\n                                }\n                                else {\n                                    if (ap.indexOf('a') !== -1 && n === 12) {\n                                        n = 0;\n                                    }\n                                }\n                            }\n\n                            tp_inst.control.value(tp_inst, tp_inst[f + '_slider'], litem, n);\n\n                            tp_inst._onTimeChange();\n                            tp_inst._onSelectHandler();\n                        }).css({\n                            cursor: 'pointer',\n                            width: (100 / gridSize[litem]) + '%',\n                            textAlign: 'center',\n                            overflow: 'hidden'\n                        });\n                    } // end if grid > 0\n                } // end for loop\n\n                // Add timezone options\n                this.timezone_select = $tp.find('.ui_tpicker_timezone').append('<select></select>').find(\"select\");\n                $.fn.append.apply(this.timezone_select,\n                    $.map(o.timezoneList, function (val, idx) {\n                        return $(\"<option />\").val(typeof val === \"object\" ? val.value : val).text(typeof val === \"object\" ? val.label : val);\n                    }));\n                if (typeof(this.timezone) !== \"undefined\" && this.timezone !== null && this.timezone !== \"\") {\n                    var local_timezone = (new Date(this.inst.selectedYear, this.inst.selectedMonth, this.inst.selectedDay, 12)).getTimezoneOffset() * -1;\n                    if (local_timezone === this.timezone) {\n                        selectLocalTimezone(tp_inst);\n                    } else {\n                        this.timezone_select.val(this.timezone);\n                    }\n                } else {\n                    if (typeof(this.hour) !== \"undefined\" && this.hour !== null && this.hour !== \"\") {\n                        this.timezone_select.val(o.timezone);\n                    } else {\n                        selectLocalTimezone(tp_inst);\n                    }\n                }\n                this.timezone_select.change(function () {\n                    tp_inst._onTimeChange();\n                    tp_inst._onSelectHandler();\n                    tp_inst._afterInject();\n                });\n                // End timezone options\n\n                // inject timepicker into datepicker\n                var $buttonPanel = $dp.find('.ui-datepicker-buttonpane');\n                if ($buttonPanel.length) {\n                    $buttonPanel.before($tp);\n                } else {\n                    $dp.append($tp);\n                }\n\n                this.$timeObj = $tp.find('.ui_tpicker_time_input');\n                this.$timeObj.change(function () {\n                    var timeFormat = tp_inst.inst.settings.timeFormat;\n                    var parsedTime = $.datepicker.parseTime(timeFormat, this.value);\n                    var update = new Date();\n                    if (parsedTime) {\n                        update.setHours(parsedTime.hour);\n                        update.setMinutes(parsedTime.minute);\n                        update.setSeconds(parsedTime.second);\n                        $.datepicker._setTime(tp_inst.inst, update);\n                    } else {\n                        this.value = tp_inst.formattedTime;\n                        this.blur();\n                    }\n                });\n\n                if (this.inst !== null) {\n                    var timeDefined = this.timeDefined;\n                    this._onTimeChange();\n                    this.timeDefined = timeDefined;\n                }\n\n                // slideAccess integration: http://trentrichardson.com/2011/11/11/jquery-ui-sliders-and-touch-accessibility/\n                if (this._defaults.addSliderAccess) {\n                    var sliderAccessArgs = this._defaults.sliderAccessArgs,\n                        rtl = this._defaults.isRTL;\n                    sliderAccessArgs.isRTL = rtl;\n\n                    setTimeout(function () { // fix for inline mode\n                        if ($tp.find('.ui-slider-access').length === 0) {\n                            $tp.find('.ui-slider:visible').sliderAccess(sliderAccessArgs);\n\n                            // fix any grids since sliders are shorter\n                            var sliderAccessWidth = $tp.find('.ui-slider-access:eq(0)').outerWidth(true);\n                            if (sliderAccessWidth) {\n                                $tp.find('table:visible').each(function () {\n                                    var $g = $(this),\n                                        oldWidth = $g.outerWidth(),\n                                        oldMarginLeft = $g.css(rtl ? 'marginRight' : 'marginLeft').toString().replace('%', ''),\n                                        newWidth = oldWidth - sliderAccessWidth,\n                                        newMarginLeft = ((oldMarginLeft * newWidth) / oldWidth) + '%',\n                                        css = { width: newWidth, marginRight: 0, marginLeft: 0 };\n                                    css[rtl ? 'marginRight' : 'marginLeft'] = newMarginLeft;\n                                    $g.css(css);\n                                });\n                            }\n                        }\n                    }, 10);\n                }\n                // end slideAccess integration\n\n                tp_inst._limitMinMaxDateTime(this.inst, true);\n            }\n        },\n\n        /*\n        * This function tries to limit the ability to go outside the\n        * min/max date range\n        */\n        _limitMinMaxDateTime: function (dp_inst, adjustSliders) {\n            var o = this._defaults,\n                dp_date = new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay);\n\n            if (!this._defaults.showTimepicker) {\n                return;\n            } // No time so nothing to check here\n\n            if ($.datepicker._get(dp_inst, 'minDateTime') !== null && $.datepicker._get(dp_inst, 'minDateTime') !== undefined && dp_date) {\n                var minDateTime = $.datepicker._get(dp_inst, 'minDateTime'),\n                    minDateTimeDate = new Date(minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), 0, 0, 0, 0);\n\n                if (this.hourMinOriginal === null || this.minuteMinOriginal === null || this.secondMinOriginal === null || this.millisecMinOriginal === null || this.microsecMinOriginal === null) {\n                    this.hourMinOriginal = o.hourMin;\n                    this.minuteMinOriginal = o.minuteMin;\n                    this.secondMinOriginal = o.secondMin;\n                    this.millisecMinOriginal = o.millisecMin;\n                    this.microsecMinOriginal = o.microsecMin;\n                }\n\n                if (dp_inst.settings.timeOnly || minDateTimeDate.getTime() === dp_date.getTime()) {\n                    this._defaults.hourMin = minDateTime.getHours();\n                    if (this.hour <= this._defaults.hourMin) {\n                        this.hour = this._defaults.hourMin;\n                        this._defaults.minuteMin = minDateTime.getMinutes();\n                        if (this.minute <= this._defaults.minuteMin) {\n                            this.minute = this._defaults.minuteMin;\n                            this._defaults.secondMin = minDateTime.getSeconds();\n                            if (this.second <= this._defaults.secondMin) {\n                                this.second = this._defaults.secondMin;\n                                this._defaults.millisecMin = minDateTime.getMilliseconds();\n                                if (this.millisec <= this._defaults.millisecMin) {\n                                    this.millisec = this._defaults.millisecMin;\n                                    this._defaults.microsecMin = minDateTime.getMicroseconds();\n                                } else {\n                                    if (this.microsec < this._defaults.microsecMin) {\n                                        this.microsec = this._defaults.microsecMin;\n                                    }\n                                    this._defaults.microsecMin = this.microsecMinOriginal;\n                                }\n                            } else {\n                                this._defaults.millisecMin = this.millisecMinOriginal;\n                                this._defaults.microsecMin = this.microsecMinOriginal;\n                            }\n                        } else {\n                            this._defaults.secondMin = this.secondMinOriginal;\n                            this._defaults.millisecMin = this.millisecMinOriginal;\n                            this._defaults.microsecMin = this.microsecMinOriginal;\n                        }\n                    } else {\n                        this._defaults.minuteMin = this.minuteMinOriginal;\n                        this._defaults.secondMin = this.secondMinOriginal;\n                        this._defaults.millisecMin = this.millisecMinOriginal;\n                        this._defaults.microsecMin = this.microsecMinOriginal;\n                    }\n                } else {\n                    this._defaults.hourMin = this.hourMinOriginal;\n                    this._defaults.minuteMin = this.minuteMinOriginal;\n                    this._defaults.secondMin = this.secondMinOriginal;\n                    this._defaults.millisecMin = this.millisecMinOriginal;\n                    this._defaults.microsecMin = this.microsecMinOriginal;\n                }\n            }\n\n            if ($.datepicker._get(dp_inst, 'maxDateTime') !== null && $.datepicker._get(dp_inst, 'maxDateTime') !== undefined && dp_date) {\n                var maxDateTime = $.datepicker._get(dp_inst, 'maxDateTime'),\n                    maxDateTimeDate = new Date(maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), 0, 0, 0, 0);\n\n                if (this.hourMaxOriginal === null || this.minuteMaxOriginal === null || this.secondMaxOriginal === null || this.millisecMaxOriginal === null) {\n                    this.hourMaxOriginal = o.hourMax;\n                    this.minuteMaxOriginal = o.minuteMax;\n                    this.secondMaxOriginal = o.secondMax;\n                    this.millisecMaxOriginal = o.millisecMax;\n                    this.microsecMaxOriginal = o.microsecMax;\n                }\n\n                if (dp_inst.settings.timeOnly || maxDateTimeDate.getTime() === dp_date.getTime()) {\n                    this._defaults.hourMax = maxDateTime.getHours();\n                    if (this.hour >= this._defaults.hourMax) {\n                        this.hour = this._defaults.hourMax;\n                        this._defaults.minuteMax = maxDateTime.getMinutes();\n                        if (this.minute >= this._defaults.minuteMax) {\n                            this.minute = this._defaults.minuteMax;\n                            this._defaults.secondMax = maxDateTime.getSeconds();\n                            if (this.second >= this._defaults.secondMax) {\n                                this.second = this._defaults.secondMax;\n                                this._defaults.millisecMax = maxDateTime.getMilliseconds();\n                                if (this.millisec >= this._defaults.millisecMax) {\n                                    this.millisec = this._defaults.millisecMax;\n                                    this._defaults.microsecMax = maxDateTime.getMicroseconds();\n                                } else {\n                                    if (this.microsec > this._defaults.microsecMax) {\n                                        this.microsec = this._defaults.microsecMax;\n                                    }\n                                    this._defaults.microsecMax = this.microsecMaxOriginal;\n                                }\n                            } else {\n                                this._defaults.millisecMax = this.millisecMaxOriginal;\n                                this._defaults.microsecMax = this.microsecMaxOriginal;\n                            }\n                        } else {\n                            this._defaults.secondMax = this.secondMaxOriginal;\n                            this._defaults.millisecMax = this.millisecMaxOriginal;\n                            this._defaults.microsecMax = this.microsecMaxOriginal;\n                        }\n                    } else {\n                        this._defaults.minuteMax = this.minuteMaxOriginal;\n                        this._defaults.secondMax = this.secondMaxOriginal;\n                        this._defaults.millisecMax = this.millisecMaxOriginal;\n                        this._defaults.microsecMax = this.microsecMaxOriginal;\n                    }\n                } else {\n                    this._defaults.hourMax = this.hourMaxOriginal;\n                    this._defaults.minuteMax = this.minuteMaxOriginal;\n                    this._defaults.secondMax = this.secondMaxOriginal;\n                    this._defaults.millisecMax = this.millisecMaxOriginal;\n                    this._defaults.microsecMax = this.microsecMaxOriginal;\n                }\n            }\n\n            if (dp_inst.settings.minTime!==null) {\n                var tempMinTime=new Date(\"01/01/1970 \" + dp_inst.settings.minTime);\n                if (this.hour<tempMinTime.getHours()) {\n                    this.hour=this._defaults.hourMin=tempMinTime.getHours();\n                    this.minute=this._defaults.minuteMin=tempMinTime.getMinutes();\n                } else if (this.hour===tempMinTime.getHours() && this.minute<tempMinTime.getMinutes()) {\n                    this.minute=this._defaults.minuteMin=tempMinTime.getMinutes();\n                } else {\n                    if (this._defaults.hourMin<tempMinTime.getHours()) {\n                        this._defaults.hourMin=tempMinTime.getHours();\n                        this._defaults.minuteMin=tempMinTime.getMinutes();\n                    } else if (this._defaults.hourMin===tempMinTime.getHours()===this.hour && this._defaults.minuteMin<tempMinTime.getMinutes()) {\n                        this._defaults.minuteMin=tempMinTime.getMinutes();\n                    } else {\n                        this._defaults.minuteMin=0;\n                    }\n                }\n            }\n\n            if (dp_inst.settings.maxTime!==null) {\n                var tempMaxTime=new Date(\"01/01/1970 \" + dp_inst.settings.maxTime);\n                if (this.hour>tempMaxTime.getHours()) {\n                    this.hour=this._defaults.hourMax=tempMaxTime.getHours();\n                    this.minute=this._defaults.minuteMax=tempMaxTime.getMinutes();\n                } else if (this.hour===tempMaxTime.getHours() && this.minute>tempMaxTime.getMinutes()) {\n                    this.minute=this._defaults.minuteMax=tempMaxTime.getMinutes();\n                } else {\n                    if (this._defaults.hourMax>tempMaxTime.getHours()) {\n                        this._defaults.hourMax=tempMaxTime.getHours();\n                        this._defaults.minuteMax=tempMaxTime.getMinutes();\n                    } else if (this._defaults.hourMax===tempMaxTime.getHours()===this.hour && this._defaults.minuteMax>tempMaxTime.getMinutes()) {\n                        this._defaults.minuteMax=tempMaxTime.getMinutes();\n                    } else {\n                        this._defaults.minuteMax=59;\n                    }\n                }\n            }\n\n            if (adjustSliders !== undefined && adjustSliders === true) {\n                var hourMax = parseInt((this._defaults.hourMax - ((this._defaults.hourMax - this._defaults.hourMin) % this._defaults.stepHour)), 10),\n                    minMax = parseInt((this._defaults.minuteMax - ((this._defaults.minuteMax - this._defaults.minuteMin) % this._defaults.stepMinute)), 10),\n                    secMax = parseInt((this._defaults.secondMax - ((this._defaults.secondMax - this._defaults.secondMin) % this._defaults.stepSecond)), 10),\n                    millisecMax = parseInt((this._defaults.millisecMax - ((this._defaults.millisecMax - this._defaults.millisecMin) % this._defaults.stepMillisec)), 10),\n                    microsecMax = parseInt((this._defaults.microsecMax - ((this._defaults.microsecMax - this._defaults.microsecMin) % this._defaults.stepMicrosec)), 10);\n\n                if (this.hour_slider) {\n                    this.control.options(this, this.hour_slider, 'hour', { min: this._defaults.hourMin, max: hourMax, step: this._defaults.stepHour });\n                    this.control.value(this, this.hour_slider, 'hour', this.hour - (this.hour % this._defaults.stepHour));\n                }\n                if (this.minute_slider) {\n                    this.control.options(this, this.minute_slider, 'minute', { min: this._defaults.minuteMin, max: minMax, step: this._defaults.stepMinute });\n                    this.control.value(this, this.minute_slider, 'minute', this.minute - (this.minute % this._defaults.stepMinute));\n                }\n                if (this.second_slider) {\n                    this.control.options(this, this.second_slider, 'second', { min: this._defaults.secondMin, max: secMax, step: this._defaults.stepSecond });\n                    this.control.value(this, this.second_slider, 'second', this.second - (this.second % this._defaults.stepSecond));\n                }\n                if (this.millisec_slider) {\n                    this.control.options(this, this.millisec_slider, 'millisec', { min: this._defaults.millisecMin, max: millisecMax, step: this._defaults.stepMillisec });\n                    this.control.value(this, this.millisec_slider, 'millisec', this.millisec - (this.millisec % this._defaults.stepMillisec));\n                }\n                if (this.microsec_slider) {\n                    this.control.options(this, this.microsec_slider, 'microsec', { min: this._defaults.microsecMin, max: microsecMax, step: this._defaults.stepMicrosec });\n                    this.control.value(this, this.microsec_slider, 'microsec', this.microsec - (this.microsec % this._defaults.stepMicrosec));\n                }\n            }\n\n        },\n\n        /*\n        * when a slider moves, set the internal time...\n        * on time change is also called when the time is updated in the text field\n        */\n        _onTimeChange: function () {\n            if (!this._defaults.showTimepicker) {\n                return;\n            }\n            var hour = (this.hour_slider) ? this.control.value(this, this.hour_slider, 'hour') : false,\n                minute = (this.minute_slider) ? this.control.value(this, this.minute_slider, 'minute') : false,\n                second = (this.second_slider) ? this.control.value(this, this.second_slider, 'second') : false,\n                millisec = (this.millisec_slider) ? this.control.value(this, this.millisec_slider, 'millisec') : false,\n                microsec = (this.microsec_slider) ? this.control.value(this, this.microsec_slider, 'microsec') : false,\n                timezone = (this.timezone_select) ? this.timezone_select.val() : false,\n                o = this._defaults,\n                pickerTimeFormat = o.pickerTimeFormat || o.timeFormat,\n                pickerTimeSuffix = o.pickerTimeSuffix || o.timeSuffix;\n\n            if (typeof(hour) === 'object') {\n                hour = false;\n            }\n            if (typeof(minute) === 'object') {\n                minute = false;\n            }\n            if (typeof(second) === 'object') {\n                second = false;\n            }\n            if (typeof(millisec) === 'object') {\n                millisec = false;\n            }\n            if (typeof(microsec) === 'object') {\n                microsec = false;\n            }\n            if (typeof(timezone) === 'object') {\n                timezone = false;\n            }\n\n            if (hour !== false) {\n                hour = parseInt(hour, 10);\n            }\n            if (minute !== false) {\n                minute = parseInt(minute, 10);\n            }\n            if (second !== false) {\n                second = parseInt(second, 10);\n            }\n            if (millisec !== false) {\n                millisec = parseInt(millisec, 10);\n            }\n            if (microsec !== false) {\n                microsec = parseInt(microsec, 10);\n            }\n            if (timezone !== false) {\n                timezone = timezone.toString();\n            }\n\n            var ampm = o[hour < 12 ? 'amNames' : 'pmNames'][0];\n\n            // If the update was done in the input field, the input field should not be updated.\n            // If the update was done using the sliders, update the input field.\n            var hasChanged = (\n                hour !== parseInt(this.hour,10) || // sliders should all be numeric\n                minute !== parseInt(this.minute,10) ||\n                second !== parseInt(this.second,10) ||\n                millisec !== parseInt(this.millisec,10) ||\n                microsec !== parseInt(this.microsec,10) ||\n                (this.ampm.length > 0 && (hour < 12) !== ($.inArray(this.ampm.toUpperCase(), this.amNames) !== -1)) ||\n                (this.timezone !== null && timezone !== this.timezone.toString()) // could be numeric or \"EST\" format, so use toString()\n            );\n\n            if (hasChanged) {\n\n                if (hour !== false) {\n                    this.hour = hour;\n                }\n                if (minute !== false) {\n                    this.minute = minute;\n                }\n                if (second !== false) {\n                    this.second = second;\n                }\n                if (millisec !== false) {\n                    this.millisec = millisec;\n                }\n                if (microsec !== false) {\n                    this.microsec = microsec;\n                }\n                if (timezone !== false) {\n                    this.timezone = timezone;\n                }\n\n                if (!this.inst) {\n                    this.inst = $.datepicker._getInst(this.$input[0]);\n                }\n\n                this._limitMinMaxDateTime(this.inst, true);\n            }\n            if (this.support.ampm) {\n                this.ampm = ampm;\n            }\n\n            // Updates the time within the timepicker\n            this.formattedTime = $.datepicker.formatTime(o.timeFormat, this, o);\n            if (this.$timeObj) {\n                if (pickerTimeFormat === o.timeFormat) {\n                    this.$timeObj.val(this.formattedTime + pickerTimeSuffix);\n                }\n                else {\n                    this.$timeObj.val($.datepicker.formatTime(pickerTimeFormat, this, o) + pickerTimeSuffix);\n                }\n                if (this.$timeObj[0].setSelectionRange) {\n                    var sPos = this.$timeObj[0].selectionStart;\n                    var ePos = this.$timeObj[0].selectionEnd;\n                    this.$timeObj[0].setSelectionRange(sPos, ePos);\n                }\n            }\n\n            this.timeDefined = true;\n            if (hasChanged) {\n                this._updateDateTime();\n                //this.$input.focus(); // may automatically open the picker on setDate\n            }\n        },\n\n        /*\n        * call custom onSelect.\n        * bind to sliders slidestop, and grid click.\n        */\n        _onSelectHandler: function () {\n            var onSelect = this._defaults.onSelect || this.inst.settings.onSelect;\n            var inputEl = this.$input ? this.$input[0] : null;\n            if (onSelect && inputEl) {\n                onSelect.apply(inputEl, [this.formattedDateTime, this]);\n            }\n        },\n\n        /*\n        * update our input with the new date time..\n        */\n        _updateDateTime: function (dp_inst) {\n            dp_inst = this.inst || dp_inst;\n            var dtTmp = (dp_inst.currentYear > 0?\n                new Date(dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay) :\n                new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),\n                dt = $.datepicker._daylightSavingAdjust(dtTmp),\n                //dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),\n                //dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay)),\n                dateFmt = $.datepicker._get(dp_inst, 'dateFormat'),\n                formatCfg = $.datepicker._getFormatConfig(dp_inst),\n                timeAvailable = dt !== null && this.timeDefined;\n            this.formattedDate = $.datepicker.formatDate(dateFmt, (dt === null ? new Date() : dt), formatCfg);\n            var formattedDateTime = this.formattedDate;\n\n            // if a slider was changed but datepicker doesn't have a value yet, set it\n            if (dp_inst.lastVal === \"\") {\n                dp_inst.currentYear = dp_inst.selectedYear;\n                dp_inst.currentMonth = dp_inst.selectedMonth;\n                dp_inst.currentDay = dp_inst.selectedDay;\n            }\n\n            /*\n            * remove following lines to force every changes in date picker to change the input value\n            * Bug descriptions: when an input field has a default value, and click on the field to pop up the date picker.\n            * If the user manually empty the value in the input field, the date picker will never change selected value.\n            */\n            //if (dp_inst.lastVal !== undefined && (dp_inst.lastVal.length > 0 && this.$input.val().length === 0)) {\n            //\treturn;\n            //}\n\n            if (this._defaults.timeOnly === true && this._defaults.timeOnlyShowDate === false) {\n                formattedDateTime = this.formattedTime;\n            } else if ((this._defaults.timeOnly !== true && (this._defaults.alwaysSetTime || timeAvailable)) || (this._defaults.timeOnly === true && this._defaults.timeOnlyShowDate === true)) {\n                formattedDateTime += this._defaults.separator + this.formattedTime + this._defaults.timeSuffix;\n            }\n\n            this.formattedDateTime = formattedDateTime;\n\n            if (!this._defaults.showTimepicker) {\n                this.$input.val(this.formattedDate);\n            } else if (this.$altInput && this._defaults.timeOnly === false && this._defaults.altFieldTimeOnly === true) {\n                this.$altInput.val(this.formattedTime);\n                this.$input.val(this.formattedDate);\n            } else if (this.$altInput) {\n                this.$input.val(formattedDateTime);\n                var altFormattedDateTime = '',\n                    altSeparator = this._defaults.altSeparator !== null ? this._defaults.altSeparator : this._defaults.separator,\n                    altTimeSuffix = this._defaults.altTimeSuffix !== null ? this._defaults.altTimeSuffix : this._defaults.timeSuffix;\n\n                if (!this._defaults.timeOnly) {\n                    if (this._defaults.altFormat) {\n                        altFormattedDateTime = $.datepicker.formatDate(this._defaults.altFormat, (dt === null ? new Date() : dt), formatCfg);\n                    }\n                    else {\n                        altFormattedDateTime = this.formattedDate;\n                    }\n\n                    if (altFormattedDateTime) {\n                        altFormattedDateTime += altSeparator;\n                    }\n                }\n\n                if (this._defaults.altTimeFormat !== null) {\n                    altFormattedDateTime += $.datepicker.formatTime(this._defaults.altTimeFormat, this, this._defaults) + altTimeSuffix;\n                }\n                else {\n                    altFormattedDateTime += this.formattedTime + altTimeSuffix;\n                }\n                this.$altInput.val(altFormattedDateTime);\n            } else {\n                this.$input.val(formattedDateTime);\n            }\n\n            this.$input.trigger(\"change\");\n        },\n\n        _onFocus: function () {\n            if (!this.$input.val() && this._defaults.defaultValue) {\n                this.$input.val(this._defaults.defaultValue);\n                var inst = $.datepicker._getInst(this.$input.get(0)),\n                    tp_inst = $.datepicker._get(inst, 'timepicker');\n                if (tp_inst) {\n                    if (tp_inst._defaults.timeOnly && (inst.input.val() !== inst.lastVal)) {\n                        try {\n                            $.datepicker._updateDatepicker(inst);\n                        } catch (err) {\n                            $.timepicker.log(err);\n                        }\n                    }\n                }\n            }\n        },\n\n        /*\n        * Small abstraction to control types\n        * We can add more, just be sure to follow the pattern: create, options, value\n        */\n        _controls: {\n            // slider methods\n            slider: {\n                create: function (tp_inst, obj, unit, val, min, max, step) {\n                    var rtl = tp_inst._defaults.isRTL; // if rtl go -60->0 instead of 0->60\n                    return obj.prop('slide', null).slider({\n                        orientation: \"horizontal\",\n                        value: rtl ? val * -1 : val,\n                        min: rtl ? max * -1 : min,\n                        max: rtl ? min * -1 : max,\n                        step: step,\n                        slide: function (event, ui) {\n                            tp_inst.control.value(tp_inst, $(this), unit, rtl ? ui.value * -1 : ui.value);\n                            tp_inst._onTimeChange();\n                        },\n                        stop: function (event, ui) {\n                            tp_inst._onSelectHandler();\n                        }\n                    });\n                },\n                options: function (tp_inst, obj, unit, opts, val) {\n                    if (tp_inst._defaults.isRTL) {\n                        if (typeof(opts) === 'string') {\n                            if (opts === 'min' || opts === 'max') {\n                                if (val !== undefined) {\n                                    return obj.slider(opts, val * -1);\n                                }\n                                return Math.abs(obj.slider(opts));\n                            }\n                            return obj.slider(opts);\n                        }\n                        var min = opts.min,\n                            max = opts.max;\n                        opts.min = opts.max = null;\n                        if (min !== undefined) {\n                            opts.max = min * -1;\n                        }\n                        if (max !== undefined) {\n                            opts.min = max * -1;\n                        }\n                        return obj.slider(opts);\n                    }\n                    if (typeof(opts) === 'string' && val !== undefined) {\n                        return obj.slider(opts, val);\n                    }\n                    return obj.slider(opts);\n                },\n                value: function (tp_inst, obj, unit, val) {\n                    if (tp_inst._defaults.isRTL) {\n                        if (val !== undefined) {\n                            return obj.slider('value', val * -1);\n                        }\n                        return Math.abs(obj.slider('value'));\n                    }\n                    if (val !== undefined) {\n                        return obj.slider('value', val);\n                    }\n                    return obj.slider('value');\n                }\n            },\n            // select methods\n            select: {\n                create: function (tp_inst, obj, unit, val, min, max, step) {\n                    var sel = '<select class=\"ui-timepicker-select ui-state-default ui-corner-all\" data-unit=\"' + unit + '\" data-min=\"' + min + '\" data-max=\"' + max + '\" data-step=\"' + step + '\">',\n                        format = tp_inst._defaults.pickerTimeFormat || tp_inst._defaults.timeFormat;\n\n                    for (var i = min; i <= max; i += step) {\n                        sel += '<option value=\"' + i + '\"' + (i === val ? ' selected' : '') + '>';\n                        if (unit === 'hour') {\n                            sel += $.datepicker.formatTime($.trim(format.replace(/[^ht ]/ig, '')), {hour: i}, tp_inst._defaults);\n                        }\n                        else if (unit === 'millisec' || unit === 'microsec' || i >= 10) { sel += i; }\n                        else {sel += '0' + i.toString(); }\n                        sel += '</option>';\n                    }\n                    sel += '</select>';\n\n                    obj.children('select').remove();\n\n                    $(sel).appendTo(obj).change(function (e) {\n                        tp_inst._onTimeChange();\n                        tp_inst._onSelectHandler();\n                        tp_inst._afterInject();\n                    });\n\n                    return obj;\n                },\n                options: function (tp_inst, obj, unit, opts, val) {\n                    var o = {},\n                        $t = obj.children('select');\n                    if (typeof(opts) === 'string') {\n                        if (val === undefined) {\n                            return $t.data(opts);\n                        }\n                        o[opts] = val;\n                    }\n                    else { o = opts; }\n                    return tp_inst.control.create(tp_inst, obj, $t.data('unit'), $t.val(), o.min>=0 ? o.min : $t.data('min'), o.max || $t.data('max'), o.step || $t.data('step'));\n                },\n                value: function (tp_inst, obj, unit, val) {\n                    var $t = obj.children('select');\n                    if (val !== undefined) {\n                        return $t.val(val);\n                    }\n                    return $t.val();\n                }\n            }\n        } // end _controls\n\n    });\n\n    $.fn.extend({\n        /*\n        * shorthand just to use timepicker.\n        */\n        timepicker: function (o) {\n            o = o || {};\n            var tmp_args = Array.prototype.slice.call(arguments);\n\n            if (typeof o === 'object') {\n                tmp_args[0] = $.extend(o, {\n                    timeOnly: true\n                });\n            }\n\n            return $(this).each(function () {\n                $.fn.datetimepicker.apply($(this), tmp_args);\n            });\n        },\n\n        /*\n        * extend timepicker to datepicker\n        */\n        datetimepicker: function (o) {\n            o = o || {};\n            var tmp_args = arguments;\n\n            if (typeof(o) === 'string') {\n                if (o === 'getDate'  || (o === 'option' && tmp_args.length === 2 && typeof (tmp_args[1]) === 'string')) {\n                    return $.fn.datepicker.apply($(this[0]), tmp_args);\n                } else {\n                    return this.each(function () {\n                        var $t = $(this);\n                        $t.datepicker.apply($t, tmp_args);\n                    });\n                }\n            } else {\n                return this.each(function () {\n                    var $t = $(this);\n                    $t.datepicker($.timepicker._newInst($t, o)._defaults);\n                });\n            }\n        }\n    });\n\n    /*\n    * Public Utility to parse date and time\n    */\n    $.datepicker.parseDateTime = function (dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {\n        var parseRes = parseDateTimeInternal(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings);\n        if (parseRes.timeObj) {\n            var t = parseRes.timeObj;\n            parseRes.date.setHours(t.hour, t.minute, t.second, t.millisec);\n            parseRes.date.setMicroseconds(t.microsec);\n        }\n\n        return parseRes.date;\n    };\n\n    /*\n    * Public utility to parse time\n    */\n    $.datepicker.parseTime = function (timeFormat, timeString, options) {\n        var o = extendRemove(extendRemove({}, $.timepicker._defaults), options || {}),\n            iso8601 = (timeFormat.replace(/\\'.*?\\'/g, '').indexOf('Z') !== -1);\n\n        // Strict parse requires the timeString to match the timeFormat exactly\n        var strictParse = function (f, s, o) {\n\n            // pattern for standard and localized AM/PM markers\n            var getPatternAmpm = function (amNames, pmNames) {\n                var markers = [];\n                if (amNames) {\n                    $.merge(markers, amNames);\n                }\n                if (pmNames) {\n                    $.merge(markers, pmNames);\n                }\n                markers = $.map(markers, function (val) {\n                    return val.replace(/[.*+?|()\\[\\]{}\\\\]/g, '\\\\$&');\n                });\n                return '(' + markers.join('|') + ')?';\n            };\n\n            // figure out position of time elements.. cause js cant do named captures\n            var getFormatPositions = function (timeFormat) {\n                var finds = timeFormat.toLowerCase().match(/(h{1,2}|m{1,2}|s{1,2}|l{1}|c{1}|t{1,2}|z|'.*?')/g),\n                    orders = {\n                        h: -1,\n                        m: -1,\n                        s: -1,\n                        l: -1,\n                        c: -1,\n                        t: -1,\n                        z: -1\n                    };\n\n                if (finds) {\n                    for (var i = 0; i < finds.length; i++) {\n                        if (orders[finds[i].toString().charAt(0)] === -1) {\n                            orders[finds[i].toString().charAt(0)] = i + 1;\n                        }\n                    }\n                }\n                return orders;\n            };\n\n            var regstr = '^' + f.toString()\n                    .replace(/([hH]{1,2}|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function (match) {\n                        var ml = match.length;\n                        switch (match.charAt(0).toLowerCase()) {\n                            case 'h':\n                                return ml === 1 ? '(\\\\d?\\\\d)' : '(\\\\d{' + ml + '})';\n                            case 'm':\n                                return ml === 1 ? '(\\\\d?\\\\d)' : '(\\\\d{' + ml + '})';\n                            case 's':\n                                return ml === 1 ? '(\\\\d?\\\\d)' : '(\\\\d{' + ml + '})';\n                            case 'l':\n                                return '(\\\\d?\\\\d?\\\\d)';\n                            case 'c':\n                                return '(\\\\d?\\\\d?\\\\d)';\n                            case 'z':\n                                return '(z|[-+]\\\\d\\\\d:?\\\\d\\\\d|\\\\S+)?';\n                            case 't':\n                                return getPatternAmpm(o.amNames, o.pmNames);\n                            default:    // literal escaped in quotes\n                                return '(' + match.replace(/\\'/g, \"\").replace(/(\\.|\\$|\\^|\\\\|\\/|\\(|\\)|\\[|\\]|\\?|\\+|\\*)/g, function (m) { return \"\\\\\" + m; }) + ')?';\n                        }\n                    })\n                    .replace(/\\s/g, '\\\\s?') +\n                o.timeSuffix + '$',\n                order = getFormatPositions(f),\n                ampm = '',\n                treg;\n\n            treg = s.match(new RegExp(regstr, 'i'));\n\n            var resTime = {\n                hour: 0,\n                minute: 0,\n                second: 0,\n                millisec: 0,\n                microsec: 0\n            };\n\n            if (treg) {\n                if (order.t !== -1) {\n                    if (treg[order.t] === undefined || treg[order.t].length === 0) {\n                        ampm = '';\n                        resTime.ampm = '';\n                    } else {\n                        ampm = $.inArray(treg[order.t].toUpperCase(), $.map(o.amNames, function (x,i) { return x.toUpperCase(); })) !== -1 ? 'AM' : 'PM';\n                        resTime.ampm = o[ampm === 'AM' ? 'amNames' : 'pmNames'][0];\n                    }\n                }\n\n                if (order.h !== -1) {\n                    if (ampm === 'AM' && treg[order.h] === '12') {\n                        resTime.hour = 0; // 12am = 0 hour\n                    } else {\n                        if (ampm === 'PM' && treg[order.h] !== '12') {\n                            resTime.hour = parseInt(treg[order.h], 10) + 12; // 12pm = 12 hour, any other pm = hour + 12\n                        } else {\n                            resTime.hour = Number(treg[order.h]);\n                        }\n                    }\n                }\n\n                if (order.m !== -1) {\n                    resTime.minute = Number(treg[order.m]);\n                }\n                if (order.s !== -1) {\n                    resTime.second = Number(treg[order.s]);\n                }\n                if (order.l !== -1) {\n                    resTime.millisec = Number(treg[order.l]);\n                }\n                if (order.c !== -1) {\n                    resTime.microsec = Number(treg[order.c]);\n                }\n                if (order.z !== -1 && treg[order.z] !== undefined) {\n                    resTime.timezone = $.timepicker.timezoneOffsetNumber(treg[order.z]);\n                }\n\n\n                return resTime;\n            }\n            return false;\n        };// end strictParse\n\n        // First try JS Date, if that fails, use strictParse\n        var looseParse = function (f, s, o) {\n            try {\n                var d = new Date('2012-01-01 ' + s);\n                if (isNaN(d.getTime())) {\n                    d = new Date('2012-01-01T' + s);\n                    if (isNaN(d.getTime())) {\n                        d = new Date('01/01/2012 ' + s);\n                        if (isNaN(d.getTime())) {\n                            throw \"Unable to parse time with native Date: \" + s;\n                        }\n                    }\n                }\n\n                return {\n                    hour: d.getHours(),\n                    minute: d.getMinutes(),\n                    second: d.getSeconds(),\n                    millisec: d.getMilliseconds(),\n                    microsec: d.getMicroseconds(),\n                    timezone: d.getTimezoneOffset() * -1\n                };\n            }\n            catch (err) {\n                try {\n                    return strictParse(f, s, o);\n                }\n                catch (err2) {\n                    $.timepicker.log(\"Unable to parse \\ntimeString: \" + s + \"\\ntimeFormat: \" + f);\n                }\n            }\n            return false;\n        }; // end looseParse\n\n        if (typeof o.parse === \"function\") {\n            return o.parse(timeFormat, timeString, o);\n        }\n        if (o.parse === 'loose') {\n            return looseParse(timeFormat, timeString, o);\n        }\n        return strictParse(timeFormat, timeString, o);\n    };\n\n    /**\n     * Public utility to format the time\n     * @param {string} format format of the time\n     * @param {Object} time Object not a Date for timezones\n     * @param {Object} [options] essentially the regional[].. amNames, pmNames, ampm\n     * @returns {string} the formatted time\n     */\n    $.datepicker.formatTime = function (format, time, options) {\n        options = options || {};\n        options = $.extend({}, $.timepicker._defaults, options);\n        time = $.extend({\n            hour: 0,\n            minute: 0,\n            second: 0,\n            millisec: 0,\n            microsec: 0,\n            timezone: null\n        }, time);\n\n        var tmptime = format,\n            ampmName = options.amNames[0],\n            hour = parseInt(time.hour, 10);\n\n        if (hour > 11) {\n            ampmName = options.pmNames[0];\n        }\n\n        tmptime = tmptime.replace(/(?:HH?|hh?|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function (match) {\n            switch (match) {\n                case 'HH':\n                    return ('0' + hour).slice(-2);\n                case 'H':\n                    return hour;\n                case 'hh':\n                    return ('0' + convert24to12(hour)).slice(-2);\n                case 'h':\n                    return convert24to12(hour);\n                case 'mm':\n                    return ('0' + time.minute).slice(-2);\n                case 'm':\n                    return time.minute;\n                case 'ss':\n                    return ('0' + time.second).slice(-2);\n                case 's':\n                    return time.second;\n                case 'l':\n                    return ('00' + time.millisec).slice(-3);\n                case 'c':\n                    return ('00' + time.microsec).slice(-3);\n                case 'z':\n                    return $.timepicker.timezoneOffsetString(time.timezone === null ? options.timezone : time.timezone, false);\n                case 'Z':\n                    return $.timepicker.timezoneOffsetString(time.timezone === null ? options.timezone : time.timezone, true);\n                case 'T':\n                    return ampmName.charAt(0).toUpperCase();\n                case 'TT':\n                    return ampmName.toUpperCase();\n                case 't':\n                    return ampmName.charAt(0).toLowerCase();\n                case 'tt':\n                    return ampmName.toLowerCase();\n                default:\n                    return match.replace(/'/g, \"\");\n            }\n        });\n\n        return tmptime;\n    };\n\n    /*\n    * the bad hack :/ override datepicker so it doesn't close on select\n    // inspired: http://stackoverflow.com/questions/1252512/jquery-datepicker-prevent-closing-picker-when-clicking-a-date/1762378#1762378\n    */\n    $.datepicker._base_selectDate = $.datepicker._selectDate;\n    $.datepicker._selectDate = function (id, dateStr) {\n        var inst = this._getInst($(id)[0]),\n            tp_inst = this._get(inst, 'timepicker'),\n            was_inline;\n\n        if (tp_inst && inst.settings.showTimepicker) {\n            tp_inst._limitMinMaxDateTime(inst, true);\n            was_inline = inst.inline;\n            inst.inline = inst.stay_open = true;\n            //This way the onSelect handler called from calendarpicker get the full dateTime\n            this._base_selectDate(id, dateStr);\n            inst.inline = was_inline;\n            inst.stay_open = false;\n            this._notifyChange(inst);\n            this._updateDatepicker(inst);\n        } else {\n            this._base_selectDate(id, dateStr);\n        }\n    };\n\n    /*\n    * second bad hack :/ override datepicker so it triggers an event when changing the input field\n    * and does not redraw the datepicker on every selectDate event\n    */\n    $.datepicker._base_updateDatepicker = $.datepicker._updateDatepicker;\n    $.datepicker._updateDatepicker = function (inst) {\n\n        // don't popup the datepicker if there is another instance already opened\n        var input = inst.input[0];\n        if ($.datepicker._curInst && $.datepicker._curInst !== inst && $.datepicker._datepickerShowing && $.datepicker._lastInput !== input) {\n            return;\n        }\n\n        if (typeof(inst.stay_open) !== 'boolean' || inst.stay_open === false) {\n\n            this._base_updateDatepicker(inst);\n\n            // Reload the time control when changing something in the input text field.\n            var tp_inst = this._get(inst, 'timepicker');\n            if (tp_inst) {\n                tp_inst._addTimePicker(inst);\n            }\n        }\n    };\n\n    /*\n    * third bad hack :/ override datepicker so it allows spaces and colon in the input field\n    */\n    $.datepicker._base_doKeyPress = $.datepicker._doKeyPress;\n    $.datepicker._doKeyPress = function (event) {\n        var inst = $.datepicker._getInst(event.target),\n            tp_inst = $.datepicker._get(inst, 'timepicker');\n\n        if (tp_inst) {\n            if ($.datepicker._get(inst, 'constrainInput')) {\n                var ampm = tp_inst.support.ampm,\n                    tz = tp_inst._defaults.showTimezone !== null ? tp_inst._defaults.showTimezone : tp_inst.support.timezone,\n                    dateChars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat')),\n                    datetimeChars = tp_inst._defaults.timeFormat.toString()\n                            .replace(/[hms]/g, '')\n                            .replace(/TT/g, ampm ? 'APM' : '')\n                            .replace(/Tt/g, ampm ? 'AaPpMm' : '')\n                            .replace(/tT/g, ampm ? 'AaPpMm' : '')\n                            .replace(/T/g, ampm ? 'AP' : '')\n                            .replace(/tt/g, ampm ? 'apm' : '')\n                            .replace(/t/g, ampm ? 'ap' : '') +\n                        \" \" + tp_inst._defaults.separator +\n                        tp_inst._defaults.timeSuffix +\n                        (tz ? tp_inst._defaults.timezoneList.join('') : '') +\n                        (tp_inst._defaults.amNames.join('')) + (tp_inst._defaults.pmNames.join('')) +\n                        dateChars,\n                    chr = String.fromCharCode(event.charCode === undefined ? event.keyCode : event.charCode);\n                return event.ctrlKey || (chr < ' ' || !dateChars || datetimeChars.indexOf(chr) > -1);\n            }\n        }\n\n        return $.datepicker._base_doKeyPress(event);\n    };\n\n    /*\n    * Fourth bad hack :/ override _updateAlternate function used in inline mode to init altField\n    * Update any alternate field to synchronise with the main field.\n    */\n    $.datepicker._base_updateAlternate = $.datepicker._updateAlternate;\n    $.datepicker._updateAlternate = function (inst) {\n        var tp_inst = this._get(inst, 'timepicker');\n        if (tp_inst) {\n            var altField = tp_inst._defaults.altField;\n            if (altField) { // update alternate field too\n                var altFormat = tp_inst._defaults.altFormat || tp_inst._defaults.dateFormat,\n                    date = this._getDate(inst),\n                    formatCfg = $.datepicker._getFormatConfig(inst),\n                    altFormattedDateTime = '',\n                    altSeparator = tp_inst._defaults.altSeparator ? tp_inst._defaults.altSeparator : tp_inst._defaults.separator,\n                    altTimeSuffix = tp_inst._defaults.altTimeSuffix ? tp_inst._defaults.altTimeSuffix : tp_inst._defaults.timeSuffix,\n                    altTimeFormat = tp_inst._defaults.altTimeFormat !== null ? tp_inst._defaults.altTimeFormat : tp_inst._defaults.timeFormat;\n\n                altFormattedDateTime += $.datepicker.formatTime(altTimeFormat, tp_inst, tp_inst._defaults) + altTimeSuffix;\n                if (!tp_inst._defaults.timeOnly && !tp_inst._defaults.altFieldTimeOnly && date !== null) {\n                    if (tp_inst._defaults.altFormat) {\n                        altFormattedDateTime = $.datepicker.formatDate(tp_inst._defaults.altFormat, date, formatCfg) + altSeparator + altFormattedDateTime;\n                    }\n                    else {\n                        altFormattedDateTime = tp_inst.formattedDate + altSeparator + altFormattedDateTime;\n                    }\n                }\n                $(altField).val( inst.input.val() ? altFormattedDateTime : \"\");\n            }\n        }\n        else {\n            $.datepicker._base_updateAlternate(inst);\n        }\n    };\n\n    /*\n    * Override key up event to sync manual input changes.\n    */\n    $.datepicker._base_doKeyUp = $.datepicker._doKeyUp;\n    $.datepicker._doKeyUp = function (event) {\n        var inst = $.datepicker._getInst(event.target),\n            tp_inst = $.datepicker._get(inst, 'timepicker');\n\n        if (tp_inst) {\n            if (tp_inst._defaults.timeOnly && (inst.input.val() !== inst.lastVal)) {\n                try {\n                    $.datepicker._updateDatepicker(inst);\n                } catch (err) {\n                    $.timepicker.log(err);\n                }\n            }\n        }\n\n        return $.datepicker._base_doKeyUp(event);\n    };\n\n    /*\n    * override \"Today\" button to also grab the time and set it to input field.\n    */\n    $.datepicker._base_gotoToday = $.datepicker._gotoToday;\n    $.datepicker._gotoToday = function (id) {\n        var inst = this._getInst($(id)[0]);\n        this._base_gotoToday(id);\n        var tp_inst = this._get(inst, 'timepicker');\n        if (!tp_inst) {\n            return;\n        }\n\n        var tzoffset = $.timepicker.timezoneOffsetNumber(tp_inst.timezone);\n        var now = new Date();\n        now.setMinutes(now.getMinutes() + now.getTimezoneOffset() + parseInt(tzoffset, 10));\n        this._setTime(inst, now);\n        this._setDate(inst, now);\n        tp_inst._onSelectHandler();\n    };\n\n    /*\n    * Disable & enable the Time in the datetimepicker\n    */\n    $.datepicker._disableTimepickerDatepicker = function (target) {\n        var inst = this._getInst(target);\n        if (!inst) {\n            return;\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n        $(target).datepicker('getDate'); // Init selected[Year|Month|Day]\n        if (tp_inst) {\n            inst.settings.showTimepicker = false;\n            tp_inst._defaults.showTimepicker = false;\n            tp_inst._updateDateTime(inst);\n        }\n    };\n\n    $.datepicker._enableTimepickerDatepicker = function (target) {\n        var inst = this._getInst(target);\n        if (!inst) {\n            return;\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n        $(target).datepicker('getDate'); // Init selected[Year|Month|Day]\n        if (tp_inst) {\n            inst.settings.showTimepicker = true;\n            tp_inst._defaults.showTimepicker = true;\n            tp_inst._addTimePicker(inst); // Could be disabled on page load\n            tp_inst._updateDateTime(inst);\n        }\n    };\n\n    /*\n    * Create our own set time function\n    */\n    $.datepicker._setTime = function (inst, date) {\n        var tp_inst = this._get(inst, 'timepicker');\n        if (tp_inst) {\n            var defaults = tp_inst._defaults;\n\n            // calling _setTime with no date sets time to defaults\n            tp_inst.hour = date ? date.getHours() : defaults.hour;\n            tp_inst.minute = date ? date.getMinutes() : defaults.minute;\n            tp_inst.second = date ? date.getSeconds() : defaults.second;\n            tp_inst.millisec = date ? date.getMilliseconds() : defaults.millisec;\n            tp_inst.microsec = date ? date.getMicroseconds() : defaults.microsec;\n\n            //check if within min/max times..\n            tp_inst._limitMinMaxDateTime(inst, true);\n\n            tp_inst._onTimeChange();\n            tp_inst._updateDateTime(inst);\n        }\n    };\n\n    /*\n    * Create new public method to set only time, callable as $().datepicker('setTime', date)\n    */\n    $.datepicker._setTimeDatepicker = function (target, date, withDate) {\n        var inst = this._getInst(target);\n        if (!inst) {\n            return;\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n\n        if (tp_inst) {\n            this._setDateFromField(inst);\n            var tp_date;\n            if (date) {\n                if (typeof date === \"string\") {\n                    tp_inst._parseTime(date, withDate);\n                    tp_date = new Date();\n                    tp_date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);\n                    tp_date.setMicroseconds(tp_inst.microsec);\n                } else {\n                    tp_date = new Date(date.getTime());\n                    tp_date.setMicroseconds(date.getMicroseconds());\n                }\n                if (tp_date.toString() === 'Invalid Date') {\n                    tp_date = undefined;\n                }\n                this._setTime(inst, tp_date);\n            }\n        }\n\n    };\n\n    /*\n    * override setDate() to allow setting time too within Date object\n    */\n    $.datepicker._base_setDateDatepicker = $.datepicker._setDateDatepicker;\n    $.datepicker._setDateDatepicker = function (target, _date) {\n        var inst = this._getInst(target);\n        var date = _date;\n        if (!inst) {\n            return;\n        }\n\n        if (typeof(_date) === 'string') {\n            date = new Date(_date);\n            if (!date.getTime()) {\n                this._base_setDateDatepicker.apply(this, arguments);\n                date = $(target).datepicker('getDate');\n            }\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n        var tp_date;\n        if (date instanceof Date) {\n            tp_date = new Date(date.getTime());\n            tp_date.setMicroseconds(date.getMicroseconds());\n        } else {\n            tp_date = date;\n        }\n\n        // This is important if you are using the timezone option, javascript's Date\n        // object will only return the timezone offset for the current locale, so we\n        // adjust it accordingly.  If not using timezone option this won't matter..\n        // If a timezone is different in tp, keep the timezone as is\n        if (tp_inst && tp_date) {\n            // look out for DST if tz wasn't specified\n            if (!tp_inst.support.timezone && tp_inst._defaults.timezone === null) {\n                tp_inst.timezone = tp_date.getTimezoneOffset() * -1;\n            }\n            date = $.timepicker.timezoneAdjust(date, $.timepicker.timezoneOffsetString(-date.getTimezoneOffset()), tp_inst.timezone);\n            tp_date = $.timepicker.timezoneAdjust(tp_date, $.timepicker.timezoneOffsetString(-tp_date.getTimezoneOffset()), tp_inst.timezone);\n        }\n\n        this._updateDatepicker(inst);\n        this._base_setDateDatepicker.apply(this, arguments);\n        this._setTimeDatepicker(target, tp_date, true);\n    };\n\n    /*\n    * override getDate() to allow getting time too within Date object\n    */\n    $.datepicker._base_getDateDatepicker = $.datepicker._getDateDatepicker;\n    $.datepicker._getDateDatepicker = function (target, noDefault) {\n        var inst = this._getInst(target);\n        if (!inst) {\n            return;\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n\n        if (tp_inst) {\n            // if it hasn't yet been defined, grab from field\n            if (inst.lastVal === undefined) {\n                this._setDateFromField(inst, noDefault);\n            }\n\n            var date = this._getDate(inst);\n\n            var currDT = null;\n\n            if (tp_inst.$altInput && tp_inst._defaults.altFieldTimeOnly) {\n                currDT = tp_inst.$input.val() + ' ' + tp_inst.$altInput.val();\n            }\n            else if (tp_inst.$input.get(0).tagName !== 'INPUT' && tp_inst.$altInput) {\n                /**\n                 * in case the datetimepicker has been applied to a non-input tag for inline UI,\n                 * and the user has not configured the plugin to display only time in altInput,\n                 * pick current date time from the altInput (and hope for the best, for now, until \"ER1\" is applied)\n                 *\n                 * @todo ER1. Since altInput can have a totally difference format, convert it to standard format by reading input format from \"altFormat\" and \"altTimeFormat\" option values\n                 */\n                currDT = tp_inst.$altInput.val();\n            }\n            else {\n                currDT = tp_inst.$input.val();\n            }\n\n            if (date && tp_inst._parseTime(currDT, !inst.settings.timeOnly)) {\n                date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);\n                date.setMicroseconds(tp_inst.microsec);\n\n                // This is important if you are using the timezone option, javascript's Date\n                // object will only return the timezone offset for the current locale, so we\n                // adjust it accordingly.  If not using timezone option this won't matter..\n                if (tp_inst.timezone != null) {\n                    // look out for DST if tz wasn't specified\n                    if (!tp_inst.support.timezone && tp_inst._defaults.timezone === null) {\n                        tp_inst.timezone = date.getTimezoneOffset() * -1;\n                    }\n                    date = $.timepicker.timezoneAdjust(date, tp_inst.timezone, $.timepicker.timezoneOffsetString(-date.getTimezoneOffset()));\n                }\n            }\n            return date;\n        }\n        return this._base_getDateDatepicker(target, noDefault);\n    };\n\n    /*\n    * override parseDate() because UI 1.8.14 throws an error about \"Extra characters\"\n    * An option in datapicker to ignore extra format characters would be nicer.\n    */\n    $.datepicker._base_parseDate = $.datepicker.parseDate;\n    $.datepicker.parseDate = function (format, value, settings) {\n        var date;\n        try {\n            date = this._base_parseDate(format, value, settings);\n        } catch (err) {\n            // Hack!  The error message ends with a colon, a space, and\n            // the \"extra\" characters.  We rely on that instead of\n            // attempting to perfectly reproduce the parsing algorithm.\n            if (err.indexOf(\":\") >= 0) {\n                date = this._base_parseDate(format, value.substring(0, value.length - (err.length - err.indexOf(':') - 2)), settings);\n                $.timepicker.log(\"Error parsing the date string: \" + err + \"\\ndate string = \" + value + \"\\ndate format = \" + format);\n            } else {\n                throw err;\n            }\n        }\n        return date;\n    };\n\n    /*\n    * override formatDate to set date with time to the input\n    */\n    $.datepicker._base_formatDate = $.datepicker._formatDate;\n    $.datepicker._formatDate = function (inst, day, month, year) {\n        var tp_inst = this._get(inst, 'timepicker');\n        if (tp_inst) {\n            tp_inst._updateDateTime(inst);\n            return tp_inst.$input.val();\n        }\n        return this._base_formatDate(inst);\n    };\n\n    /*\n    * override options setter to add time to maxDate(Time) and minDate(Time). MaxDate\n    */\n    $.datepicker._base_optionDatepicker = $.datepicker._optionDatepicker;\n    $.datepicker._optionDatepicker = function (target, name, value) {\n        var inst = this._getInst(target),\n            name_clone;\n        if (!inst) {\n            return null;\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n        if (tp_inst) {\n            var min = null,\n                max = null,\n                onselect = null,\n                overrides = tp_inst._defaults.evnts,\n                fns = {},\n                prop,\n                ret,\n                oldVal,\n                $target;\n            if (typeof name === 'string') { // if min/max was set with the string\n                if (name === 'minDate' || name === 'minDateTime') {\n                    min = value;\n                } else if (name === 'maxDate' || name === 'maxDateTime') {\n                    max = value;\n                } else if (name === 'onSelect') {\n                    onselect = value;\n                } else if (overrides.hasOwnProperty(name)) {\n                    if (typeof (value) === 'undefined') {\n                        return overrides[name];\n                    }\n                    fns[name] = value;\n                    name_clone = {}; //empty results in exiting function after overrides updated\n                }\n            } else if (typeof name === 'object') { //if min/max was set with the JSON\n                if (name.minDate) {\n                    min = name.minDate;\n                } else if (name.minDateTime) {\n                    min = name.minDateTime;\n                } else if (name.maxDate) {\n                    max = name.maxDate;\n                } else if (name.maxDateTime) {\n                    max = name.maxDateTime;\n                }\n                for (prop in overrides) {\n                    if (overrides.hasOwnProperty(prop) && name[prop]) {\n                        fns[prop] = name[prop];\n                    }\n                }\n            }\n            for (prop in fns) {\n                if (fns.hasOwnProperty(prop)) {\n                    overrides[prop] = fns[prop];\n                    if (!name_clone) { name_clone = $.extend({}, name); }\n                    delete name_clone[prop];\n                }\n            }\n            if (name_clone && isEmptyObject(name_clone)) { return; }\n            if (min) { //if min was set\n                if (min === 0) {\n                    min = new Date();\n                } else {\n                    min = new Date(min);\n                }\n                tp_inst._defaults.minDate = min;\n                tp_inst._defaults.minDateTime = min;\n            } else if (max) { //if max was set\n                if (max === 0) {\n                    max = new Date();\n                } else {\n                    max = new Date(max);\n                }\n                tp_inst._defaults.maxDate = max;\n                tp_inst._defaults.maxDateTime = max;\n            } else if (onselect) {\n                tp_inst._defaults.onSelect = onselect;\n            }\n\n            // Datepicker will override our date when we call _base_optionDatepicker when\n            // calling minDate/maxDate, so we will first grab the value, call\n            // _base_optionDatepicker, then set our value back.\n            if(min || max){\n                $target = $(target);\n                oldVal = $target.datetimepicker('getDate');\n                ret = this._base_optionDatepicker.call($.datepicker, target, name_clone || name, value);\n                $target.datetimepicker('setDate', oldVal);\n                return ret;\n            }\n        }\n        if (value === undefined) {\n            return this._base_optionDatepicker.call($.datepicker, target, name);\n        }\n        return this._base_optionDatepicker.call($.datepicker, target, name_clone || name, value);\n    };\n\n    /*\n    * jQuery isEmptyObject does not check hasOwnProperty - if someone has added to the object prototype,\n    * it will return false for all objects\n    */\n    var isEmptyObject = function (obj) {\n        var prop;\n        for (prop in obj) {\n            if (obj.hasOwnProperty(prop)) {\n                return false;\n            }\n        }\n        return true;\n    };\n\n    /*\n    * jQuery extend now ignores nulls!\n    */\n    var extendRemove = function (target, props) {\n        $.extend(target, props);\n        for (var name in props) {\n            if (props[name] === null || props[name] === undefined) {\n                target[name] = props[name];\n            }\n        }\n        return target;\n    };\n\n    /*\n    * Determine by the time format which units are supported\n    * Returns an object of booleans for each unit\n    */\n    var detectSupport = function (timeFormat) {\n        var tf = timeFormat.replace(/'.*?'/g, '').toLowerCase(), // removes literals\n            isIn = function (f, t) { // does the format contain the token?\n                return f.indexOf(t) !== -1 ? true : false;\n            };\n        return {\n            hour: isIn(tf, 'h'),\n            minute: isIn(tf, 'm'),\n            second: isIn(tf, 's'),\n            millisec: isIn(tf, 'l'),\n            microsec: isIn(tf, 'c'),\n            timezone: isIn(tf, 'z'),\n            ampm: isIn(tf, 't') && isIn(timeFormat, 'h'),\n            iso8601: isIn(timeFormat, 'Z')\n        };\n    };\n\n    /*\n    * Converts 24 hour format into 12 hour\n    * Returns 12 hour without leading 0\n    */\n    var convert24to12 = function (hour) {\n        hour %= 12;\n\n        if (hour === 0) {\n            hour = 12;\n        }\n\n        return String(hour);\n    };\n\n    var computeEffectiveSetting = function (settings, property) {\n        return settings && settings[property] ? settings[property] : $.timepicker._defaults[property];\n    };\n\n    /*\n    * Splits datetime string into date and time substrings.\n    * Throws exception when date can't be parsed\n    * Returns {dateString: dateString, timeString: timeString}\n    */\n    var splitDateTime = function (dateTimeString, timeSettings) {\n        // The idea is to get the number separator occurrences in datetime and the time format requested (since time has\n        // fewer unknowns, mostly numbers and am/pm). We will use the time pattern to split.\n        var separator = computeEffectiveSetting(timeSettings, 'separator'),\n            format = computeEffectiveSetting(timeSettings, 'timeFormat'),\n            timeParts = format.split(separator), // how many occurrences of separator may be in our format?\n            timePartsLen = timeParts.length,\n            allParts = dateTimeString.split(separator),\n            allPartsLen = allParts.length;\n\n        if (allPartsLen > 1) {\n            return {\n                dateString: allParts.splice(0, allPartsLen - timePartsLen).join(separator),\n                timeString: allParts.splice(0, timePartsLen).join(separator)\n            };\n        }\n\n        return {\n            dateString: dateTimeString,\n            timeString: ''\n        };\n    };\n\n    /*\n    * Internal function to parse datetime interval\n    * Returns: {date: Date, timeObj: Object}, where\n    *   date - parsed date without time (type Date)\n    *   timeObj = {hour: , minute: , second: , millisec: , microsec: } - parsed time. Optional\n    */\n    var parseDateTimeInternal = function (dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {\n        var date,\n            parts,\n            parsedTime;\n\n        parts = splitDateTime(dateTimeString, timeSettings);\n        date = $.datepicker._base_parseDate(dateFormat, parts.dateString, dateSettings);\n\n        if (parts.timeString === '') {\n            return {\n                date: date\n            };\n        }\n\n        parsedTime = $.datepicker.parseTime(timeFormat, parts.timeString, timeSettings);\n\n        if (!parsedTime) {\n            throw 'Wrong time format';\n        }\n\n        return {\n            date: date,\n            timeObj: parsedTime\n        };\n    };\n\n    /*\n    * Internal function to set timezone_select to the local timezone\n    */\n    var selectLocalTimezone = function (tp_inst, date) {\n        if (tp_inst && tp_inst.timezone_select) {\n            var now = date || new Date();\n            tp_inst.timezone_select.val(-now.getTimezoneOffset());\n        }\n    };\n\n    /*\n    * Create a Singleton Instance\n    */\n    $.timepicker = new Timepicker();\n\n    /**\n     * Get the timezone offset as string from a date object (eg '+0530' for UTC+5.5)\n     * @param {number} tzMinutes if not a number, less than -720 (-1200), or greater than 840 (+1400) this value is returned\n     * @param {boolean} iso8601 if true formats in accordance to iso8601 \"+12:45\"\n     * @return {string}\n     */\n    $.timepicker.timezoneOffsetString = function (tzMinutes, iso8601) {\n        if (isNaN(tzMinutes) || tzMinutes > 840 || tzMinutes < -720) {\n            return tzMinutes;\n        }\n\n        var off = tzMinutes,\n            minutes = off % 60,\n            hours = (off - minutes) / 60,\n            iso = iso8601 ? ':' : '',\n            tz = (off >= 0 ? '+' : '-') + ('0' + Math.abs(hours)).slice(-2) + iso + ('0' + Math.abs(minutes)).slice(-2);\n\n        if (tz === '+00:00') {\n            return 'Z';\n        }\n        return tz;\n    };\n\n    /**\n     * Get the number in minutes that represents a timezone string\n     * @param  {string} tzString formatted like \"+0500\", \"-1245\", \"Z\"\n     * @return {number} the offset minutes or the original string if it doesn't match expectations\n     */\n    $.timepicker.timezoneOffsetNumber = function (tzString) {\n        var normalized = tzString.toString().replace(':', ''); // excuse any iso8601, end up with \"+1245\"\n\n        if (normalized.toUpperCase() === 'Z') { // if iso8601 with Z, its 0 minute offset\n            return 0;\n        }\n\n        if (!/^(\\-|\\+)\\d{4}$/.test(normalized)) { // possibly a user defined tz, so just give it back\n            return parseInt(tzString, 10);\n        }\n\n        return ((normalized.substr(0, 1) === '-' ? -1 : 1) * // plus or minus\n            ((parseInt(normalized.substr(1, 2), 10) * 60) + // hours (converted to minutes)\n                parseInt(normalized.substr(3, 2), 10))); // minutes\n    };\n\n    /**\n     * No way to set timezone in js Date, so we must adjust the minutes to compensate. (think setDate, getDate)\n     * @param  {Date} date\n     * @param  {string} fromTimezone formatted like \"+0500\", \"-1245\"\n     * @param  {string} toTimezone formatted like \"+0500\", \"-1245\"\n     * @return {Date}\n     */\n    $.timepicker.timezoneAdjust = function (date, fromTimezone, toTimezone) {\n        var fromTz = $.timepicker.timezoneOffsetNumber(fromTimezone);\n        var toTz = $.timepicker.timezoneOffsetNumber(toTimezone);\n        if (!isNaN(toTz)) {\n            date.setMinutes(date.getMinutes() + (-fromTz) - (-toTz));\n        }\n        return date;\n    };\n\n    /**\n     * Calls `timepicker()` on the `startTime` and `endTime` elements, and configures them to\n     * enforce date range limits.\n     * n.b. The input value must be correctly formatted (reformatting is not supported)\n     * @param  {Element} startTime\n     * @param  {Element} endTime\n     * @param  {Object} options Options for the timepicker() call\n     * @return {jQuery}\n     */\n    $.timepicker.timeRange = function (startTime, endTime, options) {\n        return $.timepicker.handleRange('timepicker', startTime, endTime, options);\n    };\n\n    /**\n     * Calls `datetimepicker` on the `startTime` and `endTime` elements, and configures them to\n     * enforce date range limits.\n     * @param  {Element} startTime\n     * @param  {Element} endTime\n     * @param  {Object} options Options for the `timepicker()` call. Also supports `reformat`,\n     *   a boolean value that can be used to reformat the input values to the `dateFormat`.\n     * @param  {string} method Can be used to specify the type of picker to be added\n     * @return {jQuery}\n     */\n    $.timepicker.datetimeRange = function (startTime, endTime, options) {\n        $.timepicker.handleRange('datetimepicker', startTime, endTime, options);\n    };\n\n    /**\n     * Calls `datepicker` on the `startTime` and `endTime` elements, and configures them to\n     * enforce date range limits.\n     * @param  {Element} startTime\n     * @param  {Element} endTime\n     * @param  {Object} options Options for the `timepicker()` call. Also supports `reformat`,\n     *   a boolean value that can be used to reformat the input values to the `dateFormat`.\n     * @return {jQuery}\n     */\n    $.timepicker.dateRange = function (startTime, endTime, options) {\n        $.timepicker.handleRange('datepicker', startTime, endTime, options);\n    };\n\n    /**\n     * Calls `method` on the `startTime` and `endTime` elements, and configures them to\n     * enforce date range limits.\n     * @param  {string} method Can be used to specify the type of picker to be added\n     * @param  {Element} startTime\n     * @param  {Element} endTime\n     * @param  {Object} options Options for the `timepicker()` call. Also supports `reformat`,\n     *   a boolean value that can be used to reformat the input values to the `dateFormat`.\n     * @return {jQuery}\n     */\n    $.timepicker.handleRange = function (method, startTime, endTime, options) {\n        options = $.extend({}, {\n            minInterval: 0, // min allowed interval in milliseconds\n            maxInterval: 0, // max allowed interval in milliseconds\n            start: {},      // options for start picker\n            end: {}         // options for end picker\n        }, options);\n\n        // for the mean time this fixes an issue with calling getDate with timepicker()\n        var timeOnly = false;\n        if(method === 'timepicker'){\n            timeOnly = true;\n            method = 'datetimepicker';\n        }\n\n        function checkDates(changed, other) {\n            var startdt = startTime[method]('getDate'),\n                enddt = endTime[method]('getDate'),\n                changeddt = changed[method]('getDate');\n\n            if (startdt !== null) {\n                var minDate = new Date(startdt.getTime()),\n                    maxDate = new Date(startdt.getTime());\n\n                minDate.setMilliseconds(minDate.getMilliseconds() + options.minInterval);\n                maxDate.setMilliseconds(maxDate.getMilliseconds() + options.maxInterval);\n\n                if (options.minInterval > 0 && minDate > enddt) { // minInterval check\n                    endTime[method]('setDate', minDate);\n                }\n                else if (options.maxInterval > 0 && maxDate < enddt) { // max interval check\n                    endTime[method]('setDate', maxDate);\n                }\n                else if (startdt > enddt) {\n                    other[method]('setDate', changeddt);\n                }\n            }\n        }\n\n        function selected(changed, other, option) {\n            if (!changed.val()) {\n                return;\n            }\n            var date = changed[method].call(changed, 'getDate');\n            if (date !== null && options.minInterval > 0) {\n                if (option === 'minDate') {\n                    date.setMilliseconds(date.getMilliseconds() + options.minInterval);\n                }\n                if (option === 'maxDate') {\n                    date.setMilliseconds(date.getMilliseconds() - options.minInterval);\n                }\n            }\n\n            if (date.getTime) {\n                other[method].call(other, 'option', option, date);\n            }\n        }\n\n        $.fn[method].call(startTime, $.extend({\n            timeOnly: timeOnly,\n            onClose: function (dateText, inst) {\n                checkDates($(this), endTime);\n            },\n            onSelect: function (selectedDateTime) {\n                selected($(this), endTime, 'minDate');\n            }\n        }, options, options.start));\n        $.fn[method].call(endTime, $.extend({\n            timeOnly: timeOnly,\n            onClose: function (dateText, inst) {\n                checkDates($(this), startTime);\n            },\n            onSelect: function (selectedDateTime) {\n                selected($(this), startTime, 'maxDate');\n            }\n        }, options, options.end));\n\n        checkDates(startTime, endTime);\n\n        selected(startTime, endTime, 'minDate');\n        selected(endTime, startTime, 'maxDate');\n\n        return $([startTime.get(0), endTime.get(0)]);\n    };\n\n    /**\n     * Log error or data to the console during error or debugging\n     * @param  {Object} err pass any type object to log to the console during error or debugging\n     * @return {void}\n     */\n    $.timepicker.log = function () {\n        // Older IE (9, maybe 10) throw error on accessing `window.console.log.apply`, so check first.\n        if (window.console && window.console.log && window.console.log.apply) {\n            window.console.log.apply(window.console, Array.prototype.slice.call(arguments));\n        }\n    };\n\n    /*\n     * Add util object to allow access to private methods for testability.\n     */\n    $.timepicker._util = {\n        _extendRemove: extendRemove,\n        _isEmptyObject: isEmptyObject,\n        _convert24to12: convert24to12,\n        _detectSupport: detectSupport,\n        _selectLocalTimezone: selectLocalTimezone,\n        _computeEffectiveSetting: computeEffectiveSetting,\n        _splitDateTime: splitDateTime,\n        _parseDateTimeInternal: parseDateTimeInternal\n    };\n\n    /*\n    * Microsecond support\n    */\n    if (!Date.prototype.getMicroseconds) {\n        Date.prototype.microseconds = 0;\n        Date.prototype.getMicroseconds = function () { return this.microseconds; };\n        Date.prototype.setMicroseconds = function (m) {\n            this.setMilliseconds(this.getMilliseconds() + Math.floor(m / 1000));\n            this.microseconds = m % 1000;\n            return this;\n        };\n    }\n\n    /*\n    * Keep up with the version\n    */\n    $.timepicker.version = \"1.6.3\";\n\n}));\n","jquery/jquery.tabs.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    \"jquery\",\n    \"jquery/bootstrap/tab\",\n    \"jquery/bootstrap/collapse\",\n], function () {\n\n});\n","jquery/z-index.js":"/*!\n * zIndex plugin from jQuery UI Core - v1.10.4\n * http://jqueryui.com\n *\n * Copyright 2014 jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n *\n * http://api.jqueryui.com/category/ui-core/\n */\ndefine([\n    'jquery'\n], function ($, undefined) {\n\n// plugins\n    $.fn.extend({\n        zIndex: function (zIndex) {\n            if (zIndex !== undefined) {\n                return this.css(\"zIndex\", zIndex);\n            }\n\n            if (this.length) {\n                var elem = $(this[0]), position, value;\n                while (elem.length && elem[0] !== document) {\n                    // Ignore z-index if position is set to a value where z-index is ignored by the browser\n                    // This makes behavior of this function consistent across browsers\n                    // WebKit always returns auto if the element is positioned\n                    position = elem.css(\"position\");\n                    if (position === \"absolute\" || position === \"relative\" || position === \"fixed\") {\n                        // IE returns 0 when zIndex is not specified\n                        // other browsers return a string\n                        // we ignore the case of nested elements with an explicit value of 0\n                        // <div style=\"z-index: -10;\"><div style=\"z-index: 0;\"></div></div>\n                        value = parseInt(elem.css(\"zIndex\"), 10);\n                        if (!isNaN(value) && value !== 0) {\n                            return value;\n                        }\n                    }\n                    elem = elem.parent();\n                }\n            }\n\n            return 0;\n        }\n    });\n});\n","jquery/jquery.validate.js":"/*!\n * jQuery Validation Plugin v1.19.5\n *\n * https://jqueryvalidation.org/\n *\n * Copyright (c) 2022 J\u00f6rn Zaefferer\n * Released under the MIT license\n */\n(function( factory ) {\n    if ( typeof define === \"function\" && define.amd ) {\n        define( [\"jquery\", \"jquery/jquery.metadata\"], factory );\n    } else if (typeof module === \"object\" && module.exports) {\n        module.exports = factory( require( \"jquery\" ) );\n    } else {\n        factory( jQuery );\n    }\n}(function( $ ) {\n\n    $.extend( $.fn, {\n\n        // https://jqueryvalidation.org/validate/\n        validate: function( options ) {\n\n            // If nothing is selected, return nothing; can't chain anyway\n            if ( !this.length ) {\n                if ( options && options.debug && window.console ) {\n                    console.warn( \"Nothing selected, can't validate, returning nothing.\" );\n                }\n                return;\n            }\n\n            // Check if a validator for this form was already created\n            var validator = $.data( this[ 0 ], \"validator\" );\n            if ( validator ) {\n                return validator;\n            }\n\n            // Add novalidate tag if HTML5.\n            this.attr( \"novalidate\", \"novalidate\" );\n\n            validator = new $.validator( options, this[ 0 ] );\n            $.data( this[ 0 ], \"validator\", validator );\n\n            if ( validator.settings.onsubmit ) {\n\n                this.on( \"click.validate\", \":submit\", function( event ) {\n\n                    // Track the used submit button to properly handle scripted\n                    // submits later.\n                    validator.submitButton = event.currentTarget;\n\n                    // Allow suppressing validation by adding a cancel class to the submit button\n                    if ( $( this ).hasClass( \"cancel\" ) ) {\n                        validator.cancelSubmit = true;\n                    }\n\n                    // Allow suppressing validation by adding the html5 formnovalidate attribute to the submit button\n                    if ( $( this ).attr( \"formnovalidate\" ) !== undefined ) {\n                        validator.cancelSubmit = true;\n                    }\n                } );\n\n                // Validate the form on submit\n                this.on( \"submit.validate\", function( event ) {\n                    if ( validator.settings.debug ) {\n\n                        // Prevent form submit to be able to see console output\n                        event.preventDefault();\n                    }\n\n                    function handle() {\n                        var hidden, result;\n\n                        // Insert a hidden input as a replacement for the missing submit button\n                        // The hidden input is inserted in two cases:\n                        //   - A user defined a `submitHandler`\n                        //   - There was a pending request due to `remote` method and `stopRequest()`\n                        //     was called to submit the form in case it's valid\n                        if ( validator.submitButton && ( validator.settings.submitHandler || validator.formSubmitted ) ) {\n                            hidden = $( \"<input type='hidden'/>\" )\n                                .attr( \"name\", validator.submitButton.name )\n                                .val( $( validator.submitButton ).val() )\n                                .appendTo( validator.currentForm );\n                        }\n\n                        if ( validator.settings.submitHandler && !validator.settings.debug ) {\n                            result = validator.settings.submitHandler.call( validator, validator.currentForm, event );\n                            if ( hidden ) {\n\n                                // And clean up afterwards; thanks to no-block-scope, hidden can be referenced\n                                hidden.remove();\n                            }\n                            if ( result !== undefined ) {\n                                return result;\n                            }\n                            return false;\n                        }\n                        return true;\n                    }\n\n                    // Prevent submit for invalid forms or custom submit handlers\n                    if ( validator.cancelSubmit ) {\n                        validator.cancelSubmit = false;\n                        return handle();\n                    }\n                    if ( validator.form() ) {\n                        if ( validator.pendingRequest ) {\n                            validator.formSubmitted = true;\n                            return false;\n                        }\n                        return handle();\n                    } else {\n                        validator.focusInvalid();\n                        return false;\n                    }\n                } );\n            }\n\n            return validator;\n        },\n\n        // https://jqueryvalidation.org/valid/\n        valid: function() {\n            var valid, validator, errorList;\n\n            if ( $( this[ 0 ] ).is( \"form\" ) ) {\n                valid = this.validate().form();\n            } else {\n                errorList = [];\n                valid = true;\n                validator = $( this[ 0 ].form ).validate();\n                this.each( function() {\n                    valid = validator.element( this ) && valid;\n                    if ( !valid ) {\n                        errorList = errorList.concat( validator.errorList );\n                    }\n                } );\n                validator.errorList = errorList;\n            }\n            return valid;\n        },\n\n        // https://jqueryvalidation.org/rules/\n        rules: function( command, argument ) {\n            var element = this[ 0 ],\n                isContentEditable = typeof this.attr( \"contenteditable\" ) !== \"undefined\" && this.attr( \"contenteditable\" ) !== \"false\",\n                settings, staticRules, existingRules, data, param, filtered;\n\n            // If nothing is selected, return empty object; can't chain anyway\n            if ( element == null ) {\n                return;\n            }\n\n            if ( !element.form && isContentEditable ) {\n                element.form = this.closest( \"form\" )[ 0 ];\n                element.name = this.attr( \"name\" );\n            }\n\n            if ( element.form == null ) {\n                return;\n            }\n\n            if ( command ) {\n                settings = $.data( element.form, \"validator\" ).settings;\n                staticRules = settings.rules;\n                existingRules = $.validator.staticRules( element );\n                switch ( command ) {\n                    case \"add\":\n                        $.extend( existingRules, $.validator.normalizeRule( argument ) );\n\n                        // Remove messages from rules, but allow them to be set separately\n                        delete existingRules.messages;\n                        staticRules[ element.name ] = existingRules;\n                        if ( argument.messages ) {\n                            settings.messages[ element.name ] = $.extend( settings.messages[ element.name ], argument.messages );\n                        }\n                        break;\n                    case \"remove\":\n                        if ( !argument ) {\n                            delete staticRules[ element.name ];\n                            return existingRules;\n                        }\n                        filtered = {};\n                        $.each( argument.split( /\\s/ ), function( index, method ) {\n                            filtered[ method ] = existingRules[ method ];\n                            delete existingRules[ method ];\n                        } );\n                        return filtered;\n                }\n            }\n\n            data = $.validator.normalizeRules(\n                $.extend(\n                    {},\n                    $.validator.metadataRules(element),\n                    $.validator.classRules( element ),\n                    $.validator.attributeRules( element ),\n                    $.validator.dataRules( element ),\n                    $.validator.staticRules( element )\n                ), element );\n\n            // Make sure required is at front\n            if ( data.required ) {\n                param = data.required;\n                delete data.required;\n                data = $.extend( { required: param }, data );\n            }\n\n            // Make sure remote is at back\n            if ( data.remote ) {\n                param = data.remote;\n                delete data.remote;\n                data = $.extend( data, { remote: param } );\n            }\n\n            return data;\n        }\n    } );\n\n// JQuery trim is deprecated, provide a trim method based on String.prototype.trim\n    var trim = function( str ) {\n\n        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim#Polyfill\n        return str.replace( /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, \"\" );\n    };\n\n// Custom selectors\n    $.extend( $.expr.pseudos || $.expr[ \":\" ], {\t\t// '|| $.expr[ \":\" ]' here enables backwards compatibility to jQuery 1.7. Can be removed when dropping jQ 1.7.x support\n\n        // https://jqueryvalidation.org/blank-selector/\n        blank: function( a ) {\n            return !trim( \"\" + $( a ).val() );\n        },\n\n        // https://jqueryvalidation.org/filled-selector/\n        filled: function( a ) {\n            var val = $( a ).val();\n            return val !== null && !!trim( \"\" + val );\n        },\n\n        // https://jqueryvalidation.org/unchecked-selector/\n        unchecked: function( a ) {\n            return !$( a ).prop( \"checked\" );\n        }\n    } );\n\n// Constructor for validator\n    $.validator = function( options, form ) {\n        this.settings = $.extend( true, {}, $.validator.defaults, options );\n        this.currentForm = form;\n        this.init();\n    };\n\n// https://jqueryvalidation.org/jQuery.validator.format/\n    $.validator.format = function( source, params ) {\n        if ( arguments.length === 1 ) {\n            return function() {\n                var args = $.makeArray( arguments );\n                args.unshift( source );\n                return $.validator.format.apply( this, args );\n            };\n        }\n        if ( params === undefined ) {\n            return source;\n        }\n        if ( arguments.length > 2 && params.constructor !== Array  ) {\n            params = $.makeArray( arguments ).slice( 1 );\n        }\n        if ( params.constructor !== Array ) {\n            params = [ params ];\n        }\n        $.each( params, function( i, n ) {\n            source = source.replace( new RegExp( \"\\\\{\" + i + \"\\\\}\", \"g\" ), function() {\n                return n;\n            } );\n        } );\n        return source;\n    };\n\n    $.extend( $.validator, {\n\n        defaults: {\n            messages: {},\n            groups: {},\n            rules: {},\n            errorClass: \"error\",\n            pendingClass: \"pending\",\n            validClass: \"valid\",\n            errorElement: \"label\",\n            focusCleanup: false,\n            focusInvalid: true,\n            errorContainer: $( [] ),\n            errorLabelContainer: $( [] ),\n            onsubmit: true,\n            ignore: \":hidden\",\n            ignoreTitle: false,\n            onfocusin: function( element ) {\n                this.lastActive = element;\n\n                // Hide error label and remove error class on focus if enabled\n                if ( this.settings.focusCleanup ) {\n                    if ( this.settings.unhighlight ) {\n                        this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );\n                    }\n                    this.hideThese( this.errorsFor( element ) );\n                }\n            },\n            onfocusout: function( element ) {\n                if ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) {\n                    this.element( element );\n                }\n            },\n            onkeyup: function( element, event ) {\n\n                // Avoid revalidate the field when pressing one of the following keys\n                // Shift       => 16\n                // Ctrl        => 17\n                // Alt         => 18\n                // Caps lock   => 20\n                // End         => 35\n                // Home        => 36\n                // Left arrow  => 37\n                // Up arrow    => 38\n                // Right arrow => 39\n                // Down arrow  => 40\n                // Insert      => 45\n                // Num lock    => 144\n                // AltGr key   => 225\n                var excludedKeys = [\n                    16, 17, 18, 20, 35, 36, 37,\n                    38, 39, 40, 45, 144, 225\n                ];\n\n                if ( event.which === 9 && this.elementValue( element ) === \"\" || $.inArray( event.keyCode, excludedKeys ) !== -1 ) {\n                    return;\n                } else if ( element.name in this.submitted || element.name in this.invalid ) {\n                    this.element( element );\n                }\n            },\n            onclick: function( element ) {\n\n                // Click on selects, radiobuttons and checkboxes\n                if ( element.name in this.submitted ) {\n                    this.element( element );\n\n                    // Or option elements, check parent select in that case\n                } else if ( element.parentNode.name in this.submitted ) {\n                    this.element( element.parentNode );\n                }\n            },\n            highlight: function( element, errorClass, validClass ) {\n                if ( element.type === \"radio\" ) {\n                    this.findByName( element.name ).addClass( errorClass ).removeClass( validClass );\n                } else {\n                    $( element ).addClass( errorClass ).removeClass( validClass );\n                }\n            },\n            unhighlight: function( element, errorClass, validClass ) {\n                if ( element.type === \"radio\" ) {\n                    this.findByName( element.name ).removeClass( errorClass ).addClass( validClass );\n                } else {\n                    $( element ).removeClass( errorClass ).addClass( validClass );\n                }\n            }\n        },\n\n        // https://jqueryvalidation.org/jQuery.validator.setDefaults/\n        setDefaults: function( settings ) {\n            $.extend( $.validator.defaults, settings );\n        },\n\n        messages: {\n            required: \"This field is required.\",\n            remote: \"Please fix this field.\",\n            email: \"Please enter a valid email address.\",\n            url: \"Please enter a valid URL.\",\n            date: \"Please enter a valid date.\",\n            dateISO: \"Please enter a valid date (ISO).\",\n            number: \"Please enter a valid number.\",\n            digits: \"Please enter only digits.\",\n            equalTo: \"Please enter the same value again.\",\n            maxlength: $.validator.format( \"Please enter no more than {0} characters.\" ),\n            minlength: $.validator.format( \"Please enter at least {0} characters.\" ),\n            rangelength: $.validator.format( \"Please enter a value between {0} and {1} characters long.\" ),\n            range: $.validator.format( \"Please enter a value between {0} and {1}.\" ),\n            max: $.validator.format( \"Please enter a value less than or equal to {0}.\" ),\n            min: $.validator.format( \"Please enter a value greater than or equal to {0}.\" ),\n            step: $.validator.format( \"Please enter a multiple of {0}.\" )\n        },\n\n        autoCreateRanges: false,\n\n        prototype: {\n\n            init: function() {\n                this.labelContainer = $( this.settings.errorLabelContainer );\n                this.errorContext = this.labelContainer.length && this.labelContainer || $( this.currentForm );\n                this.containers = $( this.settings.errorContainer ).add( this.settings.errorLabelContainer );\n                this.submitted = {};\n                this.valueCache = {};\n                this.pendingRequest = 0;\n                this.pending = {};\n                this.invalid = {};\n                this.reset();\n\n                var currentForm = this.currentForm,\n                    groups = ( this.groups = {} ),\n                    rules;\n                $.each( this.settings.groups, function( key, value ) {\n                    if ( typeof value === \"string\" ) {\n                        value = value.split( /\\s/ );\n                    }\n                    $.each( value, function( index, name ) {\n                        groups[ name ] = key;\n                    } );\n                } );\n                rules = this.settings.rules;\n                $.each( rules, function( key, value ) {\n                    rules[ key ] = $.validator.normalizeRule( value );\n                } );\n\n                function delegate( event ) {\n                    var isContentEditable = typeof $( this ).attr( \"contenteditable\" ) !== \"undefined\" && $( this ).attr( \"contenteditable\" ) !== \"false\";\n\n                    // Set form expando on contenteditable\n                    if ( !this.form && isContentEditable ) {\n                        this.form = $( this ).closest( \"form\" )[ 0 ];\n                        this.name = $( this ).attr( \"name\" );\n                    }\n\n                    // Ignore the element if it belongs to another form. This will happen mainly\n                    // when setting the `form` attribute of an input to the id of another form.\n                    if ( currentForm !== this.form ) {\n                        return;\n                    }\n\n                    var validator = $.data( this.form, \"validator\" ),\n                        eventType = \"on\" + event.type.replace( /^validate/, \"\" ),\n                        settings = validator.settings;\n                    if ( settings[ eventType ] && !$( this ).is( settings.ignore ) ) {\n                        settings[ eventType ].call( validator, this, event );\n                    }\n                }\n\n                $( this.currentForm )\n                    .on( \"focusin.validate focusout.validate keyup.validate\",\n                        \":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], \" +\n                        \"[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], \" +\n                        \"[type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], \" +\n                        \"[type='radio'], [type='checkbox'], [contenteditable], [type='button']\", delegate )\n\n                    // Support: Chrome, oldIE\n                    // \"select\" is provided as event.target when clicking a option\n                    .on( \"click.validate\", \"select, option, [type='radio'], [type='checkbox']\", delegate );\n\n                if ( this.settings.invalidHandler ) {\n                    $( this.currentForm ).on( \"invalid-form.validate\", this.settings.invalidHandler );\n                }\n            },\n\n            // https://jqueryvalidation.org/Validator.form/\n            form: function() {\n                this.checkForm();\n                $.extend( this.submitted, this.errorMap );\n                this.invalid = $.extend( {}, this.errorMap );\n                if ( !this.valid() ) {\n                    $( this.currentForm ).triggerHandler( \"invalid-form\", [ this ] );\n                }\n                this.showErrors();\n                return this.valid();\n            },\n\n            checkForm: function() {\n                this.prepareForm();\n                for ( var i = 0, elements = ( this.currentElements = this.elements() ); elements[ i ]; i++ ) {\n                    this.check( elements[ i ] );\n                }\n                return this.valid();\n            },\n\n            // https://jqueryvalidation.org/Validator.element/\n            element: function( element ) {\n                var cleanElement = this.clean( element ),\n                    checkElement = this.validationTargetFor( cleanElement ),\n                    v = this,\n                    result = true,\n                    rs, group;\n\n                if ( checkElement === undefined ) {\n                    delete this.invalid[ cleanElement.name ];\n                } else {\n                    this.prepareElement( checkElement );\n                    this.currentElements = $( checkElement );\n\n                    // If this element is grouped, then validate all group elements already\n                    // containing a value\n                    group = this.groups[ checkElement.name ];\n                    if ( group ) {\n                        $.each( this.groups, function( name, testgroup ) {\n                            if ( testgroup === group && name !== checkElement.name ) {\n                                cleanElement = v.validationTargetFor( v.clean( v.findByName( name ) ) );\n                                if ( cleanElement && cleanElement.name in v.invalid ) {\n                                    v.currentElements.push( cleanElement );\n                                    result = v.check( cleanElement ) && result;\n                                }\n                            }\n                        } );\n                    }\n\n                    rs = this.check( checkElement ) !== false;\n                    result = result && rs;\n                    if ( rs ) {\n                        this.invalid[ checkElement.name ] = false;\n                    } else {\n                        this.invalid[ checkElement.name ] = true;\n                    }\n\n                    if ( !this.numberOfInvalids() ) {\n\n                        // Hide error containers on last error\n                        this.toHide = this.toHide.add( this.containers );\n                    }\n                    this.showErrors();\n\n                    // Add aria-invalid status for screen readers\n                    $( element ).attr( \"aria-invalid\", !rs );\n                }\n\n                return result;\n            },\n\n            // https://jqueryvalidation.org/Validator.showErrors/\n            showErrors: function( errors ) {\n                if ( errors ) {\n                    var validator = this;\n\n                    // Add items to error list and map\n                    $.extend( this.errorMap, errors );\n                    this.errorList = $.map( this.errorMap, function( message, name ) {\n                        return {\n                            message: message,\n                            element: validator.findByName( name )[ 0 ]\n                        };\n                    } );\n\n                    // Remove items from success list\n                    this.successList = $.grep( this.successList, function( element ) {\n                        return !( element.name in errors );\n                    } );\n                }\n                if ( this.settings.showErrors ) {\n                    this.settings.showErrors.call( this, this.errorMap, this.errorList );\n                } else {\n                    this.defaultShowErrors();\n                }\n            },\n\n            // https://jqueryvalidation.org/Validator.resetForm/\n            resetForm: function() {\n                if ( $.fn.resetForm ) {\n                    $( this.currentForm ).resetForm();\n                }\n                this.invalid = {};\n                this.submitted = {};\n                this.prepareForm();\n                this.hideErrors();\n                var elements = this.elements()\n                    .removeData( \"previousValue\" )\n                    .removeAttr( \"aria-invalid\" );\n\n                this.resetElements( elements );\n            },\n\n            resetElements: function( elements ) {\n                var i;\n\n                if ( this.settings.unhighlight ) {\n                    for ( i = 0; elements[ i ]; i++ ) {\n                        this.settings.unhighlight.call( this, elements[ i ],\n                            this.settings.errorClass, \"\" );\n                        this.findByName( elements[ i ].name ).removeClass( this.settings.validClass );\n                    }\n                } else {\n                    elements\n                        .removeClass( this.settings.errorClass )\n                        .removeClass( this.settings.validClass );\n                }\n            },\n\n            numberOfInvalids: function() {\n                return this.objectLength( this.invalid );\n            },\n\n            objectLength: function( obj ) {\n                /* jshint unused: false */\n                var count = 0,\n                    i;\n                for ( i in obj ) {\n\n                    // This check allows counting elements with empty error\n                    // message as invalid elements\n                    if ( obj[ i ] !== undefined && obj[ i ] !== null && obj[ i ] !== false ) {\n                        count++;\n                    }\n                }\n                return count;\n            },\n\n            hideErrors: function() {\n                this.hideThese( this.toHide );\n            },\n\n            hideThese: function( errors ) {\n                errors.not( this.containers ).text( \"\" );\n                this.addWrapper( errors ).hide();\n            },\n\n            valid: function() {\n                return this.size() === 0;\n            },\n\n            size: function() {\n                return this.errorList.length;\n            },\n\n            focusInvalid: function() {\n                if ( this.settings.focusInvalid ) {\n                    try {\n                        $( this.findLastActive() || this.errorList.length && this.errorList[ 0 ].element || [] )\n                            .filter( \":visible\" )\n                            .trigger( \"focus\" )\n\n                            // Manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find\n                            .trigger( \"focusin\" );\n                    } catch ( e ) {\n\n                        // Ignore IE throwing errors when focusing hidden elements\n                    }\n                }\n            },\n\n            findLastActive: function() {\n                var lastActive = this.lastActive;\n                return lastActive && $.grep( this.errorList, function( n ) {\n                    return n.element.name === lastActive.name;\n                } ).length === 1 && lastActive;\n            },\n\n            elements: function() {\n                var validator = this,\n                    rulesCache = {};\n\n                // Select all valid inputs inside the form (no submit or reset buttons)\n                return $( this.currentForm )\n                    .find( \"input, select, textarea, [contenteditable]\" )\n                    .not( \":submit, :reset, :image, :disabled\" )\n                    .not( this.settings.ignore )\n                    .filter( function() {\n                        var name = this.name || $( this ).attr( \"name\" ); // For contenteditable\n                        var isContentEditable = typeof $( this ).attr( \"contenteditable\" ) !== \"undefined\" && $( this ).attr( \"contenteditable\" ) !== \"false\";\n\n                        if ( !name && validator.settings.debug && window.console ) {\n                            console.error( \"%o has no name assigned\", this );\n                        }\n\n                        // Set form expando on contenteditable\n                        if ( isContentEditable ) {\n                            this.form = $( this ).closest( \"form\" )[ 0 ];\n                            this.name = name;\n                        }\n\n                        // Ignore elements that belong to other/nested forms\n                        if ( this.form !== validator.currentForm ) {\n                            return false;\n                        }\n\n                        // Select only the first element for each name, and only those with rules specified\n                        if ( name in rulesCache || !validator.objectLength( $( this ).rules() ) ) {\n                            return false;\n                        }\n\n                        rulesCache[ name ] = true;\n                        return true;\n                    } );\n            },\n\n            clean: function( selector ) {\n                return $( selector )[ 0 ];\n            },\n\n            errors: function() {\n                var errorClass = this.settings.errorClass.split( \" \" ).join( \".\" );\n                return $( this.settings.errorElement + \".\" + errorClass, this.errorContext );\n            },\n\n            resetInternals: function() {\n                this.successList = [];\n                this.errorList = [];\n                this.errorMap = {};\n                this.toShow = $( [] );\n                this.toHide = $( [] );\n            },\n\n            reset: function() {\n                this.resetInternals();\n                this.currentElements = $( [] );\n            },\n\n            prepareForm: function() {\n                this.reset();\n                this.toHide = this.errors().add( this.containers );\n            },\n\n            prepareElement: function( element ) {\n                this.reset();\n                this.toHide = this.errorsFor( element );\n            },\n\n            elementValue: function( element ) {\n                var $element = $( element ),\n                    type = element.type,\n                    isContentEditable = typeof $element.attr( \"contenteditable\" ) !== \"undefined\" && $element.attr( \"contenteditable\" ) !== \"false\",\n                    val, idx;\n\n                if ( type === \"radio\" || type === \"checkbox\" ) {\n                    return this.findByName( element.name ).filter( \":checked\" ).val();\n                } else if ( type === \"number\" && typeof element.validity !== \"undefined\" ) {\n                    return element.validity.badInput ? \"NaN\" : $element.val();\n                }\n\n                if ( isContentEditable ) {\n                    val = $element.text();\n                } else {\n                    val = $element.val();\n                }\n\n                if ( type === \"file\" ) {\n\n                    // Modern browser (chrome & safari)\n                    if ( val.substr( 0, 12 ) === \"C:\\\\fakepath\\\\\" ) {\n                        return val.substr( 12 );\n                    }\n\n                    // Legacy browsers\n                    // Unix-based path\n                    idx = val.lastIndexOf( \"/\" );\n                    if ( idx >= 0 ) {\n                        return val.substr( idx + 1 );\n                    }\n\n                    // Windows-based path\n                    idx = val.lastIndexOf( \"\\\\\" );\n                    if ( idx >= 0 ) {\n                        return val.substr( idx + 1 );\n                    }\n\n                    // Just the file name\n                    return val;\n                }\n\n                if ( typeof val === \"string\" ) {\n                    return val.replace( /\\r/g, \"\" );\n                }\n                return val;\n            },\n\n            check: function( element ) {\n                element = this.validationTargetFor( this.clean( element ) );\n\n                var rules = $( element ).rules(),\n                    rulesCount = $.map( rules, function( n, i ) {\n                        return i;\n                    } ).length,\n                    dependencyMismatch = false,\n                    val = this.elementValue( element ),\n                    result, method, rule, normalizer;\n\n                // Prioritize the local normalizer defined for this element over the global one\n                // if the former exists, otherwise user the global one in case it exists.\n                if ( typeof rules.normalizer === \"function\" ) {\n                    normalizer = rules.normalizer;\n                } else if (\ttypeof this.settings.normalizer === \"function\" ) {\n                    normalizer = this.settings.normalizer;\n                }\n\n                // If normalizer is defined, then call it to the changed value instead\n                // of using the real one.\n                // Note that `this` in the normalizer is `element`.\n                if ( normalizer ) {\n                    val = normalizer.call( element, val );\n\n                    // Delete the normalizer from rules to avoid treating it as a pre-defined method.\n                    delete rules.normalizer;\n                }\n\n                for ( method in rules ) {\n                    rule = { method: method, parameters: rules[ method ] };\n                    try {\n                        result = $.validator.methods[ method ].call( this, val, element, rule.parameters );\n\n                        // If a method indicates that the field is optional and therefore valid,\n                        // don't mark it as valid when there are no other rules\n                        if ( result === \"dependency-mismatch\" && rulesCount === 1 ) {\n                            dependencyMismatch = true;\n                            continue;\n                        }\n                        dependencyMismatch = false;\n\n                        if ( result === \"pending\" ) {\n                            this.toHide = this.toHide.not( this.errorsFor( element ) );\n                            return;\n                        }\n\n                        if ( !result ) {\n                            this.formatAndAdd( element, rule );\n                            return false;\n                        }\n                    } catch ( e ) {\n                        if ( this.settings.debug && window.console ) {\n                            console.log( \"Exception occurred when checking element \" + element.id + \", check the '\" + rule.method + \"' method.\", e );\n                        }\n                        if ( e instanceof TypeError ) {\n                            e.message += \".  Exception occurred when checking element \" + element.id + \", check the '\" + rule.method + \"' method.\";\n                        }\n\n                        throw e;\n                    }\n                }\n                if ( dependencyMismatch ) {\n                    return;\n                }\n                if ( this.objectLength( rules ) ) {\n                    this.successList.push( element );\n                }\n                return true;\n            },\n\n            // Return the custom message for the given element and validation method\n            // specified in the element's HTML5 data attribute\n            // return the generic message if present and no method specific message is present\n            customDataMessage: function( element, method ) {\n                return $( element ).data( \"msg\" + method.charAt( 0 ).toUpperCase() +\n                    method.substring( 1 ).toLowerCase() ) || $( element ).data( \"msg\" );\n            },\n\n            // Return the custom message for the given element name and validation method\n            customMessage: function( name, method ) {\n                var m = this.settings.messages[ name ];\n                return m && ( m.constructor === String ? m : m[ method ] );\n            },\n\n            // Return the first defined argument, allowing empty strings\n            findDefined: function() {\n                for ( var i = 0; i < arguments.length; i++ ) {\n                    if ( arguments[ i ] !== undefined ) {\n                        return arguments[ i ];\n                    }\n                }\n                return undefined;\n            },\n\n            // The second parameter 'rule' used to be a string, and extended to an object literal\n            // of the following form:\n            // rule = {\n            //     method: \"method name\",\n            //     parameters: \"the given method parameters\"\n            // }\n            //\n            // The old behavior still supported, kept to maintain backward compatibility with\n            // old code, and will be removed in the next major release.\n            defaultMessage: function( element, rule ) {\n                if ( typeof rule === \"string\" ) {\n                    rule = { method: rule };\n                }\n\n                var message = this.findDefined(\n                        this.customMessage( element.name, rule.method ),\n                        this.customDataMessage( element, rule.method ),\n\n                        // 'title' is never undefined, so handle empty string as undefined\n                        !this.settings.ignoreTitle && element.title || undefined,\n                        $.validator.messages[ rule.method ],\n                        \"<strong>Warning: No message defined for \" + element.name + \"</strong>\"\n                    ),\n                    theregex = /\\$?\\{(\\d+)\\}/g;\n                if ( typeof message === \"function\" ) {\n                    message = message.call( this, rule.parameters, element );\n                } else if ( theregex.test( message ) ) {\n                    message = $.validator.format( message.replace( theregex, \"{$1}\" ), rule.parameters );\n                }\n\n                return message;\n            },\n\n            formatAndAdd: function( element, rule ) {\n                var message = this.defaultMessage( element, rule );\n\n                this.errorList.push( {\n                    message: message,\n                    element: element,\n                    method: rule.method\n                } );\n\n                this.errorMap[ element.name ] = message;\n                this.submitted[ element.name ] = message;\n            },\n\n            addWrapper: function( toToggle ) {\n                if ( this.settings.wrapper ) {\n                    toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );\n                }\n                return toToggle;\n            },\n\n            defaultShowErrors: function() {\n                var i, elements, error;\n                for ( i = 0; this.errorList[ i ]; i++ ) {\n                    error = this.errorList[ i ];\n                    if ( this.settings.highlight ) {\n                        this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );\n                    }\n                    this.showLabel( error.element, error.message );\n                }\n                if ( this.errorList.length ) {\n                    this.toShow = this.toShow.add( this.containers );\n                }\n                if ( this.settings.success ) {\n                    for ( i = 0; this.successList[ i ]; i++ ) {\n                        this.showLabel( this.successList[ i ] );\n                    }\n                }\n                if ( this.settings.unhighlight ) {\n                    for ( i = 0, elements = this.validElements(); elements[ i ]; i++ ) {\n                        this.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass );\n                    }\n                }\n                this.toHide = this.toHide.not( this.toShow );\n                this.hideErrors();\n                this.addWrapper( this.toShow ).show();\n            },\n\n            validElements: function() {\n                return this.currentElements.not( this.invalidElements() );\n            },\n\n            invalidElements: function() {\n                return $( this.errorList ).map( function() {\n                    return this.element;\n                } );\n            },\n\n            showLabel: function( element, message ) {\n                var place, group, errorID, v,\n                    error = this.errorsFor( element ),\n                    elementID = this.idOrName( element ),\n                    describedBy = $( element ).attr( \"aria-describedby\" );\n\n                if ( error.length ) {\n\n                    // Refresh error/success class\n                    error.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );\n\n                    // Replace message on existing label\n                    error.html( message );\n                } else {\n\n                    // Create error element\n                    error = $( \"<\" + this.settings.errorElement + \">\" )\n                        .attr( \"id\", elementID + \"-error\" )\n                        .addClass( this.settings.errorClass )\n                        .html( message || \"\" );\n\n                    // Maintain reference to the element to be placed into the DOM\n                    place = error;\n                    if ( this.settings.wrapper ) {\n\n                        // Make sure the element is visible, even in IE\n                        // actually showing the wrapped element is handled elsewhere\n                        place = error.hide().show().wrap( \"<\" + this.settings.wrapper + \"/>\" ).parent();\n                    }\n                    if ( this.labelContainer.length ) {\n                        this.labelContainer.append( place );\n                    } else if ( this.settings.errorPlacement ) {\n                        this.settings.errorPlacement.call( this, place, $( element ) );\n                    } else {\n                        place.insertAfter( element );\n                    }\n\n                    // Link error back to the element\n                    if ( error.is( \"label\" ) ) {\n\n                        // If the error is a label, then associate using 'for'\n                        error.attr( \"for\", elementID );\n\n                        // If the element is not a child of an associated label, then it's necessary\n                        // to explicitly apply aria-describedby\n                    } else if ( error.parents( \"label[for='\" + this.escapeCssMeta( elementID ) + \"']\" ).length === 0 ) {\n                        errorID = error.attr( \"id\" );\n\n                        // Respect existing non-error aria-describedby\n                        if ( !describedBy ) {\n                            describedBy = errorID;\n                        } else if ( !describedBy.match( new RegExp( \"\\\\b\" + this.escapeCssMeta( errorID ) + \"\\\\b\" ) ) ) {\n\n                            // Add to end of list if not already present\n                            describedBy += \" \" + errorID;\n                        }\n                        $( element ).attr( \"aria-describedby\", describedBy );\n\n                        // If this element is grouped, then assign to all elements in the same group\n                        group = this.groups[ element.name ];\n                        if ( group ) {\n                            v = this;\n                            $.each( v.groups, function( name, testgroup ) {\n                                if ( testgroup === group ) {\n                                    $( \"[name='\" + v.escapeCssMeta( name ) + \"']\", v.currentForm )\n                                        .attr( \"aria-describedby\", error.attr( \"id\" ) );\n                                }\n                            } );\n                        }\n                    }\n                }\n                if ( !message && this.settings.success ) {\n                    error.text( \"\" );\n                    if ( typeof this.settings.success === \"string\" ) {\n                        error.addClass( this.settings.success );\n                    } else {\n                        this.settings.success( error, element );\n                    }\n                }\n                this.toShow = this.toShow.add( error );\n            },\n\n            errorsFor: function( element ) {\n                var name = this.escapeCssMeta( this.idOrName( element ) ),\n                    describer = $( element ).attr( \"aria-describedby\" ),\n                    selector = \"label[for='\" + name + \"'], label[for='\" + name + \"'] *\";\n\n                // 'aria-describedby' should directly reference the error element\n                if ( describer ) {\n                    selector = selector + \", #\" + this.escapeCssMeta( describer )\n                        .replace( /\\s+/g, \", #\" ) + \":visible\";\n                }\n\n                return this\n                    .errors()\n                    .filter( selector );\n            },\n\n            // See https://api.jquery.com/category/selectors/, for CSS\n            // meta-characters that should be escaped in order to be used with JQuery\n            // as a literal part of a name/id or any selector.\n            escapeCssMeta: function( string ) {\n                if ( string === undefined ) {\n                    return \"\";\n                }\n\n                return string.replace( /([\\\\!\"#$%&'()*+,./:;<=>?@\\[\\]^`{|}~])/g, \"\\\\$1\" );\n            },\n\n            idOrName: function( element ) {\n                return this.groups[ element.name ] || ( this.checkable( element ) ? element.name : element.id || element.name );\n            },\n\n            validationTargetFor: function( element ) {\n\n                // If radio/checkbox, validate first element in group instead\n                if ( this.checkable( element ) ) {\n                    element = this.findByName( element.name );\n                }\n\n                // Always apply ignore filter\n                return $( element ).not( this.settings.ignore )[ 0 ];\n            },\n\n            checkable: function( element ) {\n                return ( /radio|checkbox/i ).test( element.type );\n            },\n\n            findByName: function( name ) {\n                return $( this.currentForm ).find( \"[name='\" + this.escapeCssMeta( name ) + \"']\" );\n            },\n\n            getLength: function( value, element ) {\n                switch ( element.nodeName.toLowerCase() ) {\n                    case \"select\":\n                        return $( \"option:selected\", element ).length;\n                    case \"input\":\n                        if ( this.checkable( element ) ) {\n                            return this.findByName( element.name ).filter( \":checked\" ).length;\n                        }\n                }\n                return value.length;\n            },\n\n            depend: function( param, element ) {\n                return this.dependTypes[ typeof param ] ? this.dependTypes[ typeof param ]( param, element ) : true;\n            },\n\n            dependTypes: {\n                \"boolean\": function( param ) {\n                    return param;\n                },\n                \"string\": function( param, element ) {\n                    return !!$( param, element.form ).length;\n                },\n                \"function\": function( param, element ) {\n                    return param( element );\n                }\n            },\n\n            optional: function( element ) {\n                var val = this.elementValue( element );\n                return !$.validator.methods.required.call( this, val, element ) && \"dependency-mismatch\";\n            },\n\n            startRequest: function( element ) {\n                if ( !this.pending[ element.name ] ) {\n                    this.pendingRequest++;\n                    $( element ).addClass( this.settings.pendingClass );\n                    this.pending[ element.name ] = true;\n                }\n            },\n\n            stopRequest: function( element, valid ) {\n                this.pendingRequest--;\n\n                // Sometimes synchronization fails, make sure pendingRequest is never < 0\n                if ( this.pendingRequest < 0 ) {\n                    this.pendingRequest = 0;\n                }\n                delete this.pending[ element.name ];\n                $( element ).removeClass( this.settings.pendingClass );\n                if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() && this.pendingRequest === 0 ) {\n                    $( this.currentForm ).trigger( \"submit\" );\n\n                    // Remove the hidden input that was used as a replacement for the\n                    // missing submit button. The hidden input is added by `handle()`\n                    // to ensure that the value of the used submit button is passed on\n                    // for scripted submits triggered by this method\n                    if ( this.submitButton ) {\n                        $( \"input:hidden[name='\" + this.submitButton.name + \"']\", this.currentForm ).remove();\n                    }\n\n                    this.formSubmitted = false;\n                } else if ( !valid && this.pendingRequest === 0 && this.formSubmitted ) {\n                    $( this.currentForm ).triggerHandler( \"invalid-form\", [ this ] );\n                    this.formSubmitted = false;\n                }\n            },\n\n            previousValue: function( element, method ) {\n                method = typeof method === \"string\" && method || \"remote\";\n\n                return $.data( element, \"previousValue\" ) || $.data( element, \"previousValue\", {\n                    old: null,\n                    valid: true,\n                    message: this.defaultMessage( element, { method: method } )\n                } );\n            },\n\n            // Cleans up all forms and elements, removes validator-specific events\n            destroy: function() {\n                this.resetForm();\n\n                $( this.currentForm )\n                    .off( \".validate\" )\n                    .removeData( \"validator\" )\n                    .find( \".validate-equalTo-blur\" )\n                    .off( \".validate-equalTo\" )\n                    .removeClass( \"validate-equalTo-blur\" )\n                    .find( \".validate-lessThan-blur\" )\n                    .off( \".validate-lessThan\" )\n                    .removeClass( \"validate-lessThan-blur\" )\n                    .find( \".validate-lessThanEqual-blur\" )\n                    .off( \".validate-lessThanEqual\" )\n                    .removeClass( \"validate-lessThanEqual-blur\" )\n                    .find( \".validate-greaterThanEqual-blur\" )\n                    .off( \".validate-greaterThanEqual\" )\n                    .removeClass( \"validate-greaterThanEqual-blur\" )\n                    .find( \".validate-greaterThan-blur\" )\n                    .off( \".validate-greaterThan\" )\n                    .removeClass( \"validate-greaterThan-blur\" );\n            }\n\n        },\n\n        classRuleSettings: {\n            required: { required: true },\n            email: { email: true },\n            url: { url: true },\n            date: { date: true },\n            dateISO: { dateISO: true },\n            number: { number: true },\n            digits: { digits: true },\n            creditcard: { creditcard: true }\n        },\n\n        addClassRules: function( className, rules ) {\n            if ( className.constructor === String ) {\n                this.classRuleSettings[ className ] = rules;\n            } else {\n                $.extend( this.classRuleSettings, className );\n            }\n        },\n\n        classRules: function( element ) {\n            var rules = {},\n                classes = $( element ).attr( \"class\" );\n\n            if ( classes ) {\n                $.each( classes.split( \" \" ), function() {\n                    if ( this in $.validator.classRuleSettings ) {\n                        $.extend( rules, $.validator.classRuleSettings[ this ] );\n                    }\n                } );\n            }\n            return rules;\n        },\n\n        normalizeAttributeRule: function( rules, type, method, value ) {\n\n            // Convert the value to a number for number inputs, and for text for backwards compability\n            // allows type=\"date\" and others to be compared as strings\n            if ( /min|max|step/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) {\n                value = Number( value );\n\n                // Support Opera Mini, which returns NaN for undefined minlength\n                if ( isNaN( value ) ) {\n                    value = undefined;\n                }\n            }\n\n            if ( value || value === 0 ) {\n                rules[ method ] = value;\n            } else if ( type === method && type !== \"range\" ) {\n\n                // Exception: the jquery validate 'range' method\n                // does not test for the html5 'range' type\n                rules[ type === \"date\" ? \"dateISO\" : method ] = true;\n            }\n        },\n\n        attributeRules: function( element ) {\n            var rules = {},\n                $element = $( element ),\n                type = element.getAttribute( \"type\" ),\n                method, value;\n\n            for ( method in $.validator.methods ) {\n\n                // Support for <input required> in both html5 and older browsers\n                if ( method === \"required\" ) {\n                    value = element.getAttribute( method );\n\n                    // Some browsers return an empty string for the required attribute\n                    // and non-HTML5 browsers might have required=\"\" markup\n                    if ( value === \"\" ) {\n                        value = true;\n                    }\n\n                    // Force non-HTML5 browsers to return bool\n                    value = !!value;\n                } else {\n                    value = $element.attr( method );\n                }\n\n                this.normalizeAttributeRule( rules, type, method, value );\n            }\n\n            // 'maxlength' may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs\n            if ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) {\n                delete rules.maxlength;\n            }\n\n            return rules;\n        },\n\n        metadataRules: function (element) {\n            if (!$.metadata) {\n                return {};\n            }\n\n            var meta = $.data(element.form, 'validator').settings.meta;\n            return meta ?\n                $(element).metadata()[meta] :\n                $(element).metadata();\n        },\n\n        dataRules: function( element ) {\n            var rules = {},\n                $element = $( element ),\n                type = element.getAttribute( \"type\" ),\n                method, value;\n\n            for ( method in $.validator.methods ) {\n                value = $element.data( \"rule\" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() );\n\n                // Cast empty attributes like `data-rule-required` to `true`\n                if ( value === \"\" ) {\n                    value = true;\n                }\n\n                this.normalizeAttributeRule( rules, type, method, value );\n            }\n            return rules;\n        },\n\n        staticRules: function( element ) {\n            var rules = {},\n                validator = $.data( element.form, \"validator\" );\n\n            if ( validator.settings.rules ) {\n                rules = $.validator.normalizeRule( validator.settings.rules[ element.name ] ) || {};\n            }\n            return rules;\n        },\n\n        normalizeRules: function( rules, element ) {\n\n            // Handle dependency check\n            $.each( rules, function( prop, val ) {\n\n                // Ignore rule when param is explicitly false, eg. required:false\n                if ( val === false ) {\n                    delete rules[ prop ];\n                    return;\n                }\n                if ( val.param || val.depends ) {\n                    var keepRule = true;\n                    switch ( typeof val.depends ) {\n                        case \"string\":\n                            keepRule = !!$( val.depends, element.form ).length;\n                            break;\n                        case \"function\":\n                            keepRule = val.depends.call( element, element );\n                            break;\n                    }\n                    if ( keepRule ) {\n                        rules[ prop ] = val.param !== undefined ? val.param : true;\n                    } else {\n                        $.data( element.form, \"validator\" ).resetElements( $( element ) );\n                        delete rules[ prop ];\n                    }\n                }\n            } );\n\n            // Evaluate parameters\n            $.each( rules, function( rule, parameter ) {\n                rules[ rule ] = typeof parameter === \"function\" && rule !== \"normalizer\" ? parameter( element ) : parameter;\n            } );\n\n            // Clean number parameters\n            $.each( [ \"minlength\", \"maxlength\" ], function() {\n                if ( rules[ this ] ) {\n                    rules[ this ] = Number( rules[ this ] );\n                }\n            } );\n            $.each( [ \"rangelength\", \"range\" ], function() {\n                var parts;\n                if ( rules[ this ] ) {\n                    if ( Array.isArray( rules[ this ] ) ) {\n                        rules[ this ] = [ Number( rules[ this ][ 0 ] ), Number( rules[ this ][ 1 ] ) ];\n                    } else if ( typeof rules[ this ] === \"string\" ) {\n                        parts = rules[ this ].replace( /[\\[\\]]/g, \"\" ).split( /[\\s,]+/ );\n                        rules[ this ] = [ Number( parts[ 0 ] ), Number( parts[ 1 ] ) ];\n                    }\n                }\n            } );\n\n            if ( $.validator.autoCreateRanges ) {\n\n                // Auto-create ranges\n                if ( rules.min != null && rules.max != null ) {\n                    rules.range = [ rules.min, rules.max ];\n                    delete rules.min;\n                    delete rules.max;\n                }\n                if ( rules.minlength != null && rules.maxlength != null ) {\n                    rules.rangelength = [ rules.minlength, rules.maxlength ];\n                    delete rules.minlength;\n                    delete rules.maxlength;\n                }\n            }\n\n            return rules;\n        },\n\n        // Converts a simple string to a {string: true} rule, e.g., \"required\" to {required:true}\n        normalizeRule: function( data ) {\n            if ( typeof data === \"string\" ) {\n                var transformed = {};\n                $.each( data.split( /\\s/ ), function() {\n                    transformed[ this ] = true;\n                } );\n                data = transformed;\n            }\n            return data;\n        },\n\n        // https://jqueryvalidation.org/jQuery.validator.addMethod/\n        addMethod: function( name, method, message ) {\n            $.validator.methods[ name ] = method;\n            $.validator.messages[ name ] = message !== undefined ? message : $.validator.messages[ name ];\n            if ( method.length < 3 ) {\n                $.validator.addClassRules( name, $.validator.normalizeRule( name ) );\n            }\n        },\n\n        // https://jqueryvalidation.org/jQuery.validator.methods/\n        methods: {\n\n            // https://jqueryvalidation.org/required-method/\n            required: function( value, element, param ) {\n\n                // Check if dependency is met\n                if ( !this.depend( param, element ) ) {\n                    return \"dependency-mismatch\";\n                }\n                if ( element.nodeName.toLowerCase() === \"select\" ) {\n\n                    // Could be an array for select-multiple or a string, both are fine this way\n                    var val = $( element ).val();\n                    return val && val.length > 0;\n                }\n                if ( this.checkable( element ) ) {\n                    return this.getLength( value, element ) > 0;\n                }\n                return value !== undefined && value !== null && value.length > 0;\n            },\n\n            // https://jqueryvalidation.org/email-method/\n            email: function( value, element ) {\n\n                // From https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address\n                // Retrieved 2014-01-14\n                // If you have a problem with this implementation, report a bug against the above spec\n                // Or use custom methods to implement your own email validation\n                return this.optional( element ) || /^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test( value );\n            },\n\n            // https://jqueryvalidation.org/url-method/\n            url: function( value, element ) {\n\n                // Copyright (c) 2010-2013 Diego Perini, MIT licensed\n                // https://gist.github.com/dperini/729294\n                // see also https://mathiasbynens.be/demo/url-regex\n                // modified to allow protocol-relative URLs\n                return this.optional( element ) || /^(?:(?:(?:https?|ftp):)?\\/\\/)(?:(?:[^\\]\\[?\\/<~#`!@$^&*()+=}|:\";',>{ ]|%[0-9A-Fa-f]{2})+(?::(?:[^\\]\\[?\\/<~#`!@$^&*()+=}|:\";',>{ ]|%[0-9A-Fa-f]{2})*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z0-9\\u00a1-\\uffff][a-z0-9\\u00a1-\\uffff_-]{0,62})?[a-z0-9\\u00a1-\\uffff]\\.)+(?:[a-z\\u00a1-\\uffff]{2,}\\.?))(?::\\d{2,5})?(?:[/?#]\\S*)?$/i.test( value );\n            },\n\n            // https://jqueryvalidation.org/date-method/\n            date: ( function() {\n                var called = false;\n\n                return function( value, element ) {\n                    if ( !called ) {\n                        called = true;\n                        if ( this.settings.debug && window.console ) {\n                            console.warn(\n                                \"The `date` method is deprecated and will be removed in version '2.0.0'.\\n\" +\n                                \"Please don't use it, since it relies on the Date constructor, which\\n\" +\n                                \"behaves very differently across browsers and locales. Use `dateISO`\\n\" +\n                                \"instead or one of the locale specific methods in `localizations/`\\n\" +\n                                \"and `additional-methods.js`.\"\n                            );\n                        }\n                    }\n\n                    return this.optional( element ) || !/Invalid|NaN/.test( new Date( value ).toString() );\n                };\n            }() ),\n\n            // https://jqueryvalidation.org/dateISO-method/\n            dateISO: function( value, element ) {\n                return this.optional( element ) || /^\\d{4}[\\/\\-](0?[1-9]|1[012])[\\/\\-](0?[1-9]|[12][0-9]|3[01])$/.test( value );\n            },\n\n            // https://jqueryvalidation.org/number-method/\n            number: function( value, element ) {\n                return this.optional( element ) || /^(?:-?\\d+|-?\\d{1,3}(?:,\\d{3})+)?(?:\\.\\d+)?$/.test( value );\n            },\n\n            // https://jqueryvalidation.org/digits-method/\n            digits: function( value, element ) {\n                return this.optional( element ) || /^\\d+$/.test( value );\n            },\n\n            // https://jqueryvalidation.org/minlength-method/\n            minlength: function( value, element, param ) {\n                var length = Array.isArray( value ) ? value.length : this.getLength( value, element );\n                return this.optional( element ) || length >= param;\n            },\n\n            // https://jqueryvalidation.org/maxlength-method/\n            maxlength: function( value, element, param ) {\n                var length = Array.isArray( value ) ? value.length : this.getLength( value, element );\n                return this.optional( element ) || length <= param;\n            },\n\n            // https://jqueryvalidation.org/rangelength-method/\n            rangelength: function( value, element, param ) {\n                var length = Array.isArray( value ) ? value.length : this.getLength( value, element );\n                return this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] );\n            },\n\n            // https://jqueryvalidation.org/min-method/\n            min: function( value, element, param ) {\n                return this.optional( element ) || value >= param;\n            },\n\n            // https://jqueryvalidation.org/max-method/\n            max: function( value, element, param ) {\n                return this.optional( element ) || value <= param;\n            },\n\n            // https://jqueryvalidation.org/range-method/\n            range: function( value, element, param ) {\n                return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] );\n            },\n\n            // https://jqueryvalidation.org/step-method/\n            step: function( value, element, param ) {\n                var type = $( element ).attr( \"type\" ),\n                    errorMessage = \"Step attribute on input type \" + type + \" is not supported.\",\n                    supportedTypes = [ \"text\", \"number\", \"range\" ],\n                    re = new RegExp( \"\\\\b\" + type + \"\\\\b\" ),\n                    notSupported = type && !re.test( supportedTypes.join() ),\n                    decimalPlaces = function( num ) {\n                        var match = ( \"\" + num ).match( /(?:\\.(\\d+))?$/ );\n                        if ( !match ) {\n                            return 0;\n                        }\n\n                        // Number of digits right of decimal point.\n                        return match[ 1 ] ? match[ 1 ].length : 0;\n                    },\n                    toInt = function( num ) {\n                        return Math.round( num * Math.pow( 10, decimals ) );\n                    },\n                    valid = true,\n                    decimals;\n\n                // Works only for text, number and range input types\n                // TODO find a way to support input types date, datetime, datetime-local, month, time and week\n                if ( notSupported ) {\n                    throw new Error( errorMessage );\n                }\n\n                decimals = decimalPlaces( param );\n\n                // Value can't have too many decimals\n                if ( decimalPlaces( value ) > decimals || toInt( value ) % toInt( param ) !== 0 ) {\n                    valid = false;\n                }\n\n                return this.optional( element ) || valid;\n            },\n\n            // https://jqueryvalidation.org/equalTo-method/\n            equalTo: function( value, element, param ) {\n\n                // Bind to the blur event of the target in order to revalidate whenever the target field is updated\n                var target = $( param );\n                if ( this.settings.onfocusout && target.not( \".validate-equalTo-blur\" ).length ) {\n                    target.addClass( \"validate-equalTo-blur\" ).on( \"blur.validate-equalTo\", function() {\n                        $( element ).valid();\n                    } );\n                }\n                return value === target.val();\n            },\n\n            // https://jqueryvalidation.org/remote-method/\n            remote: function( value, element, param, method ) {\n                if ( this.optional( element ) ) {\n                    return \"dependency-mismatch\";\n                }\n\n                method = typeof method === \"string\" && method || \"remote\";\n\n                var previous = this.previousValue( element, method ),\n                    validator, data, optionDataString;\n\n                if ( !this.settings.messages[ element.name ] ) {\n                    this.settings.messages[ element.name ] = {};\n                }\n                previous.originalMessage = previous.originalMessage || this.settings.messages[ element.name ][ method ];\n                this.settings.messages[ element.name ][ method ] = previous.message;\n\n                param = typeof param === \"string\" && { url: param } || param;\n                optionDataString = $.param( $.extend( { data: value }, param.data ) );\n                if ( previous.old === optionDataString ) {\n                    return previous.valid;\n                }\n\n                previous.old = optionDataString;\n                validator = this;\n                this.startRequest( element );\n                data = {};\n                data[ element.name ] = value;\n                $.ajax( $.extend( true, {\n                    mode: \"abort\",\n                    port: \"validate\" + element.name,\n                    dataType: \"json\",\n                    data: data,\n                    context: validator.currentForm,\n                    success: function( response ) {\n                        var valid = response === true || response === \"true\",\n                            errors, message, submitted;\n\n                        validator.settings.messages[ element.name ][ method ] = previous.originalMessage;\n                        if ( valid ) {\n                            submitted = validator.formSubmitted;\n                            validator.resetInternals();\n                            validator.toHide = validator.errorsFor( element );\n                            validator.formSubmitted = submitted;\n                            validator.successList.push( element );\n                            validator.invalid[ element.name ] = false;\n                            validator.showErrors();\n                        } else {\n                            errors = {};\n                            message = response || validator.defaultMessage( element, { method: method, parameters: value } );\n                            errors[ element.name ] = previous.message = message;\n                            validator.invalid[ element.name ] = true;\n                            validator.showErrors( errors );\n                        }\n                        previous.valid = valid;\n                        validator.stopRequest( element, valid );\n                    }\n                }, param ) );\n                return \"pending\";\n            }\n        }\n\n    } );\n\n// Ajax mode: abort\n// usage: $.ajax({ mode: \"abort\"[, port: \"uniqueport\"]});\n// if mode:\"abort\" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()\n\n    var pendingRequests = {},\n        ajax;\n\n// Use a prefilter if available (1.5+)\n    if ( $.ajaxPrefilter ) {\n        $.ajaxPrefilter( function( settings, _, xhr ) {\n            var port = settings.port;\n            if ( settings.mode === \"abort\" ) {\n                if ( pendingRequests[ port ] ) {\n                    pendingRequests[ port ].abort();\n                }\n                pendingRequests[ port ] = xhr;\n            }\n        } );\n    } else {\n\n        // Proxy ajax\n        ajax = $.ajax;\n        $.ajax = function( settings ) {\n            var mode = ( \"mode\" in settings ? settings : $.ajaxSettings ).mode,\n                port = ( \"port\" in settings ? settings : $.ajaxSettings ).port;\n            if ( mode === \"abort\" ) {\n                if ( pendingRequests[ port ] ) {\n                    pendingRequests[ port ].abort();\n                }\n                pendingRequests[ port ] = ajax.apply( this, arguments );\n                return pendingRequests[ port ];\n            }\n            return ajax.apply( this, arguments );\n        };\n    }\n    return $;\n}));\n"}
}});