PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /home/u588966950/domains/vtsinfotech.online/public_html/duroplast/tiny_mce/classes/dom
Viewing File: /home/u588966950/domains/vtsinfotech.online/public_html/duroplast/tiny_mce/classes/dom/Selection.js
/** * Selection.js * * Copyright 2009, Moxiecode Systems AB * Released under LGPL License. * * License: http://tinymce.moxiecode.com/license * Contributing: http://tinymce.moxiecode.com/contributing */ (function(tinymce) { function trimNl(s) { return s.replace(/[\n\r]+/g, ''); }; // Shorten names var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each; /** * This class handles text and control selection it's an crossbrowser utility class. * Consult the TinyMCE Wiki API for more details and examples on how to use this class. * * @class tinymce.dom.Selection * @example * // Getting the currently selected node for the active editor * alert(tinymce.activeEditor.selection.getNode().nodeName); */ tinymce.create('tinymce.dom.Selection', { /** * Constructs a new selection instance. * * @constructor * @method Selection * @param {tinymce.dom.DOMUtils} dom DOMUtils object reference. * @param {Window} win Window to bind the selection object to. * @param {tinymce.dom.Serializer} serializer DOM serialization class to use for getContent. */ Selection : function(dom, win, serializer) { var t = this; t.dom = dom; t.win = win; t.serializer = serializer; // Add events each([ /** * This event gets executed before contents is extracted from the selection. * * @event onBeforeSetContent * @param {tinymce.dom.Selection} selection Selection object that fired the event. * @param {Object} args Contains things like the contents that will be returned. */ 'onBeforeSetContent', /** * This event gets executed before contents is inserted into selection. * * @event onBeforeGetContent * @param {tinymce.dom.Selection} selection Selection object that fired the event. * @param {Object} args Contains things like the contents that will be inserted. */ 'onBeforeGetContent', /** * This event gets executed when contents is inserted into selection. * * @event onSetContent * @param {tinymce.dom.Selection} selection Selection object that fired the event. * @param {Object} args Contains things like the contents that will be inserted. */ 'onSetContent', /** * This event gets executed when contents is extracted from the selection. * * @event onGetContent * @param {tinymce.dom.Selection} selection Selection object that fired the event. * @param {Object} args Contains things like the contents that will be returned. */ 'onGetContent' ], function(e) { t[e] = new tinymce.util.Dispatcher(t); }); // No W3C Range support if (!t.win.getSelection) t.tridentSel = new tinymce.dom.TridentSelection(t); if (tinymce.isIE && dom.boxModel) this._fixIESelection(); // Prevent leaks tinymce.addUnload(t.destroy, t); }, /** * Move the selection cursor range to the specified node and offset. * @param node Node to put the cursor in. * @param offset Offset from the start of the node to put the cursor at. */ setCursorLocation: function(node, offset) { var t = this; var r = t.dom.createRng(); r.setStart(node, offset); r.setEnd(node, offset); t.setRng(r); t.collapse(false); }, /** * Returns the selected contents using the DOM serializer passed in to this class. * * @method getContent * @param {Object} s Optional settings class with for example output format text or html. * @return {String} Selected contents in for example HTML format. * @example * // Alerts the currently selected contents * alert(tinyMCE.activeEditor.selection.getContent()); * * // Alerts the currently selected contents as plain text * alert(tinyMCE.activeEditor.selection.getContent({format : 'text'})); */ getContent : function(s) { var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n; s = s || {}; wb = wa = ''; s.get = true; s.format = s.format || 'html'; s.forced_root_block = ''; t.onBeforeGetContent.dispatch(t, s); if (s.format == 'text') return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : '')); if (r.cloneContents) { n = r.cloneContents(); if (n) e.appendChild(n); } else if (is(r.item) || is(r.htmlText)) { // IE will produce invalid markup if elements are present that // it doesn't understand like custom elements or HTML5 elements. // Adding a BR in front of the contents and then remoiving it seems to fix it though. e.innerHTML = '<br>' + (r.item ? r.item(0).outerHTML : r.htmlText); e.removeChild(e.firstChild); } else e.innerHTML = r.toString(); // Keep whitespace before and after if (/^\s/.test(e.innerHTML)) wb = ' '; if (/\s+$/.test(e.innerHTML)) wa = ' '; s.getInner = true; s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa; t.onGetContent.dispatch(t, s); return s.content; }, /** * Sets the current selection to the specified content. If any contents is selected it will be replaced * with the contents passed in to this function. If there is no selection the contents will be inserted * where the caret is placed in the editor/page. * * @method setContent * @param {String} content HTML contents to set could also be other formats depending on settings. * @param {Object} args Optional settings object with for example data format. * @example * // Inserts some HTML contents at the current selection * tinyMCE.activeEditor.selection.setContent('<strong>Some contents</strong>'); */ setContent : function(content, args) { var self = this, rng = self.getRng(), caretNode, doc = self.win.document, frag, temp; args = args || {format : 'html'}; args.set = true; content = args.content = content; // Dispatch before set content event if (!args.no_events) self.onBeforeSetContent.dispatch(self, args); content = args.content; if (rng.insertNode) { // Make caret marker since insertNode places the caret in the beginning of text after insert content += '<span id="__caret">_</span>'; // Delete and insert new node if (rng.startContainer == doc && rng.endContainer == doc) { // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents doc.body.innerHTML = content; } else { rng.deleteContents(); if (doc.body.childNodes.length == 0) { doc.body.innerHTML = content; } else { // createContextualFragment doesn't exists in IE 9 DOMRanges if (rng.createContextualFragment) { rng.insertNode(rng.createContextualFragment(content)); } else { // Fake createContextualFragment call in IE 9 frag = doc.createDocumentFragment(); temp = doc.createElement('div'); frag.appendChild(temp); temp.outerHTML = content; rng.insertNode(frag); } } } // Move to caret marker caretNode = self.dom.get('__caret'); // Make sure we wrap it compleatly, Opera fails with a simple select call rng = doc.createRange(); rng.setStartBefore(caretNode); rng.setEndBefore(caretNode); self.setRng(rng); // Remove the caret position self.dom.remove('__caret'); try { self.setRng(rng); } catch (ex) { // Might fail on Opera for some odd reason } } else { if (rng.item) { // Delete content and get caret text selection doc.execCommand('Delete', false, null); rng = self.getRng(); } // Explorer removes spaces from the beginning of pasted contents if (/^\s+/.test(content)) { rng.pasteHTML('<span id="__mce_tmp">_</span>' + content); self.dom.remove('__mce_tmp'); } else rng.pasteHTML(content); } // Dispatch set content event if (!args.no_events) self.onSetContent.dispatch(self, args); }, /** * Returns the start element of a selection range. If the start is in a text * node the parent element will be returned. * * @method getStart * @return {Element} Start element of selection range. */ getStart : function() { var rng = this.getRng(), startElement, parentElement, checkRng, node; if (rng.duplicate || rng.item) { // Control selection, return first item if (rng.item) return rng.item(0); // Get start element checkRng = rng.duplicate(); checkRng.collapse(1); startElement = checkRng.parentElement(); // Check if range parent is inside the start element, then return the inner parent element // This will fix issues when a single element is selected, IE would otherwise return the wrong start element parentElement = node = rng.parentElement(); while (node = node.parentNode) { if (node == startElement) { startElement = parentElement; break; } } return startElement; } else { startElement = rng.startContainer; if (startElement.nodeType == 1 && startElement.hasChildNodes()) startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)]; if (startElement && startElement.nodeType == 3) return startElement.parentNode; return startElement; } }, /** * Returns the end element of a selection range. If the end is in a text * node the parent element will be returned. * * @method getEnd * @return {Element} End element of selection range. */ getEnd : function() { var t = this, r = t.getRng(), e, eo; if (r.duplicate || r.item) { if (r.item) return r.item(0); r = r.duplicate(); r.collapse(0); e = r.parentElement(); if (e && e.nodeName == 'BODY') return e.lastChild || e; return e; } else { e = r.endContainer; eo = r.endOffset; if (e.nodeType == 1 && e.hasChildNodes()) e = e.childNodes[eo > 0 ? eo - 1 : eo]; if (e && e.nodeType == 3) return e.parentNode; return e; } }, /** * Returns a bookmark location for the current selection. This bookmark object * can then be used to restore the selection after some content modification to the document. * * @method getBookmark * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex. * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization. * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection. * @example * // Stores a bookmark of the current selection * var bm = tinyMCE.activeEditor.selection.getBookmark(); * * tinyMCE.activeEditor.setContent(tinyMCE.activeEditor.getContent() + 'Some new content'); * * // Restore the selection bookmark * tinyMCE.activeEditor.selection.moveToBookmark(bm); */ getBookmark : function(type, normalized) { var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, index, chr = '\uFEFF', styles; function findIndex(name, element) { var index = 0; each(dom.select(name), function(node, i) { if (node == element) index = i; }); return index; }; if (type == 2) { function getLocation() { var rng = t.getRng(true), root = dom.getRoot(), bookmark = {}; function getPoint(rng, start) { var container = rng[start ? 'startContainer' : 'endContainer'], offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0; if (container.nodeType == 3) { if (normalized) { for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling) offset += node.nodeValue.length; } point.push(offset); } else { childNodes = container.childNodes; if (offset >= childNodes.length && childNodes.length) { after = 1; offset = Math.max(0, childNodes.length - 1); } point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after); } for (; container && container != root; container = container.parentNode) point.push(t.dom.nodeIndex(container, normalized)); return point; }; bookmark.start = getPoint(rng, true); if (!t.isCollapsed()) bookmark.end = getPoint(rng); return bookmark; }; if (t.tridentSel) return t.tridentSel.getBookmark(type); return getLocation(); } // Handle simple range if (type) return {rng : t.getRng()}; rng = t.getRng(); id = dom.uniqueId(); collapsed = tinyMCE.activeEditor.selection.isCollapsed(); styles = 'overflow:hidden;line-height:0px'; // Explorer method if (rng.duplicate || rng.item) { // Text selection if (!rng.item) { rng2 = rng.duplicate(); try { // Insert start marker rng.collapse(); rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>'); // Insert end marker if (!collapsed) { rng2.collapse(false); // Detect the empty space after block elements in IE and move the end back one character <p></p>] becomes <p>]</p> rng.moveToElementText(rng2.parentElement()); if (rng.compareEndPoints('StartToEnd', rng2) == 0) rng2.move('character', -1); rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>'); } } catch (ex) { // IE might throw unspecified error so lets ignore it return null; } } else { // Control selection element = rng.item(0); name = element.nodeName; return {name : name, index : findIndex(name, element)}; } } else { element = t.getNode(); name = element.nodeName; if (name == 'IMG') return {name : name, index : findIndex(name, element)}; // W3C method rng2 = rng.cloneRange(); // Insert end marker if (!collapsed) { rng2.collapse(false); rng2.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_end', style : styles}, chr)); } rng.collapse(true); rng.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_start', style : styles}, chr)); } t.moveToBookmark({id : id, keep : 1}); return {id : id}; }, /** * Restores the selection to the specified bookmark. * * @method moveToBookmark * @param {Object} bookmark Bookmark to restore selection from. * @return {Boolean} true/false if it was successful or not. * @example * // Stores a bookmark of the current selection * var bm = tinyMCE.activeEditor.selection.getBookmark(); * * tinyMCE.activeEditor.setContent(tinyMCE.activeEditor.getContent() + 'Some new content'); * * // Restore the selection bookmark * tinyMCE.activeEditor.selection.moveToBookmark(bm); */ moveToBookmark : function(bookmark) { var t = this, dom = t.dom, marker1, marker2, rng, root, startContainer, endContainer, startOffset, endOffset; if (bookmark) { if (bookmark.start) { rng = dom.createRng(); root = dom.getRoot(); function setEndPoint(start) { var point = bookmark[start ? 'start' : 'end'], i, node, offset, children; if (point) { offset = point[0]; // Find container node for (node = root, i = point.length - 1; i >= 1; i--) { children = node.childNodes; if (point[i] > children.length - 1) return; node = children[point[i]]; } // Move text offset to best suitable location if (node.nodeType === 3) offset = Math.min(point[0], node.nodeValue.length); // Move element offset to best suitable location if (node.nodeType === 1) offset = Math.min(point[0], node.childNodes.length); // Set offset within container node if (start) rng.setStart(node, offset); else rng.setEnd(node, offset); } return true; }; if (t.tridentSel) return t.tridentSel.moveToBookmark(bookmark); if (setEndPoint(true) && setEndPoint()) { t.setRng(rng); } } else if (bookmark.id) { function restoreEndPoint(suffix) { var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep; if (marker) { node = marker.parentNode; if (suffix == 'start') { if (!keep) { idx = dom.nodeIndex(marker); } else { node = marker.firstChild; idx = 1; } startContainer = endContainer = node; startOffset = endOffset = idx; } else { if (!keep) { idx = dom.nodeIndex(marker); } else { node = marker.firstChild; idx = 1; } endContainer = node; endOffset = idx; } if (!keep) { prev = marker.previousSibling; next = marker.nextSibling; // Remove all marker text nodes each(tinymce.grep(marker.childNodes), function(node) { if (node.nodeType == 3) node.nodeValue = node.nodeValue.replace(/\uFEFF/g, ''); }); // Remove marker but keep children if for example contents where inserted into the marker // Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature while (marker = dom.get(bookmark.id + '_' + suffix)) dom.remove(marker, 1); // If siblings are text nodes then merge them unless it's Opera since it some how removes the node // and we are sniffing since adding a lot of detection code for a browser with 3% of the market isn't worth the effort. Sorry, Opera but it's just a fact if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !tinymce.isOpera) { idx = prev.nodeValue.length; prev.appendData(next.nodeValue); dom.remove(next); if (suffix == 'start') { startContainer = endContainer = prev; startOffset = endOffset = idx; } else { endContainer = prev; endOffset = idx; } } } } }; function addBogus(node) { // Adds a bogus BR element for empty block elements or just a space on IE since it renders BR elements incorrectly if (dom.isBlock(node) && !node.innerHTML) node.innerHTML = !isIE ? '<br data-mce-bogus="1" />' : ' '; return node; }; // Restore start/end points restoreEndPoint('start'); restoreEndPoint('end'); if (startContainer) { rng = dom.createRng(); rng.setStart(addBogus(startContainer), startOffset); rng.setEnd(addBogus(endContainer), endOffset); t.setRng(rng); } } else if (bookmark.name) { t.select(dom.select(bookmark.name)[bookmark.index]); } else if (bookmark.rng) t.setRng(bookmark.rng); } }, /** * Selects the specified element. This will place the start and end of the selection range around the element. * * @method select * @param {Element} node HMTL DOM element to select. * @param {Boolean} content Optional bool state if the contents should be selected or not on non IE browser. * @return {Element} Selected element the same element as the one that got passed in. * @example * // Select the first paragraph in the active editor * tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('p')[0]); */ select : function(node, content) { var t = this, dom = t.dom, rng = dom.createRng(), idx; if (node) { idx = dom.nodeIndex(node); rng.setStart(node.parentNode, idx); rng.setEnd(node.parentNode, idx + 1); // Find first/last text node or BR element if (content) { function setPoint(node, start) { var walker = new tinymce.dom.TreeWalker(node, node); do { // Text node if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) { if (start) rng.setStart(node, 0); else rng.setEnd(node, node.nodeValue.length); return; } // BR element if (node.nodeName == 'BR') { if (start) rng.setStartBefore(node); else rng.setEndBefore(node); return; } } while (node = (start ? walker.next() : walker.prev())); }; setPoint(node, 1); setPoint(node); } t.setRng(rng); } return node; }, /** * Returns true/false if the selection range is collapsed or not. Collapsed means if it's a caret or a larger selection. * * @method isCollapsed * @return {Boolean} true/false state if the selection range is collapsed or not. Collapsed means if it's a caret or a larger selection. */ isCollapsed : function() { var t = this, r = t.getRng(), s = t.getSel(); if (!r || r.item) return false; if (r.compareEndPoints) return r.compareEndPoints('StartToEnd', r) === 0; return !s || r.collapsed; }, /** * Collapse the selection to start or end of range. * * @method collapse * @param {Boolean} to_start Optional boolean state if to collapse to end or not. Defaults to start. */ collapse : function(to_start) { var self = this, rng = self.getRng(), node; // Control range on IE if (rng.item) { node = rng.item(0); rng = self.win.document.body.createTextRange(); rng.moveToElementText(node); } rng.collapse(!!to_start); self.setRng(rng); }, /** * Returns the browsers internal selection object. * * @method getSel * @return {Selection} Internal browser selection object. */ getSel : function() { var t = this, w = this.win; return w.getSelection ? w.getSelection() : w.document.selection; }, /** * Returns the browsers internal range object. * * @method getRng * @param {Boolean} w3c Forces a compatible W3C range on IE. * @return {Range} Internal browser range object. * @see http://www.quirksmode.org/dom/range_intro.html * @see http://www.dotvoid.com/2001/03/using-the-range-object-in-mozilla/ */ getRng : function(w3c) { var t = this, s, r, elm, doc = t.win.document; // Found tridentSel object then we need to use that one if (w3c && t.tridentSel) return t.tridentSel.getRangeAt(0); try { if (s = t.getSel()) r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : doc.createRange()); } catch (ex) { // IE throws unspecified error here if TinyMCE is placed in a frame/iframe } // We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet if (tinymce.isIE && r && r.setStart && doc.selection.createRange().item) { elm = doc.selection.createRange().item(0); r = doc.createRange(); r.setStartBefore(elm); r.setEndAfter(elm); } // No range found then create an empty one // This can occur when the editor is placed in a hidden container element on Gecko // Or on IE when there was an exception if (!r) r = doc.createRange ? doc.createRange() : doc.body.createTextRange(); if (t.selectedRange && t.explicitRange) { if (r.compareBoundaryPoints(r.START_TO_START, t.selectedRange) === 0 && r.compareBoundaryPoints(r.END_TO_END, t.selectedRange) === 0) { // Safari, Opera and Chrome only ever select text which causes the range to change. // This lets us use the originally set range if the selection hasn't been changed by the user. r = t.explicitRange; } else { t.selectedRange = null; t.explicitRange = null; } } return r; }, /** * Changes the selection to the specified DOM range. * * @method setRng * @param {Range} r Range to select. */ setRng : function(r) { var s, t = this; if (!t.tridentSel) { s = t.getSel(); if (s) { t.explicitRange = r; try { s.removeAllRanges(); } catch (ex) { // IE9 might throw errors here don't know why } s.addRange(r); t.selectedRange = s.getRangeAt(0); } } else { // Is W3C Range if (r.cloneRange) { t.tridentSel.addRange(r); return; } // Is IE specific range try { r.select(); } catch (ex) { // Needed for some odd IE bug #1843306 } } }, /** * Sets the current selection to the specified DOM element. * * @method setNode * @param {Element} n Element to set as the contents of the selection. * @return {Element} Returns the element that got passed in. * @example * // Inserts a DOM node at current selection/caret location * tinyMCE.activeEditor.selection.setNode(tinyMCE.activeEditor.dom.create('img', {src : 'some.gif', title : 'some title'})); */ setNode : function(n) { var t = this; t.setContent(t.dom.getOuterHTML(n)); return n; }, /** * Returns the currently selected element or the common ancestor element for both start and end of the selection. * * @method getNode * @return {Element} Currently selected element or common ancestor element. * @example * // Alerts the currently selected elements node name * alert(tinyMCE.activeEditor.selection.getNode().nodeName); */ getNode : function() { var t = this, rng = t.getRng(), sel = t.getSel(), elm, start = rng.startContainer, end = rng.endContainer; // Range maybe lost after the editor is made visible again if (!rng) return t.dom.getRoot(); if (rng.setStart) { elm = rng.commonAncestorContainer; // Handle selection a image or other control like element such as anchors if (!rng.collapsed) { if (rng.startContainer == rng.endContainer) { if (rng.endOffset - rng.startOffset < 2) { if (rng.startContainer.hasChildNodes()) elm = rng.startContainer.childNodes[rng.startOffset]; } } // If the anchor node is a element instead of a text node then return this element //if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1) // return sel.anchorNode.childNodes[sel.anchorOffset]; // Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent. // This happens when you double click an underlined word in FireFox. if (start.nodeType === 3 && end.nodeType === 3) { function skipEmptyTextNodes(n, forwards) { var orig = n; while (n && n.nodeType === 3 && n.length === 0) { n = forwards ? n.nextSibling : n.previousSibling; } return n || orig; } if (start.length === rng.startOffset) { start = skipEmptyTextNodes(start.nextSibling, true); } else { start = start.parentNode; } if (rng.endOffset === 0) { end = skipEmptyTextNodes(end.previousSibling, false); } else { end = end.parentNode; } if (start && start === end) return start; } } if (elm && elm.nodeType == 3) return elm.parentNode; return elm; } return rng.item ? rng.item(0) : rng.parentElement(); }, getSelectedBlocks : function(st, en) { var t = this, dom = t.dom, sb, eb, n, bl = []; sb = dom.getParent(st || t.getStart(), dom.isBlock); eb = dom.getParent(en || t.getEnd(), dom.isBlock); if (sb) bl.push(sb); if (sb && eb && sb != eb) { n = sb; var walker = new tinymce.dom.TreeWalker(sb, dom.getRoot()); while ((n = walker.next()) && n != eb) { if (dom.isBlock(n)) bl.push(n); } } if (eb && sb != eb) bl.push(eb); return bl; }, normalize : function() { var self = this, rng, normalized; // Normalize only on non IE browsers for now if (tinymce.isIE) return; function normalizeEndPoint(start) { var container, offset, walker, dom = self.dom, body = dom.getRoot(), node; container = rng[(start ? 'start' : 'end') + 'Container']; offset = rng[(start ? 'start' : 'end') + 'Offset']; // If the container is a document move it to the body element if (container.nodeType === 9) { container = container.body; offset = 0; } // If the container is body try move it into the closest text node or position // TODO: Add more logic here to handle element selection cases if (container === body) { // Resolve the index if (container.hasChildNodes()) { container = container.childNodes[Math.min(!start && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1)]; offset = 0; // Don't walk into elements that doesn't have any child nodes like a IMG if (container.hasChildNodes()) { // Walk the DOM to find a text node to place the caret at or a BR node = container; walker = new tinymce.dom.TreeWalker(container, body); do { // Found a text node use that position if (node.nodeType === 3) { offset = start ? 0 : node.nodeValue.length - 1; container = node; break; } // Found a BR element that we can place the caret before if (node.nodeName === 'BR') { offset = dom.nodeIndex(node); container = node.parentNode; break; } } while (node = (start ? walker.next() : walker.prev())); normalized = true; } } } // Set endpoint if it was normalized if (normalized) rng['set' + (start ? 'Start' : 'End')](container, offset); }; rng = self.getRng(); // Normalize the end points normalizeEndPoint(true); if (rng.collapsed) normalizeEndPoint(); // Set the selection if it was normalized if (normalized) { //console.log(self.dom.dumpRng(rng)); self.setRng(rng); } }, destroy : function(s) { var t = this; t.win = null; // Manual destroy then remove unload handler if (!s) tinymce.removeUnload(t.destroy); }, // IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode _fixIESelection : function() { var dom = this.dom, doc = dom.doc, body = doc.body, started, startRng, htmlElm; // Make HTML element unselectable since we are going to handle selection by hand doc.documentElement.unselectable = true; // Return range from point or null if it failed function rngFromPoint(x, y) { var rng = body.createTextRange(); try { rng.moveToPoint(x, y); } catch (ex) { // IE sometimes throws and exception, so lets just ignore it rng = null; } return rng; }; // Fires while the selection is changing function selectionChange(e) { var pointRng; // Check if the button is down or not if (e.button) { // Create range from mouse position pointRng = rngFromPoint(e.x, e.y); if (pointRng) { // Check if pointRange is before/after selection then change the endPoint if (pointRng.compareEndPoints('StartToStart', startRng) > 0) pointRng.setEndPoint('StartToStart', startRng); else pointRng.setEndPoint('EndToEnd', startRng); pointRng.select(); } } else endSelection(); } // Removes listeners function endSelection() { var rng = doc.selection.createRange(); // If the range is collapsed then use the last start range if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0) startRng.select(); dom.unbind(doc, 'mouseup', endSelection); dom.unbind(doc, 'mousemove', selectionChange); startRng = started = 0; }; // Detect when user selects outside BODY dom.bind(doc, ['mousedown', 'contextmenu'], function(e) { if (e.target.nodeName === 'HTML') { if (started) endSelection(); // Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML htmlElm = doc.documentElement; if (htmlElm.scrollHeight > htmlElm.clientHeight) return; started = 1; // Setup start position startRng = rngFromPoint(e.x, e.y); if (startRng) { // Listen for selection change events dom.bind(doc, 'mouseup', endSelection); dom.bind(doc, 'mousemove', selectionChange); dom.win.focus(); startRng.select(); } } }); } }); })(tinymce);