wdgt#H$y"d Resources/about.png Last.fm 1 Resources/last_fm_logo_icon.gif lastfmPreferenceGroup Username text lastfmPreferenceGroup Password text yes lastfmPreferenceGroup popup Automatic: songs are added to your profile unless you opt-out Manual: you must opt-in for each song to be added to your profile. Radio 3 Resources/bbc.png radioPreferenceGroup Radio station popup Radio 1 radioPreferenceGroup Use alternative player popup Ask me If the embedded player does not work, BBC Radio Player will automatically open GG"gHPNG  IHDR,RUgAMAOX2tEXtSoftwareAdobe ImageReadyqe<G0IDATx}ŵټP&I"؈$0ƀ&#'Id,0DA$r@(ڼ3S];aI}鮮:}U8${te1x%|t)KRGTNyu#-@ h)Y8r֩SFDsU1 GՑ:ީS'8p`V%Nw),Sv(cB*,*R $rI')BaZf }2cZt)+9t衇Rii)ŋiɒ%hڹs'vx( K C9ZV+㏣kײ:F}=4o|*..VB0*+kN=uI f)uz3Z) YV7q.*N-&L mذ^L5H^Y6%讻kјo}n4?Vm]RHhe_B 28oR8nOkNϧp($t PuMp t7(G>_)_}㎣:g6ZVDa {+cr QnZڸiܱC9wyj E|>graAa?o&L@[6oSZ|Uu64OtLobjնt̅ɉK KRDdDTYvl߮.\HǏ}* J4.X"P9,sTĎ7^/]qПV h#s#TiˮDa BjV'SK4Cќ0]}mVXcῷ?xqNlRE&Z mw:CXMX)؟FJӦN͛bXŔ Or{_~t1 9s(⃣}UŤIhs9ԓ̈́=$LWieڵ~z:Y(GҥK驧O zr1Q * >˗Iׅ:,U.0>'QK@P?nVuhܸS3TЂ9oXO":!r=SO?k>ngSm׮]KյL9P/MG&‚B&4۔˘poڴƕ3\U(FxVE'b  Oz饗TphN}5b*'˄&'q?%%dR啞? jɍoW޴?u0)Vm8Wm Gn[Gt*쪥M5Lb6VC-jjrgԻ=z`uUO0 ; S t`M7ݠ1eXzITEYP ! 7HS8=#hذ2'0E%A [ ?7Lg6 0\ꙜMȨMAK $lǁY&, {P >Q[6dcq]v˾R-k<+̹&E=7Sɸ8d8֧O>Gv)?:/ x3m{{Mq)*/77GA8r&N|P pǫzIUUVF%'/My9Hg}R!ޢOK~1q\J_ ݍO%H$8N&,KG|ʨLRR(nE.%u{j9|PiSL}IiS}M$W=" ?yUWo0lgS_W6v2io'˲ >Cl sMP<{Rt<k\7HVPmUp)+w߁2I XV9Xi9S't#PM})1;찴u`K/;_u$?9e3t(ѰN<ϯ3u*Ƅ{oz%o =)uo]s @oz>~)Y9~m,lgq_L]~[}u.u]6-^?6&5_|{DA :Obm?5R *0qEXyc>d% I$k1\J ~nbQ|Xk0av|DŽhlg7Mp<]ٳmjԅI:~g󗿤HS"l"~ev]mm{qzC|f2a 3qTvO~Ŝcso\ LsGY笞q(wv}ULrjN`#k:fMyQ9eK5SZk!n]~+ |~w +;\[Y/\skr2|h 4z29y p oc: gc 7 (݇ȇ}(;ڰ~ǮuH*S%ߑh#M]"[;CjU#K۩R;fdAfeMQtBơ<02^EYlk& RLB 3P <ɶ(_dzvN{!O=  .$DEz-"@RZ˽ !Xn;;nW*S3[)!WTAtyf.fJ֗Cl?{ɔuH^;I@^C%]labPf._a|#ijڃ~x) ǧyS}V1FXN#rYZtP`+[-JS:yc>ieBz ~nfJO.)L#~3 L5@, 8B%eTU~ ֗quLh yuW _JpSF!VySL [+}xk2_b~0y)JWnaÔC: 3][ϐA?ڀ2 [8+ )r1\wUyӦOsMd^;w6;]xjx;"_E#j焊\ZyDd%_vVzGB&@l̔2Ԛ"W%8Cy,N)Q$.ks$R~y{pM}Z0cƧ/DO| M$,0dL+~ado_.VΞMt/23U >Pw,L(SmDHm;}C-0޸Zlt~3k\)S+9Ą_N?>3cq)n3༿\v'LFtv_#t$XZ` F`{eMl lԶ2 54弋i(.&tԙ^ELbʅ'I(D3)P 5b4lTph β7g"D aQJzR`ử<3FklUsze.b->65=Et0q2u-Mw=S;$:c.hŊT09jOΌyL|\_iա߶vlV͑ߣ@|}1oo#: ]D%%DW;ҩ">VAtj{!:sJr$մߨs1 (]xDcϧ}d/h ϖ'HtPW[Gx?R4ػjNkZ|Y-kU3qɡo3C7ӷ;u^άB/.&fY}O<_?2[*U (~_P=9BT¦K}ҡ_G>: ;to/GHty描K]J3D_y#QN;&6'ZUUNe0m[vD7oVB;>SSOCzWg]JNGJɗh+W‰TVL2y tYn8>#O4Ttplh~WuZz$C̬p`]+_HBQZ~==uӇVSƍߤsӦ3YH(,Yo4p5}]:Uz_}EeO?O>P.P+AwgUմp:UerBG෗I.?2gOE2ȬJ4sHN!ւ(,;U-3 <4E՘}5Kԟr)l[W@#f5'ۓCt$6.s"BE5y uHHMp D$ |1 mAXN#,PDrZ`XrS=g>tgg~?[n*[p+f1C8p { eic:ne#=BgIݞ⯲RaGRXUL|O@.81,k>hs,DX^6I!rx@ܡcw:޿A VP٧SDpN=mw: VqϨS7ػǟc6hcb*TDϿEJ[Q᧟CPQJG%5֋oSk<Rk Xh;d$\ {ЄPږޕIvB@:V^}4WOQ0MlJ_hʾwouLB^EXb<]FgQb,*O>X~|r*橥졦rRA*3v]y/E^{*w#ءv1?8*>| ,9WyU>~\ڇ'+zMPHw3bGSXPCTHhΝJyk>(R l@X\~%7LUb ²?qՌEa5Q^aKuR܉L'|]" ''9uqu&MO#|K N42@&HPr@a$j,GP%W*Ig ɯK @] BX@ kF^ZZ@ cVBXBXVFPKK %%aajR))=ZM5 VN3? QXMC¡)Zp?[DbR?-_Sp>)WOYYɼD2tNP6~=MW\U;Epr<]%*3NTNj)(ҔgNGBXAGWXLI Ba6%cz=hBN{œ 9Jrg Mm޵VPMgt2jnsȵx:*%dAX-faE\N :MNvgYf;T)-(dr.jEB/h9NjysgPNz3`WX]3lyyPmYڗWO!:UtNE%T(Š͹%Z+ ;. QX\V|NJC0Q-U%1;bd֩cE nWZn](,`$,('' P+ Suy93yXdWUSYGΝ(ڙ3Xi1EvmF%%<";tc. ˋ3aFՑjVbz C0bjjxNrxIg a { aY*,e)V"A9(.eT͊'V]ExRePGv B8@RLZ_*6}w! ;Ϝ(a|䅄vh:gϖQ,VZ@i9 M]9XA 'V>]=ycTQVNHXoTn^!SŸjzml@TW{V1RBc>Eԝmҥj_W&멨W//Ia33A]JW\I|F*&:SiK.c/7؆`ς=ڧ i,V7V<_SS&My%%bդMa*.)VWԐX"J*OB.Dr"JRji21xhDsD չyɲy_.**]vz}ߔԚ(dɄ5TWW0=r@f ?p@;Q<3iQB} 9:\B|: #+y?6?-/7ӂ)HTvRw4؎HޕB;qp-z^.d(1YE6DXdͤ[\B9:3 dFQF)ğ\wTDbr'ڛq:I'3E>[Td~6B3 7:5mȃr̜qa&/zvZg&  _m68dI\lyl+=cy&g)ϛĪ^y3.sg X*!}J,RT@JrVhkNwe^q|$%hpʩB 't3:i':IuiZھjtV3VstPc ɖ,ڽIݝ&~ؒS#Z^a ŎDg9lBXޫ-,G(,%+'޳p:ص~=?_bFk,[.;hrԥ P9O TEkxuVΜ됉&h-.VP'2!_ҫ:֜z#`ƮۘM%Z=pIskq6% i`}5xE#hؿ~o[JD2LJ} Uvsۉs ;hcCK8b1PD`끣G{mhjB;M6Phw:hidg5VTNjM5 da!UU1 Tiq ypl[o uS!I.mҁrغU]Q} a5Jw)ޑlwAj ж}a,Ō{w^۝MU>la@tQ+ ?P{~ȓXLEG;PX\F8V)8';bFtm@Y1.[5@dM~FdaQRXp] P?\JiΔ)j[/&uLVeLCVyD_EEH_ b%ԍfx?6Ugc.o3멧 Uq  2 .Գ sϥ<цN؝ m6*t_,MJ1'/ڇ#/P}sM8ɳ'7v6LH ?s }WCakTo`#ߵ|yr? ĉٌߗ{r:jjE $@BiHeya%2öPLjLm 8ԉ"HPRukOG!+3*ܭ6wb ǏWp fCe%P@\rID6dBG]F h;3spn6èc"ŀB{1%l46*gOXlmke_7`RvbҪ yꗾM?;룿 FVd>y{70v&q{L,1'11LE8!e(bhOn/QǼ/Ưp jn;zhG9y?.dRCی h _GuER T1b`C]~? f Y 9 c $P@ : B[6TPC;]Aݴ[fL ڟA﫦* uOP. 2_}!?Ԯ::&||FwPٕ^S6k8gːY[  j\6<7V0;2ӔvT z~S͸q[V-SBs&eE8*SYGE4ձ-8E2NƏ..Vțuǃ{dIOh^1e۪?5q[:4xZz+%t4R6MjS`12?s3mIy-gBXAՒZq8L7cVJKܔIͶ$)e$8'@Nf by\G[8_MǦ |e#=ޤ"9NXg)1z1O--r=sݭ|hvy>5 2E=sAӷ0[=͌-o;ۂq[>sM2K}Dr "ꩢ/4M8@Ւ؝HwǓ^.Afh'#I*(/fHXSMC@P"DrtFCO3^`"B}MjHxGK#gf}Sn)Oԯh,e&zM?cJ jo<Z9]8lD|>IY35*C L(`fK kwfk0RR(W'ؾ0s#=]< m>sk3$ol A[VK%reL0K2>*'i>&<7X:BBA:iiVWjfIrB _vԏf;N"/ H-X t!~S5y~[@ +~?,! +hEYB4i%gLHǐJ4pIUJi!v>+dL8mRAr4 ' i6²Mi_]YU(, 3MdɦiCT*%I&aJ#,އN;fLNUTC:6h`/(n0`֬\,Y0eߌL c>m[˴eq}G(3 d7mCA"A2P.Gi杋ܗO1j. %][ |&PYosCtPe^;;ߡ7`Mcp7aҤ/D"D//q+RG Sv>u!dE>\;R7Mm1фU yPڊe k'LLV|_Pa*o>1Hg&(sv*N?^z957aE 'ReMOcʀ˼=n??@)tǯkB{LQu >\ݓ'{ү0xXτߟ9%&܃ܓ]u|SN4yL<(ǪBu%>CG a\} gaݣ00AqIgPV0̀Lt>wؗnLAMe?yv2P[Qw'wi_Acx&^o R$Rwfѿy>Ňӧӱ&0ϔ 4H~/{"Ym|6i;Q!ts8p |]_}J<{6Ц??`S)p4sƤ fM 8#/XVC@VcYw?;3s!Lg#!4<Ħj(i?ιLȏzWJT(=6.pp]A(p׿dw@Ggj+ɛ}qo0kn w;gs a+ooqv4[2A{op*BŖj@LB@ hi@@ % K QX@@ QX@@ QX@@ QXU#DXBVCVķ M_aaGS\+$u>.kVB koI vܔ+,|洃&&w2GsNM n紆R}@ -[ܳLsvMt&!l-\r8hN9IW VLufrZ kMIð,+TWμ%)Z_\FMTH5Q`-V)9Ej]H&/!0@, mm[|D=VH+1AoǛb bНrM~9v-Lu濏s<@(( @"YJ)"ENCYK dx.JWe2(ֺ2)8GmBA&M10[%xᱯuQF]a #}M!p]P^z̴Aj(EaΉWಀ3e~-Į[l tEXtComment̖IDATx=HPQRu  n:;Y';7\,Xp*djj7Q ҢXQɹӭzkK|ylllll~*!˲Pb"L.!IREp@D"111Q$IRXESS###J.#d$X$t:q$e}ۜbHss3 ;;;Wxή2H**+214jxEZ##$CCE؜ŹᄄiDS<;--0%%'΋GGHŠDLVUX81L 557~ffg>R1 :?Ǐ?>B-]]_:_f~~97 "<('*940/2<9;9=МY`rqpw|Zrwwx0>毵oopPV[bWVWYYZ|NNO>G]䀀~HHK77:ăВ'=5㯮7:x+>挖PfKeQl!,xF H[ȰÇ#JHQb‹3ȱǏ5ɓ(Ïˊ+c"|/U6iɓ ͜سhU*}XѤKkʓc13f4iRHY(CK'\u >U5k-8Rq-@8pvժ&EGv?Vpz`i`!Ch&8s`@*'5u a | 1Qt& Ǝ!h#E`0|xNbdD*$"b$C:%wVQXc/Ҡؐ;g8) d ,dLX# G *A Z6@81 zNsDT0 X=4X edRm̓)9<<:\؈T<؂(!/g8("0uXܴ(@QNwNVFڰt(BfC2`v`@A WaP cYF+AP\Hq`AA)^UG6 `XE"pC =HG@o!눀 *$ hB60[P%y<(QPFd8ɈGCj> ,Ni(~&cj<J 9BOA+,0'L [ [Uc OFL(NGl`>dm:B;@@!C@D2@ DЄ`n`a0 !d`X@ R D0A> DA AsQ[;d`(( brx>g B0A}`C >", 5=€B`_C|wS$XpH"h M0 㰂Dp-\!SC`DZO _D! z~&@>tgd 0ě P vWV'Wޗ vVA@ P NPxg (xP >&tRw`dpO56xowo&L(g=悱w{'F 4 A*`P PA  `gh =VnSk7n*  * V:0wrP0:`t@Vp%@6Iv啀gfCv@ xpj`hr]cw 'rЋwp@  ! sia a3f@ P Q6Hf:pc>j=f} PfcvƊ A+ ` xdhjBhVcEdlQPfj~xQPL !jffj&g\vgwgǑefdhGwSIcvP &hf}!hqvj\ld/}" )Fڸ\7_- iC_"Yz P?C W Cqguc)|q{ٗيM)WynY;1|lJFIF``ExifII*CCFK" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?(_:-WJּx3Y»o\mdemS~֓K2Kjg6xcdq\QWiwv?+Qd?nk 7o_Y>#~^ ~,k?h0}a0uw%qn4 k$v"IM*9`uo"G:w(.b&5Cwo Ū&_TFPVjXvUwimMקAWÿFK~Կ h_~o>qվk"f|sB]jN/Pյ)!l&?9_3]~Ϳ\s>GuC&:g:\Լ' mmckkSOgGIN/w}z_KI_Kv?n+w`57kOo~>4-'_śK.<+j~+CHyykkپH?~j_~!tOߴ'ǿp+uo[x/nmoL]q6eACmnӧ$*{yo馡gkj QP 8w0nN.Ĩ~== 9S:<}꺏u 7Lgn>? #3mد¿8ߍ~_5|WsN/:xG~ k΋o~$O4]Yj:}s[Mu}ig+iRjկuk FcCQ'y~5#Ig|_|/yg7]_:}x;}R#J!D$_T?5)|ޙ@^2+ۍ| _C?;gNOq ƚO=ψt|@{m5|Y6P# _|{WO g~0>n ?°Wv_tҒ&cM47?|(;EVZtm7m//? ߰*/>2|bvZo)> xx2ΝgYͦG _YڻwŢjqF^ohΗM_a=Ct[E5 \a^wz_'  g_ڧ]? c[߆ [UomF}/Maԯ?yI|}_?k; {boMYAx_NҴx^)ӼI-{º:s_;&IjZRNϿ[W}˯( A@鿵V6o oo3%ė/څůofl'M"oy0-?lك ooǟσb٣Po 'h|p5u/k{]_j~)Qu {ĺ6z:R<vve~;||ٿ?ei?߳|&-'X|<^H|k#Gsx -a]/mj'5tM}m' 1~˟?io/>H>1~ϟ>|?oIߊRXYx |u =rG񜚦[鯧Y߽m[{ֳI{-5z~w'ӧ~E=߱?mχ|1#_kke֏]ŊYMz+iJ^'M~*w~-T1x#$xwu >Ywx➥uMhW:u\Zi-Zdq [(qQ-)J+&՟mbH\7 84Uԫ֧E:Tv?uߌߴW/؛㧅f7~> &SUҵoCh'c4Z+ C^}o?i?jR6yY#=K?OxG7<-~.x/-|WnkmO]~fͮ,oڛφ?/mO|V~%~X_ /5aˬE.Om%o/$8o>ؖng nWLyY W9#o^W>5A&.В(yI>va?fJQ/Jy^tf[e;\vkQ+lßSo~_i~f|fSƾ,o|&mK&fM[_ֵ4{-o2o#NJ> _#7m?~7o~1 6+|8mRO|,|rvwךj!̦bo}˽ND׮>2WG/EmjJjz@tK{YO7iHH?h(VWAڟ4/ :*?|A_%|S î+x#jm>yg%\ip8y5:^TQqI&F8Ř_GNXeX8yU,WFZ8E Cb= OkkC_oRM|9'{-||@sc x+s}eVjK`V/ٛ !?o| /3=?g xcǷëi:vװdM0a0h+XhFR6T"ڲMO#?gpWqg@fk=\NfW+aNU8V $SOr5$Lڨ ~xU*ȿ,~04񖡣CŸe֙Z=/ixwH0Zgu|Eo46%<c҄TSNrm()O&۾Uxoy~M8 普7 e؝뛏|2h_ r_ xc\|o?ٟG_}7fm_E~J׾En>TgCR?mrUxh7о xCyxJF s[TZ5/ Ma tAPڞo&=K.rx?RxMCj&|A&]Am&u{f+*W-*t)SGsyd$T#ZZ\ҵ|WM:6hV|5ּ9x[M'D_ڕm^/u.*ަ Ʊ4zFMo?U khֶz^, $4 ૶Szwl|O<(dTsᙢ5;IZ~s=㏴7K%;?2eß_X>kצ_46:]Ѕuss<e<,W.SfU*O Z1c<B.tjכmǚ?zS8׊, xne? @*nkcqqnbkʳ\ GTpUxL袊\ Š(((((ٟ@p{PNG  IHDRJnAcZ pHYs  gAMA|Q cHRMz%u0`:o_FIDATxݻncUΐIf AH\n@pDAAԈ rP_eDtGZrv7xQnouw_ hamtk$r"tt q\kՎ7XH9Ir4)rf{삹KE,qkpC$۩PQܪu$&ybF.qYOrP=n&ƗNIGn^$' W $O|U{ $'Er"SU}$;`M{1CaalܮH$7`ݤVSu3S< Vf%'6myQڵ=\r܅~w*dG߲-Cپu0B,_-'vr#tL?=ދ2" P B B B B P%P%P%P%PJJJJ@(@(@(@(JJJJ@(@(@(@(@(JJJJ@(@(@(@(J粽 %II%u:5Q,wErڦKWS([rX~ڵP5fG`Y#ɯI~ZwTa..%ٮI$[5me0\=+V{M#N'ƃݳ$*B Hվ⼋e[gKȹ5C/j$Bla`)ש-d;nQW8+tⴛ O#O^IENDB`DPNG  IHDRJnAcZ pHYs  gAMA|Q cHRMz%u0`:o_FZIDATxNA,x GL\xKoeUA5yC2yS?VM&YJi d~풬ZNxwPvI$٬^SUvI&YI$ɋ$=mt`&Gﭺu(BYm{;s5o<H@(H(˔8" 唹$Rh?I@(0rW" enr݋IƖ ʳk &ڡH2/\Kr[$ڡ\Nr#~7 KҾ=oG eX?4;5 ~ʝow#K0wCyToSB9gSʁPދBtK ʶݔӄrjue1l(}߀P6줼p Ϳ"R?Ǐge199o$,@\c AzTMDD3 T \ RpEG`' ";AyTТ@}&i(m}=dV?1Fq@g߿'=z5Ç>Qc @T)@=(d#7o||JD"\ 1mϟ_6f9rkR={1D ԚU\P:v'O=FV@Ѭ&yÇ89AM+Zi3DAyv ۷7-=D&."v%5k|&E.AXp0ڵ TҲن .۱cѣG^b%3B@6ʠ x4 \ك޽Dݹs4 Ti ŗ/_^yy&Ν;?zJ`/u Dv A|YYY177,<95@x=+  QP  8 99cxxG`R7&A @XBsrrr o mbYYY@axl1 1y KA# |I! @~%Q{ՕPPaP @d X @ddd0Լb _^ia #ҟSCIENDB` GIF89a 7D\ZꯤjdWV⋀~s@I-A:F"=㌁0B1B駝$=%>nf袗2B3Cn!=>HIN(?c^{z9FXWJN\Y.AǾ갥騝FLꬡ벧&B柔䔉kdb^GMż馛䑆[X馜|vm{q噎嘍CKzpMPPRKO䖋ST?I~㏃e`wnꭣ䕊㎃8Eysk벨㍂߁wle쵫icUU~t圐㐅ph^[zDLvlLPxTTȿngmfgay촩ꭢꮤVV;F6D杒=Hog|䓈嗌췭AJQS߀uBJ}袘|r߀vyp쵪ꫡ}s<,  H*\ȰÇ#JHŋ3jD ,` E*\R)Eį@M-xfe&PAׯS(~9U3*+_WZ`@ Vq `J`_kw`f\ b+0W)~jMm‰07^ͺװc˞: ;,<,hPNG  IHDRKKhަ pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-gAMA|Q cHRMz%u0`:o_F!IDATxhVg{9OB&YeY Ndq"dNW8""E)H)R)ŕRR)H'"E+"R N\,s!y?{}m9y㾿^>'yeY۬ f[.o~_]mjDuq(mID;mWX˶ݱws8>߂vꖵ<}ö7Ѷmirm{`?-0[  k/vnthǍKybOqq,yv{ڿҀv~7lBֶmmli׌X;!+}}!NF@0!u Λ{Vssbl*|m[o U`]cm:_u"ҁJ cbp %K2@%ۃY{mGl`{>˧pd$]w9ٱ Y6ճ߶Ml}~ cX׿)sǁ2/'.I0]7{I+pMk6ۅ<?0.|z+h dmAзym?3a:??W]~|6ݘ6.s^4>@fK0R3`~2/;R^r4s P:dg@#E{[o/Zsݸ/~k~`9*@ e>oW=nq(ǫ: 9~_qQU4-`ܶXm[eG_J 3s3}bm~0qugdd$NFFbB3M \0|P)@4`ٌі?3o3o8f8s}`? TICIqb2S; UW5)K׫Wb6]S@{Đ/eoq=$''E:F"|Bm&*7`fwb;GZh:$/v~@67E1/ I b!f(\Z|CYg񪛱_,lYz;j]2W]CML AF}>glkG\p.,@ƀ3bR+,O],!w$uWLª;s !,ƻw);PxxfjqX-:NB`Q&f, 4 !Nm/b;҃P (6Qdv}ʨasL`d'kFs B'LW4`WC0c/CC\F3;w 3wEvh7c[YX0faά˰o'eL*x:bh-EmW=ц)6IgV E?9Z`C+Jsi™ ]8$7fR)fr&*{rQ|34} (fۉ-C7xK%q&q]y|cSZ2R"?²$DqiP×<࢘FcWfIi 02dOcQ8Gq<_9[q ʀXX*FZuګ0 TbdB"YdfC4HD;. ՗Fc ) aF>Jc:-B[0[biy' @ۢb=q\]oCއ~- q - RoF ۲W5Qf:^kޘWb6'(juB%8u|̀b!\5iuA4STf!W] E/ipKоh@V +`gMZbN`=;h13lY ~1lb\@K<ݯ SFV3 4K Z^`sKKGNllxϢ}_FwlXYG?_3ԧQ[?;FϠUvY7:(d݁=-YLO )Xm?p츥1Dh$Jwe?mpx69t7l,ToAd,1yoPbRdWbLnG`a'e1wihm,-J~4>b$fs)bR5˿r{{؁UD܉ ?X$1h4<+hBC]Z=P\FmblL` ˷?vqpI*-zRQgzc @m+Krv1L{q`=B$``vvfy]ht+/?"_k\~Ƣq~=&3|d 7:hمŲg(BWj!Hvk@Yеט=fjo`: &BWUk7c>XӔOAH]Q:Fƪ1rSl৭Pjd6/FXsZQvm`e 2KC>{~֠cuLmy梡e-G4^"j5ihY//e= j!hQ+yhH#h8s=@dO#qݑjm8wvP`hO\C3Lّ/@%~7 [џt^/Fz@J|[U7˂S`UQ#iRH,(PIZ!z!Ovv}|#,|<"kR7~7$ށ?e-[ hh(n9'c9%o1Ii!܅lN ïZEo8x `d&0 @hXTb 8bbWFET<~ICnZ.iBy4jZNoyXTO*|*IU`/&u cOf0i$s1DΡք0f|6!>`iQ7f!G߶sA\YY;`)00BdW;\CgKf`2O` Gg&`[É/L٧%g,-_+iݖ۪x5K5վ04! [vgʁp#BKdzO< >YM Wt j5& Il` "ϒȮSgb*`;crQ+Sqdg,Z9*6Cr-AT/ qN( Yf;o[5o"$ބ*NLi*w6"g "_SQt^nu]!?= Dͤ0*iFjYE` iG=@K }-ї4 do)$l8f"6?_c͉\ +w# 0;>baGh!m8Ÿcux(CC ][XxJ?ph)GL&n,Bd=6{`zK$C;)dsK(%+=`4VxkXz1\,YA(&??k) mh`ȟJd]1M֍i&64Z}}."9VC RW#0JNJ@)&;Cb|KB2rZ]==ǩǞ,~iS*IqV4֠O`j6Q&]ڬH_pHgPjY ucu: <` &zz``7 ` Zp|U=4&Bf 3jMq3M~ڄPWPlTəC?:AZKBJȈr]/SDc58ϡ9;3 6O F=H0XŒ[$FpQʹ g`pqrq^ Q~ X?o6ѷV``bWO`_8` ˞ln3.NN̈́Z^"CV|2nuR?,)RWy`90V`O*OñlC9I|ZCq[O:Ϥ=dd/8=R?1.I+@qmۢ-!ˁAK=~Jlva"`"~FFG0Daz/Jb2KKcHw>: !HYV;QCq ʺ@M%&{a / XؓE~A"';^dD 6x%Cdg[M ElhSIT[MP%6g@C X3*Թ64ez-X3p``хWۊ+^&BdXSi |+@ dTyh/HY8*hTXP`,i,rj19S8~b6U,\cuiJNf%lOi5дT(.044VDx^TW}e[MC_6k&vf8-i3iKtJl >2 "ڻpV顐0"30uSmEN^nШW-(}mSKwƐ1"hIOD}i8gq;suȀ!L=GOŰdcvM x媅l2B-DUؙ>15JxMtxۺctv,m+{:^W,l"N\@o!h(M/Zܱߥ|AEf~/4w!P"DRܯWWVdbЁxao%Ce1Rk7m>z``"l"0ޖˠ[PjaDBjf=^V zE/akdD7:Ih{hO [lo(`;~Vcֺ*_#YWuSzZ ̄(ǹjF^ -MH2k /2F=< AubBvd蔈kF>[ U|^rp/7`+0{ ᇑ`P=6jgE?Ntej|@lK溏`ID^ٺ2<Χ^sLհ:7XX4aSH1D4e E߹,4s~|)7kj9d* CkC&zN%bX QZBvGS fb N&Ga(l\اwAy#J|&%h+D_e[ѧFs|{  ƬN\FLi -d~R5BB.ʛ1tS;{ګ{ ,lЌ[Q\x@êMx^[l{csFuUW,/w\$ֺ*-;kkf^V`Zmp]tڟ+ֲ݆^q] }SC̏,A!`ͧ IENDB`'ZPNG  IHDRa pHYs  gAMA|Q cHRMz%u0`:o_F}IDATxb?2x=?b! f w@LPPzb 4X?x 7qlP3 m< +.  &: AUA bY* ĝ@g44,xTP/@1{oq`#t>.A XL4`q @a$MpA" ؋  H``ƣ$7 @Au@ ?{rod ,6mR P [Ԁ DDX6O)@@97A! 4Eq(c1931)9 PA>0bĒIX)Aa5@V`cR HIENDB`-m hPNG  IHDRa pHYs  gAMA|Q cHRMz%u0`:o_FIDATxb?2x bW  @<]PPzbq ~Ӂ}qlP3?} ǁd@1A搠,x2@LP? IS4$~P@1A .q P>% P1f N  adHW$~+X% @.F#wP # &4JD8  LbN,فx+ȀMh x !R!ˣ@޽32Nb/F]=Kb=DPa 0 fϻ؆ȔX@~8kh,i #r L*|7P}dcasxIENDB`PNG  IHDRa pHYs  gAMA|Q cHRMz%u0`:o_FIDATxb?2x=?b! f w@LPPzb 4X?x 7qlP3 m< +.  &: AUA bYt @,G  @  bb 04 ~( d /'Q3(  &h~I~ X $he{ @!g&F %C#(&@X3&0ng ud@GIENDB`CPNG  IHDRa pHYs  gAMA|Q cHRMz%u0`:o_FIDATxb?2x bW  @<]PPzbq ~Ӂ}qlP3?} ǁd@1A搠,x2@޽0/4@@,@IБH=@\ @> &@1A >@`(0F,@ %vP4. &h^LSz,Ӂ A  Ќ5@ FbzO  F,YH@.T,:>zAH*IENDB`oPNG  IHDR22] pHYs  gAMA|Q cHRMz%u0`:o_FIDATxχaǿ0 ð CN! aH/驆 %JKz(Z!JO=.t%TBrH%,C4LdN氏&3w>bR|ulBTU8 …tT*e_- ÅkZxs,W`u]I6$I~X9F6g,XZ6ϝuLl۞ƭK茱V@ @8nB!e5M;'V*&A:aiX\jNjb1 \.7L'#t:m4(FQ+4aaf3HV/swzzض s쨰dYfb1Sbml6 c@@8߬i5 &.Kbp@x/( X*!4d2 ˝ z3x6ݳO<YR-zt]B#ԦR#,o:|Ҷ;o9(fm$X VvL\NrעFr tj̰hX+[U(r||7UHd5J&#i=' GQ:?kKz0/z XX:99!iE 9*{5/IENDB`CPNG  IHDRa pHYs  gAMA|Q cHRMz%u0`:o_F;IDATxb?2x2b% ;@|ZPP)zbX?[qlP3 ā@d@1A/F(S=A bYk됸@ 0zdd9'V3@ )F݈ &|$ H:f0@ I@6q(} @ç_ &hھM.A)Kʬ@X #2)2r93cš5 #,̠ L* BY=@lr|EIENDB`PNG  IHDRa pHYs  gAMA|Q cHRMz%u0`:o_FAIDATxb?2x bW  @<]PPzbq ~Ӂ}qlP3?} ǁd@1A߿0%O1ݻw ?&Z!!!zy@l@L#J32 @@ Ռi|H^9bLA h2`E8Azd\thEb2_G3L@ 6D&eP @\Ŀh+i #r L*|B}Y=@fMV VIENDB`zPNG  IHDRa pHYs  gAMA|Q cHRMz%u0`:o_F.IDATxb?2x=?b! f w@LPPzb 4X?x 7qlP3 m H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-gAMA|Q cHRMz%u0`:o_F!IDATxhW~9/Yn,s2B,qR8'N9J'"RJWJD:JRJq+""sDJ8q\\o~ޟqwx9~ޟyΓ<˲,RzԖksڙ֎=jw_O?Gma퀵Y;emiCk߳v|.;v`WRJ)l4nks~6@ S.Wm>_@ b}_n߲w>E^oח `yy.XW++l&g {KX;a!%[XvK]a{Ǝ߷B|uHv W9X,1Q&Ym[xg|=eVsK #:sǹUX(!0V_Pq$^k?.7+-2X{lCad,1̘#f )kXkZ-WmJ͘k5mjۭЖD Q\nxU0X6dm-2$ϖGEM 7Xk";v##$@mv{3nyd~Jc6D&L;e=1d-" `$Q yv2 1Pxړ{ʏ0:7Ph 6[;|(23y j'V!زܴ^aB. hb;1(  `{*BA lr*w=i̘,?[AZk 6Â:2F![8 I7-CYrQ(0`gB3 6b4WaM֚ϕ}f#\俀XD؄,"c@[OF`8#W#| /`Y @d+"_ 6c2IJD̘ {6R}IEhb(-l`^n#k `盄ȥvU08E+|,6s1 T}Kh`LBw@317+u;d/ULGnOZ`o>a4GKFK@^vY{jb=D{O١3՗TI|g|JgY3؞' G.[,vB!IT+[4@ uC1󐨬FLV:*'#AcMHY8 u*EKs3X(2ߏB%6mXe]2Kϕ&KnA. !V5gڪJh/Kdd̅[ٿt{ {bޒ0ҽoc9Ŭ0{d{%}>ژu&hzMpRW _"l WTRY }2X~Xj5U qo f \ٞ:c-T+;`/,#T*^jVajY %Zk}QeO?^䗇*Sd˾.wu(|mQkC4`$9b9z@4^}Z #*yXF0VgEV0Cl#J;vϓD>XuѨ mJ .WF-oL!Sb׌žӲZVF5Ssѐt-&3&- 7>!݅g Vނqq~!r {w!r vb\SɗұW#Zou#zZ}1tDHpd XA? n$ۢP3kvJ>k#DwgaZ"`>0W?z| /2V܌kKv!tV8P8 R~P6nF)mt1^^%hcȚ]oʹE2߄:2"]Zx|õq;-BVȿ p1j1Շq\J=5suV9\Sj!iL10O=;;]"/j*_¸E+v0>lh"/_;6oQTsópQ M] M",B[e*`jl'wV$TE pM`iϵ u:ɳ6_Z"`g X%kD4m ;!\`_K9Ka X~.p!B۲ B$9݋T"95B#U"_ Z`gXXk`^P53:6SYZ#jߋ'|i(*#ts__MhN"5asY*>k"O5Xz%:3;Xjh`0hMhMlSSX?DM!``F~-4WU6Yjx_`0ךHӠ `tymt] fe5_ dCc FG<ߌ>PD GK)`цwY`+Bd3 !:?K"Cd3 *f<{GE(\K?pZ^d@_Nkjg!ol4dLںl?*DV1؞LJHmF2K w6q'qL>wE|W7§źf/Ȯ@wDİx<}0w޿⎻ĺ56h+ڃh6lkRlC9˪ /T;HO ۇ/2l/Qف1c&"<+\ZĐ TP;f>Csn dX% $pVD~| 0Eklu*`mZDquX `|ނ~ 6A?g!hK`Vvx/O,; F*RMMBdU-x9K܃١0Pˠ20J2?Yi9;ї[n؀ԭJf?YkU`K iP"lC6jGl)u[L%[ktCi\)u86`IҮ'8 c%%WOf`IKhǾg[Q{y6[F?S5PuWqtQ|i %;Yz y?2j5h3l X k P;}hJ֗`u @,,p0]_+Q*k פC+ՌF`r eYvq0Q[:aIAnfSˬAG:PD[ǷK=a~]|iFwVꩽLnՂBUc|'ؙVߟD[ րAm|v6: Wm R1~nXZ9=/&'ء5Qٍ՟AHѾd `IZvaNQQgo 0Zt1ڱ82u0Ƃ14;#84(8]c*~C]i6 P\Zvc 66 gzd{%ERmX m؜Q-7lY_ &VW6:Rvi;Yf4B" ^G~$O*zdrG zZq>Olҝt/.T;jҀʱ7C(+j<at(J"ާkThIeȘ]oV74:9[}"|v|hILh+)#QdyKTzz+D 9afGZgYףHw]n@lOQJh#뗆@uB{PCrz!YIP@HĦ((kPVA Pюm~Ҍb75E\Iu`.CmU-0Eno+U7LQ#8!du-QQJPBei ȓ]G:5K"t_EhHolwFbvf(撿dZFG9I}[0v@Z-!_F4b*BْĀѸ ?8>S+6 E٤r8&?ʖDmNs?dUՋp fwbǹJd XItpT!f-7 BXT(-)U:]`πqu{|?E~Cpun9>7hEOLҔCU C0p/>kbYbM,.' 0֠[M. E'hih'UCRY71l>!Qz Le-k#&b|wwրNIoEhu2NTOоxkCYFSX,FÒF+"rS>Eu228 h1ժ}16=ad@1P4 ߏYCh"]H^12eQ{Oq^|3Eƽ/ZnOY/YOh`9#{8(gZ9^c"Ca@_@- K[,m{L˿&h}3j"go ϻ1dzfShW@kDP|)ff$Q#޸Zs6fX*{DBMRD#C&81lOCoS.f @l13Javv\щ>TvI٨Z^ .vW@IA]+C4̦$8-SS!XC n=l$Yű>h@)f'J!2t/AdY̾1מ]D[m~^mY3Y X PԷ쎀h396{u^)7E^Hn-B@ jh_*;<P˱v鵷Wl4CrCMt4r%U|!T Ckow ][YZHbV2LJ^Ei˗";-UDgc_jm`qTKo5_K)M;;cM0)4:ỶhKL"?i0[:)u$ X z 7RT!a<-ׇ &Ү67pbT%x_ 92߭Ndo"$( >tpk>s52!T>ɏ>m)p>j_-k=zvI(?UXK3h7 S 5=fb4mey;G`)lh޿3?44gq$vu>!:z9VSY$BWod4gkPP7#?@+|:P<-[L5Vlh[@_sPj:\Ÿy᳉1ㄓQX[S7~;qԫ(q]k3dz=@ V @ Y3ʟ)֙fa,xֆI|>Źf7m6ZPSaLW8 wcHP(U]QK))vL d+x:$aDmC?@XpTB/CDvE{aV?7pM>ǙzX##/.iZK L`鳲AYdl{у3 | Sb@xm'QN`tE"tڠއXT%[P@*ّz U0b,OTp#+TeHZ+b{  QzX8۳g8b{@~|~p0Fz&̲H i =$jq' ]@뿔5 t4W su)Obͭ8RCv!K CBhWRD-(#vG~R/UՅ8j؝)S\~<A!(f8f|8@TMƨlc(]tqJYkZ lv ~w<+-`Qc)ɯk54ο,[ɟbZKQMZk!J7(i)vQVJQf 2|V϶C,X*CFf" ~|:V|Td %/.WG9px>E06ϓAXȐisj|ޭ*jꄓ> 11@Z>ƥ﮲&^n)|hSeHc2N87PS%bԞڐE^ p ޖXpߵN qOA{5TsPd{R *֒/#+̼jAEvBх dó%` O؇wQzvYFmU? X Y@}X< RG'DnlO+AcqThkϙgy$yPxL(>: my΢Br3qɏt-63-!ۊ:_o,ˍHwzx3 deMqt֕ao~ 1T$.xc`愧y`3AJ^F_COn'KS…oJ1?M#a+ϯF)QRY :we+fYނQӍǿ?~zjU6 p̀7AEK@ދLb%mUo{oS_߿7?sUʞh2>!0y<56 M]zuT]|פMUJ.rIENDB`=NPNG  IHDR2h pHYs  gAMA|Q cHRMz%u0`:o_F IDATxb?(`hQ0 h`dd&-`4̀DQ0 ?DH4bh Xp*<Y*?ZQ0  7b Xpt@s1'sAi6Bf3 Fj _@AP  b~ |H hWi5T|@J3@#dKbb1NN SHuAA [ `(`_8}z9Zn  F` `\XAF XT ( i;) 9@Vr*aء! V;YSv4(G(mޜ d>ҏЂ HP>h`'JMLCat!(jjozς B/``ҜZ Q0 F^) 3`N1A^VpWfQ@p23 2@WX BoV2a`2 :0^;z`B% V"x!@ 0E` bZ@=(`?#zĂ0E`P҂ < ( SN Ri3 F(@,xU`"Jܫh4 F(Y 0` -"Q0 hVhiQ@.@vF(4kߟ? ~ Q0 Forjh荂Q0 0 f4Q0 F>N)[Q0 /x `Ь@xgFG(04qQ@.@Q0 hV(f]$?3E`x"kŒ`F(D01( \-gQ0 !i 5`F( nP@,X e3 F( [" 8  HhO`#h荂Q0 в/r!@-PEw͆1`4F(/ P3tF×Mad|, ;`l-c`YqTf@[1( @ak}7s`bc` Q0 F2*#2/`v#dـX\A.1Rؚ Q0 F|} 3_P%b$X@ #U*d4! c`H=PҀaW+W:! Pw…Z00`VA*dA XxA E^,2'tb,Q0 Fv+6 . @]$ y fB f׊A_[+Wޣ`b Q0 F@:-IENDB`YS9,/* * BBC Radio Player + Last.fm * * chris.bowley@bbc.co.uk * August 2006 * */ function Ajax() { this.APOS = "'"; this.QUOTE = '"'; this.ESCAPED_QUOTE = { }; this.ESCAPED_QUOTE[this.QUOTE] = '"'; this.ESCAPED_QUOTE[this.APOS] = '''; this.xmlhttp_request = this.xmlhttpArray(); this.xmlhttp_post; } Ajax.prototype.xmlhttpArray = function() { var arr = new Array(); for (var i=0; i<20; i++) { arr.push({callback: null, xmlhttp: null, func: null}); } return arr; }; Ajax.prototype.allocateXmlhttp = function() { for (var i=0; i'; } else { xml='<' + name + att_str + '>' + content + ''; } return xml }; Ajax.prototype.formatAttributes = function(attributes) { var att_value; var apos_pos, quot_pos; var use_quote, escape, quote_to_escape; var att_str; var re; var result = ''; for (var att in attributes) { att_value = attributes[att]; // Find first quote marks if any apos_pos = att_value.indexOf(this.APOS); quot_pos = att_value.indexOf(this.QUOTE); // Determine which quote type to use around // the attribute value if (apos_pos == -1 && quot_pos == -1) { att_str = ' ' + att + "='" + att_value + "'"; result += att_str; continue; } // Prefer the single quote unless forced to use double if (quot_pos != -1 && quot_pos < apos_pos) { use_quote = this.APOS; } else { use_quote = this.QUOTE; } // Figure out which kind of quote to escape // Use nice dictionary instead of yucky if-else nests escape = this.ESCAPED_QUOTE[use_quote]; // Escape only the right kind of quote re = new RegExp(use_quote,'g'); att_str = ' ' + att + '=' + use_quote + att_value.replace(re, escape) + use_quote; result += att_str } return result; }; ,c+'X/* * BBC Radio Player + Last.fm * * chris.bowley@bbc.co.uk * August 2006 * */ function AudioScrobbler() { this.request_api_root = 'http://ws.audioscrobbler.com/1.0/'; this.post_api_root = 'http://post.audioscrobbler.com/?'; this.post_api_url; this.md5_challange; this.md5_password; this.md5; this.username; this.password; //this.realname; //this.user_image; this.avatar; this.networkImage; this.handshakeTimer = new Timer(); this.handshakeTimer.interval = 15; this.handshakeTimer.onTimerFired = function() {print("*** handshakeTimer fired"); audioScrobbler.handshake()}; this.handshakeTimer.ticking = false; this.submit_tracks_failed = false; this.signed_in = false; // timer objects for each operation for check for timeouts this.submitTracksTimer = new Timer(); this.submitTracksTimer.interval = 15; this.submitTracksTimer.onTimerFired = function() {print('*** submitTracksTimer fired'); ajax.releaseXmlhttp('submitTracks');}; this.submitTracksTimer.ticking = false; this.artistInfoTimer = new Timer(); this.artistInfoTimer.interval = 15; this.artistInfoTimer.onTimerFired = function() {print('*** artistInfoTimer fired'); audioScrobbler.artistInfo();}; this.artistInfoTimer.ticking = false; this.nowPlayingTimer = new Timer(); this.nowPlayingTimer.interval = 15; this.nowPlayingTimer.onTimerFired = function() {print('*** nowPlayingTimer fired'); ajax.releaseXmlhttp('nowPlaying');}; this.nowPlayingTimer.ticking = false; this.networkProfileTimer = new Timer(); this.networkProfileTimer.interval = 15; this.networkProfileTimer.onTimerFired = function() {print("*** networkProfileTimer fired"); audioScrobbler.networkProfile();}; this.networkProfileTimer.ticking = false; this.userProfileTimer = new Timer(); this.userProfileTimer.interval = 15; this.userProfileTimer.onTimerFired = function() {print("*** userProfileTimer fired"); audioScrobbler.userProfile();}; this.userProfileTimer.ticking = false; } AudioScrobbler.prototype.init = function(username,password) { this.username = username; this.password = password; this.md5_password = hex_md5(password); }; AudioScrobbler.prototype.handshake = function() { print("AudioScrobbler.handshake"); ajax.releaseXmlhttp('handshake'); ajax.post(this.post_api_root,'handshake',this.handshakeCallback,'hs=true&p=1.1&c=tst&v=1.0&u='+this.username); this.handshakeTimer.interval = 15; this.handshakeTimer.ticking = true; this.handshakeTimer.reset(); }; AudioScrobbler.prototype.handshakeCallback = function() { var xmlhttp = ajax.getXmlhttp('handshake'); if (xmlhttp.readyState != 4) return; if (xmlhttp.status != 200) return; if (!xmlhttp.responseText) return; print(xmlhttp.responseText); var response = escape(xmlhttp.responseText); var params = response.split('%0A'); audioScrobbler.handshakeTimer.ticking = false; if (unescape(params[0]).indexOf('UPTODATE') != -1) { audioScrobbler.md5_challange = unescape(params[1]); audioScrobbler.md5 = hex_md5(audioScrobbler.md5_password+audioScrobbler.md5_challange); audioScrobbler.post_api_url = unescape(params[2]); audioScrobbler.signed_in = true; state = 'signed_in'; if (audioScrobbler.submit_tracks_failed) { recommendationManager.submitTracks(); } else { audioScrobbler.userProfile(); } } else { var timeout = parseInt(unescape(params[3]).split(' ')[1],10); print("handshake timeout, trying again in " + timeout); audioScrobbler.handshakeTimer.interval = timeout; audioScrobbler.handshakeTimer.ticking = true; audioScrobbler.handshakeTimer.reset(); } ajax.releaseXmlhttp('handshake'); // debug //audioScrobbler.userProfile(); }; AudioScrobbler.prototype.networkProfile = function() { ajax.releaseXmlhttp('networkProfile'); ajax.post(this.request_api_root,'networkProfile',this.networkProfileCallback,'user/'+recommendationManager.networkId+'/profile.xml'); this.networkProfileTimer.ticking = true; this.networkProfileTimer.reset(); }; AudioScrobbler.prototype.networkProfileCallback = function() { var xmlhttp = ajax.getXmlhttp('networkProfile'); if (!xmlhttp) return; if (xmlhttp.readyState != 4) return; if (xmlhttp.status != 200) return; if (!xmlhttp.responseXML) return; var response = xmlhttp.responseXML; //audioScrobbler.realname = response.evaluate('string(profile/realname)'); audioScrobbler.networkImage = response.evaluate('string(profile/avatar)'); audioScrobbler.networkProfileTimer.ticking = false; ajax.releaseXmlhttp('networkProfile'); //presentation.displayUserInfo(); //recommendationManager.getProgramme(); presentation.displayLogo(audioScrobbler.networkImage); }; AudioScrobbler.prototype.userProfile = function() { ajax.releaseXmlhttp('userProfile'); ajax.post(this.request_api_root,'userProfile',this.userProfileCallback,'user/'+this.username+'/profile.xml'); this.userProfileTimer.ticking = true; this.userProfileTimer.reset(); }; AudioScrobbler.prototype.userProfileCallback = function() { var xmlhttp = ajax.getXmlhttp('userProfile'); if (!xmlhttp) return; if (xmlhttp.readyState != 4) return; if (xmlhttp.status != 200) return; if (!xmlhttp.responseXML) return; var response = xmlhttp.responseXML; audioScrobbler.realname = response.evaluate('string(profile/realname)'); audioScrobbler.avatar = response.evaluate('string(profile/avatar)'); audioScrobbler.userProfileTimer.ticking = false; ajax.releaseXmlhttp('userProfile'); presentation.displayUserInfo(); recommendationManager.getProgramme(); }; AudioScrobbler.prototype.nowPlaying = function(username) { print("AudioScrobbler.nowPlaying:" + username); ajax.post(this.request_api_root,'nowPlaying',this.nowPlayingCallback,'user/'+username+'/recenttracks.xml'); this.nowPlayingTimer.ticking = true; this.nowPlayingTimer.reset(); }; AudioScrobbler.prototype.nowPlayingCallback = function() { print("nowPlayingCallback"); var xmlhttp = ajax.getXmlhttp('nowPlaying'); if (!xmlhttp) return; if (xmlhttp.readyState != 4) return; if (xmlhttp.status != 200) return; if (!xmlhttp.responseXML) return; //print(xmlhttp.responseText); var response = xmlhttp.responseXML; var tracks = response.evaluate('recenttracks/track'); var track = tracks.item(0); var artist = String(track.evaluate('artist').item(0).firstChild.data); var title = String(track.evaluate('name').item(0).firstChild.data); var trackUrl = String(track.evaluate('url').item(0).firstChild.data); var date = audioScrobbler.formatDate(String(track.evaluate('date').item(0).getAttribute('uts'))); var utc = parseInt(track.evaluate('date').item(0).getAttribute('uts'),10) * 1000; var artistUrl = trackUrl.substring(0, trackUrl.lastIndexOf('_')); var track = { artist: artist, title: title, trackUrl: trackUrl, artistUrl: artistUrl, date: date, utc: utc }; audioScrobbler.nowPlayingTimer.ticking = false; ajax.releaseXmlhttp('nowPlaying'); recommendationManager.nowPlayingSuccessful(track); }; AudioScrobbler.prototype.artistInfo = function(artist) { print('Fetching info for artist '+artist); ajax.post(this.request_api_root,'artistInfo',this.artistInfoCallback,'artist/'+artist+'/similar.xml'); this.artistInfoTimer.ticking = true; this.artistInfoTimer.reset(); }; AudioScrobbler.prototype.artistInfoCallback = function() { var xmlhttp = ajax.getXmlhttp('artistInfo'); if (!xmlhttp) return; if (xmlhttp.readyState != 4) return; if (xmlhttp.status != 200) return; if (!xmlhttp.responseXML) { ajax.releaseXmlhttp('artistInfo'); var artists = new Array(); recommendationManager.nowPlayingArtistInfo({image: 'Resources/no_artist.png', artists: artists}); } else { var response = xmlhttp.responseXML; var image = response.evaluate('similarartists').item(0).getAttribute('picture'); if (image.indexOf('.')==-1) { ajax.releaseXmlhttp('artistInfo'); var artists = new Array(); recommendationManager.nowPlayingArtistInfo({image: 'Resources/no_artist.png', artists: artists}); } else { var artists = new Array(); var artist = new Object(); artist.image = image; var similarArtists = response.evaluate('similarartists/artist'); var numArtists = similarArtists.length; if (numArtists > 3) numArtists = 3; for (var i=0; i 16) bkey = binl_md5(bkey, key.length * 8); var ipad = Array(16), opad = Array(16); for(var i = 0; i < 16; i++) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5C5C5C5C; } var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8); return binl2rstr(binl_md5(opad.concat(hash), 512 + 128)); } /* * Convert a raw string to a hex string */ function rstr2hex(input) { var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; var output = ""; var x; for(var i = 0; i < input.length; i++) { x = input.charCodeAt(i); output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt( x & 0x0F); } return output; } /* * Convert a raw string to a base-64 string */ function rstr2b64(input) { var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var output = ""; var len = input.length; for(var i = 0; i < len; i += 3) { var triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i+2) : 0); for(var j = 0; j < 4; j++) { if(i * 8 + j * 6 > input.length * 8) output += b64pad; else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F); } } return output; } /* * Convert a raw string to an arbitrary string encoding */ function rstr2any(input, encoding) { var divisor = encoding.length; var i, j, q, x, quotient; /* Convert to an array of 16-bit big-endian values, forming the dividend */ var dividend = Array(Math.ceil(input.length / 2)); for(i = 0; i < dividend.length; i++) { dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1); } /* * Repeatedly perform a long division. The binary array forms the dividend, * the length of the encoding is the divisor. Once computed, the quotient * forms the dividend for the next step. All remainders are stored for later * use. */ var full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2))); var remainders = Array(full_length); for(j = 0; j < full_length; j++) { quotient = Array(); x = 0; for(i = 0; i < dividend.length; i++) { x = (x << 16) + dividend[i]; q = Math.floor(x / divisor); x -= q * divisor; if(quotient.length > 0 || q > 0) quotient[quotient.length] = q; } remainders[j] = x; dividend = quotient; } /* Convert the remainders to the output string */ var output = ""; for(i = remainders.length - 1; i >= 0; i--) output += encoding.charAt(remainders[i]); return output; } /* * Encode a string as utf-8. * For efficiency, this assumes the input is valid utf-16. */ function str2rstr_utf8(input) { var output = ""; var i = -1; var x, y; while(++i < input.length) { /* Decode utf-16 surrogate pairs */ x = input.charCodeAt(i); y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0; if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) { x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF); i++; } /* Encode output as utf-8 */ if(x <= 0x7F) output += String.fromCharCode(x); else if(x <= 0x7FF) output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F), 0x80 | ( x & 0x3F)); else if(x <= 0xFFFF) output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), 0x80 | ((x >>> 6 ) & 0x3F), 0x80 | ( x & 0x3F)); else if(x <= 0x1FFFFF) output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), 0x80 | ((x >>> 12) & 0x3F), 0x80 | ((x >>> 6 ) & 0x3F), 0x80 | ( x & 0x3F)); } return output; } /* * Encode a string as utf-16 */ function str2rstr_utf16le(input) { var output = ""; for(var i = 0; i < input.length; i++) output += String.fromCharCode( input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF); return output; } function str2rstr_utf16be(input) { var output = ""; for(var i = 0; i < input.length; i++) output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF); return output; } /* * Convert a raw string to an array of little-endian words * Characters >255 have their high-byte silently ignored. */ function rstr2binl(input) { var output = Array(input.length >> 2); for(var i = 0; i < output.length; i++) output[i] = 0; for(var i = 0; i < input.length * 8; i += 8) output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32); return output; } /* * Convert an array of little-endian words to a string */ function binl2rstr(input) { var output = ""; for(var i = 0; i < input.length * 32; i += 8) output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF); return output; } /* * Calculate the MD5 of an array of little-endian words, and a bit length. */ function binl_md5(x, len) { /* append padding */ x[len >> 5] |= 0x80 << ((len) % 32); x[(((len + 64) >>> 9) << 4) + 14] = len; var a = 1732584193; var b = -271733879; var c = -1732584194; var d = 271733878; for(var i = 0; i < x.length; i += 16) { var olda = a; var oldb = b; var oldc = c; var oldd = d; a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936); d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586); c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819); b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897); d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426); c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341); b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983); a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416); d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417); c = md5_ff(c, d, a, b, x[i+10], 17, -42063); b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162); a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682); d = md5_ff(d, a, b, c, x[i+13], 12, -40341101); c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290); b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329); a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510); d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632); c = md5_gg(c, d, a, b, x[i+11], 14, 643717713); b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302); a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691); d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083); c = md5_gg(c, d, a, b, x[i+15], 14, -660478335); b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438); d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690); c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961); b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501); a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467); d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784); c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473); b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734); a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558); d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562); b = md5_hh(b, c, d, a, x[i+14], 23, -35309556); a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060); d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353); c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632); b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640); a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174); d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222); c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979); b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487); d = md5_hh(d, a, b, c, x[i+12], 11, -421815835); c = md5_hh(c, d, a, b, x[i+15], 16, 530742520); b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651); a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844); d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415); c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905); b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055); a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571); d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); c = md5_ii(c, d, a, b, x[i+10], 15, -1051523); b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799); a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359); d = md5_ii(d, a, b, c, x[i+15], 10, -30611744); c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380); b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649); a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070); d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379); c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259); b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); a = safe_add(a, olda); b = safe_add(b, oldb); c = safe_add(c, oldc); d = safe_add(d, oldd); } return Array(a, b, c, d); } /* * These functions implement the four basic operations the algorithm uses. */ function md5_cmn(q, a, b, x, s, t) { return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b); } function md5_ff(a, b, c, d, x, s, t) { return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); } function md5_gg(a, b, c, d, x, s, t) { return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); } function md5_hh(a, b, c, d, x, s, t) { return md5_cmn(b ^ c ^ d, a, b, x, s, t); } function md5_ii(a, b, c, d, x, s, t) { return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); } /* * Add integers, wrapping at 2^32. This uses 16-bit operations internally * to work around bugs in some JS interpreters. */ function safe_add(x, y) { var lsw = (x & 0xFFFF) + (y & 0xFFFF); var msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xFFFF); } /* * Bitwise rotate a 32-bit number to the left. */ function bit_rol(num, cnt) { return (num << cnt) | (num >>> (32 - cnt)); } DC'X/* * BBC Radio Player + Last.fm * * chris.bowley@bbc.co.uk * August 2006 * */ function Presentation () { this.track_font_size = 12; this.std_font_color = '#cccccc'; this.abs_tl_x = 4; this.abs_tl_y = 54; this.abs_br_x = 326; this.abs_br_y = 156; this.dropShadow = this.createDropShadow('#000000'); statusTimer = new Timer(); statusTimer.interval = 5.0; statusTimer.ticking = false; statusTimer.onTimerFired = this.hideMessage; this.initDisplayElements(); this.createImages(); this.trackOutOfDate = false; } Presentation.prototype.initDisplayElements = function() { username_required_txt = new Text(); signing_in_txt = new Text(); signing_in_2_txt = new Text(); retrieving_now_playing_txt = new Text(); artist_frame_img = new Image(); artist_img = new Image(); artist_shine_img = new Image(); track_txt = new Text(); artist_txt = new Text(); try_txt = new Text(); similarArtists = null; warning_img = new Image(); warning_txt = new Text(); question_txt = new Text(); ok_img = new Image(); cancel_img = new Image(); }; Presentation.prototype.createDropShadow = function(clr) { var s = new Shadow(); s.hOffset = 1; s.vOffset = 1; s.color = clr; s.opacity = 128; return s; }; Presentation.prototype.createImages = function() { user_img = new Image(); user_img.window = mainWindow; user_cover_img = new Image(); user_cover_img.window = mainWindow; user_cover_img.src = 'Resources/filter.png'; user_cover_img.hOffset = this.abs_br_x-75; user_cover_img.vOffset = this.abs_tl_y; user_cover_img.width = user_cover_img.height = 75; user_cover_img.opacity = 115; profile_crop_img = new Image(); profile_crop_img.window = mainWindow; profile_crop_img.opacity = 255; profile_crop_img.src = 'Resources/profile_image_crop.png'; profile_crop_img.hOffset = this.abs_br_x-75; profile_crop_img.vOffset = this.abs_tl_y; logo_img = new Image(); logo_img.window = mainWindow; logo_img.opacity = 100; logo_img.hOffset = 4; logo_img.vOffset = this.abs_br_y-75; logo_crop_img = new Image(); logo_crop_img.window = mainWindow; logo_crop_img.opacity = 255; logo_crop_img.src = 'Resources/logo_image_crop.png'; logo_crop_img.hOffset = this.abs_tl_x; logo_crop_img.vOffset = this.abs_br_y-75; opt_img = new Image(); opt_img.window = mainWindow; opt_img.vOffset = this.abs_tl_y+40; opt_img.hOffset = this.abs_tl_x+70; status_txt = new Text(); status_txt.window = mainWindow; status_txt.vOffset = opt_img.vOffset + 12; status_txt.hOffset = opt_img.hOffset + 25; status_txt.size = 12; bg_shine_img = new Image(); bg_shine_img.window = mainWindow; bg_shine_img.src = 'Resources/bg_shine.png'; bg_shine_img.opacity = 150; bg_shine_img.vOffset = this.abs_tl_y-4; bg_shine_img.hOffset = 0; programme_txt = new Text(); programme_txt.window = mainWindow; programme_txt.vOffset = this.abs_br_y-8; programme_txt.hOffset = this.abs_tl_x+70; programme_txt.color = '#aaaaaa'; programme_txt.size = this.track_font_size-2; programme_txt.shadow = this.dropShadow; programme_txt.width = 230; //programme_txt.style = 'bold'; programme_txt.scrolling = 'autoLeft'; }; Presentation.prototype.displayUsernameRequiredText = function() { username_required_txt = new Text(); username_required_txt.window = mainWindow; username_required_txt.vOffset = this.abs_tl_y+50; username_required_txt.hOffset = this.abs_tl_x+10; username_required_txt.color = '#ffffff'; username_required_txt.size = 12; username_required_txt.shadow = this.dropShadow; username_required_txt.data = 'Please enter your Last.fm login details'; // update the display updateNow(); showWidgetPreferences(); }; Presentation.prototype.displaySigningInText = function() { signing_in_txt = new Text(); signing_in_2_txt = new Text(); signing_in_txt.window = mainWindow; signing_in_txt.vOffset = this.abs_tl_y+40; signing_in_txt.hOffset = this.abs_tl_x+70; signing_in_txt.color = '#ffffff'; signing_in_txt.size = 12; signing_in_txt.shadow = this.dropShadow; signing_in_txt.data = 'Signing in to Last.fm'; signing_in_2_txt.window = mainWindow; signing_in_2_txt.vOffset = this.abs_tl_y+55; signing_in_2_txt.hOffset = this.abs_tl_x+70; signing_in_2_txt.color = '#ffffff'; signing_in_2_txt.size = 12; signing_in_2_txt.shadow = this.dropShadow; signing_in_2_txt.data = 'This should just take a moment...'; // update the display updateNow(); }; Presentation.prototype.displayUserInfo = function() { mainWindow.removeChild(signing_in_txt); mainWindow.removeChild(signing_in_2_txt); delete signing_in_txt; delete signing_in_2_txt; // profile image user_img.src = audioScrobbler.avatar; img_width_ratio = user_img.srcWidth / 75; img_height_ratio = user_img.srcHeight / 75; img_ratio = (img_width_ratio75) user_height = 75; user_img.vOffset = this.abs_tl_y; user_img.hOffset = this.abs_br_x - user_width; user_img.width = user_width; user_img.height = user_height; this.displayRetrievingNowPlayingText(); // update the display updateNow(); }; Presentation.prototype.displayRetrievingNowPlayingText = function() { retrieving_now_playing_txt = new Text(); retrieving_now_playing_txt.window = mainWindow; retrieving_now_playing_txt.vOffset = this.abs_tl_y+50; retrieving_now_playing_txt.hOffset = this.abs_tl_x+70; retrieving_now_playing_txt.color = '#ffffff'; retrieving_now_playing_txt.size = 12; retrieving_now_playing_txt.shadow = this.dropShadow; retrieving_now_playing_txt.data = 'Retrieving now playing information...'; }; Presentation.prototype.displayLogo = function(image) { logo_img.src = image; img_width_ratio = logo_img.srcWidth / 75; img_height_ratio = logo_img.srcHeight / 75; img_ratio = (img_width_ratio75) logo_height = 75; if (logo_width > 75) logo_width = 75; //logo_img.vOffset = this.abs_tl_y; //logo_img.hOffset = this.abs_br_x - user_width; logo_img.width = logo_width; logo_img.height = logo_height; }; Presentation.prototype.displayOptionButton = function() { print("displayOptionButton preferences.opt: " + preferences.opt.value); if (preferences.opt.value == 'Automatic') { this.displayRemoveTrackControl(); } else { this.displayAddTrackControl(); } print("displayOptionButton opt_img: " + opt_img.window + ' ' + opt_img.src); print("displayOptionButton state: " + state); if (state == 'now_playing') { if (track_txt.data.indexOf("Track information is out of date") == -1) opt_img.visible = true; } // update the display updateNow(); }; Presentation.prototype.displayNowPlaying = function(track) { print('displayNowPlaying'); if (state == 'now_playing') this.hideNowPlaying(); if (retrieving_now_playing_txt) { mainWindow.removeChild(retrieving_now_playing_txt); retrieving_now_playing_txt = null; } // track name track_txt = new Text(); track_txt.window = mainWindow; track_txt.vOffset = this.abs_tl_y+20; track_txt.hOffset = this.abs_tl_x+70; track_txt.color = '#ffffff'; track_txt.size = this.track_font_size; track_txt.shadow = this.dropShadow; //track_txt.width = 200; track_txt.style = 'bold'; //track_txt.scrolling = 'autoLeft'; track_txt.data = track.title; // artist artist_txt = new Text(); artist_txt.window = mainWindow; artist_txt.vOffset = track_txt.vOffset+track_txt.height; artist_txt.hOffset = track_txt.hOffset; artist_txt.color = '#dddddd'; artist_txt.size = this.track_font_size; artist_txt.shadow = this.dropShadow; //artist_txt.width = 200; //artist_txt.scrolling = 'autoLeft'; artist_txt.data = track.artist; if (track.title.indexOf("Track information is out of date") != -1) { this.trackOutOfDate = true; track_txt.color = '#ff0000'; artist_txt.color = '#ff0000'; } else { this.trackOutOfDate = false; // artist frame artist_frame_img = new Image(); artist_frame_img.window = mainWindow; artist_frame_img.src = 'Resources/frame.png'; artist_frame_img.opacity = 50; // artist image artist_img = new Image(); artist_img.window = mainWindow; //artist_img.remoteAsync = true; artist_img.onImageLoaded = function() {print("Loaded artist_img");}; artist_img.src = track.image; //artist_img.hOffset = artist_img.vOffset = 100; img_width_ratio = artist_img.width / 50; img_height_ratio = artist_img.height / 75; img_ratio = (img_width_ratio 50 || artist_height > 75) { artist_width = artist_img.srcWidth / alt_img_ratio; artist_height = artist_img.srcHeight / alt_img_ratio; } artist_img.vOffset = this.abs_tl_y+10; artist_img.hOffset = this.abs_tl_x+60-artist_width; artist_img.width = artist_width; artist_img.height = artist_height; artist_img.onMouseDown = function() {openURL(recommendationManager.nowPlaying.artistUrl)}; artist_img.tooltip = track.artistUrl; artist_frame_img.height = artist_img.height+4; artist_frame_img.width = artist_img.width+4; artist_frame_img.vOffset = artist_img.vOffset-2; artist_frame_img.hOffset = artist_img.hOffset-2; // artist shine artist_shine_img = new Image(); artist_shine_img.window = mainWindow; artist_shine_img.src = 'Resources/artist_shine.png'; artist_shine_img.height = artist_img.height; artist_shine_img.width = artist_img.width; artist_shine_img.vOffset = artist_img.vOffset; artist_shine_img.hOffset = artist_img.hOffset; track_txt.onMouseDown = function() {openURL(recommendationManager.nowPlaying.trackUrl)}; track_txt.tooltip = track.trackUrl if (track_txt.width+track_txt.hOffset > this.abs_br_x) { track_txt.width = this.abs_br_x - track_txt.hOffset - 20; track_txt.scrolling = 'autoLeft'; } else { track_txt.scrolling = 'off'; } artist_txt.onMouseDown = function() {openURL(recommendationManager.nowPlaying.artistUrl)}; artist_txt.tooltip = track.artistUrl; if (artist_txt.width+artist_txt.hOffset > this.abs_br_x) { artist_txt.width = this.abs_br_x - artist_txt.hOffset - 20; artist_txt.scrolling = 'autoLeft'; } else { artist_txt.scrolling = 'off'; } // similar artists try_txt = new Text(); try_txt.window = mainWindow; try_txt.vOffset = artist_txt.vOffset+35; try_txt.hOffset = track_txt.hOffset; try_txt.color = '#ffffff'; try_txt.size = this.track_font_size-2; try_txt.shadow = this.dropShadow; try_txt.data = 'Like this? Try'; var artists = track.similarArtists; var x = try_txt.hOffset+try_txt.width+5; similarArtists = new Array(); if (artists.length == 0) { artists.push({name: '[No matches]'}); } for (var i=0; i=mainWindow.width) { mainWindow.removeChild(similarArtists[i].text); similarArtists.pop(); } } opt_img.visible = true; } // update the display updateNow(); }; Presentation.prototype.hideNowPlaying = function() { try { if (artist_img != null) mainWindow.removeChild(artist_img); if (artist_frame_img) mainWindow.removeChild(artist_frame_img); if (artist_shine_img) mainWindow.removeChild(artist_shine_img); if (artist_txt) mainWindow.removeChild(artist_txt); if (track_txt) mainWindow.removeChild(track_txt); if (try_txt) mainWindow.removeChild(try_txt); } catch (e) { print("hideNowPlaying error"); } /* try { if (programme_txt) mainWindow.removeChild(programme_txt); } catch (e) { print("hideNowPlaying error"); } */ try { if (similarArtists) { for (var i=0; i 100) this.volume = 100; else if (this.volume < 0) this.volume = 0; if (this.playing) document.getElementById("realplayer").SetVolume(this.volume); presentation.displayStatusMessage("Volume: " + this.volume + "%"); }; RadioPlayer.prototype.play = function() { // set HTML element //this.radioElement.html = ''; this.radioElement.html = 'HELLO!'; print("play: " + this.radioElement.html); // refresh volume this.changeVolume(0); // update state this.playing = true; }; RadioPlayer.prototype.pause = function() { // remove radio html element this.radioElement.innerHTML = null; // update state this.playing = false; }; function OnPlayStateChange(oldState,newState) { //alert('OnPlayStateChange ' + oldState +' ' + newState); } function OnBuffering(flag,percent) { //alert('OnBuffering: ' + flag + ' ' + percent); presentation.displayStatusMessage("Buffering: " + percent + "%"); }q/* * BBC Radio Player + Last.fm * * chris.bowley@bbc.co.uk * August 2006 * */ function RadioPlayerMac(stream) { this.stream = stream; this.player; this.createPlayer(); } RadioPlayerMac.prototype.createPlayer = function() { var script = 'tell application "RealPlayer"\n'; script += 'open clip with URL "' + this.stream + '"\n'; script += 'set res to number of players as string\n'; script += 'end tell\n'; script += 'get res\n'; var e = appleScript(script, 10); if (e.indexOf("AppleScript Error") >= 0) { print( "Unable to open RealPlayer: " + e ); } }; RadioPlayerMac.prototype.play = function() { var script = 'tell application "RealPlayer"\n'; script += 'play player ' + this.player + '\n'; script += 'set res to ""\n'; script += 'set res to res & state of player ' + Player + '\n'; script += 'end tell\n'; script += 'get res\n'; var e = appleScript(script, 10); print('Error playing player: '+e); }; RadioPlayerMac.prototype.pause = function() { var script = 'tell application "RealPlayer"\n'; script += 'pause player ' + this.player + '\n'; script += 'set res to ""\n'; script += 'set res to res & state of player ' + Player + '\n'; script += 'end tell\n'; script += 'get res\n'; var e = appleScript(script, 10); print('Error pausing player: '+e); }; RadioPlayerMac.prototype.stop = function() { var script = 'tell application "RealPlayer"\n'; script += 'stop player ' + this.player + '\n'; script += 'set res to ""\n'; script += 'set res to res & state of player ' + Player + '\n'; script += 'end tell\n'; script += 'get res\n'; var e = appleScript(script, 10); print('Error stopping player: '+e); }; RadioPlayerMac.prototype.closePlayer = function() { var script = 'tell application "RealPlayer" to quit\n'; var e = appleScript(script, 10); print('Error closing player: '+e); };p/* * BBC Radio Player + Last.fm * * chris.bowley@bbc.co.uk * August 2006 * */ function RadioPlayerWin() { this.stream; this.streamAlt; this.player; //this.createPlayer(); this.altPlayer = false; } RadioPlayerWin.prototype.createPlayer = function(stream, streamAlt) { this.stream = stream; this.streamAlt = streamAlt; try { if ( !this.player ) { this.player = COM.createObject('rmocx.RealPlayer G2 Control'); //this.player = COM.createObject('RealPlayer'); COM.connectObject( this.player, "RealPlay" ); } print('Realplayer stream: ' + this.stream); this.player.SetSource( this.stream ); this.player.DoPlay(); //openURL(this.stream); } catch(e) { print('Error creating player: '+e); // first see if we are already using alt player if (this.altPlayer) { presentation.displayWarning('Please close Radio Player in your browser,', preferences.network.value + ' will open in a new window.', this.createAltPlayer, presentation.hideWarning); } else { if (preferences.useAltPlayer.value == 'Always') this.createAltPlayer(); else if (preferences.useAltPlayer.value == 'Ask me') presentation.displayWarning('Cannot start embedded Real Player.', 'Open Radio Player in your browser?', this.createAltPlayer, presentation.hideWarning); } } }; RadioPlayerWin.prototype.createAltPlayer = function() { radioPlayer.altPlayer = true; openURL(radioPlayer.streamAlt); presentation.hideWarning(); }; RadioPlayerWin.prototype.play = function() { try { this.player.DoPlay(); } catch (e) { print('Error playing player: '+e); } }; RadioPlayerWin.prototype.pause = function() { try { this.player.DoPause(); } catch (e) { print('Error pausing player: '+e); } }; RadioPlayerWin.prototype.stop = function() { try { this.player.DoStop(); } catch (e) { print('Error stopping player: '+e); } }; RadioPlayerWin.prototype.closePlayer = function() { try { this.player.DoStop(); COM.disconnectObject( this.player ); } catch(e) { print('Error closing player: '+e); } };/* * BBC Radio Player + Last.fm * * chris.bowley@bbc.co.uk * August 2006 * */ function RecommendationManager() { this.networkId; this.networkImage; this.channelId; this.nowPlaying = {title: null, artist: null, url: null, date: null}; this.tracks = new Array(); this.holdingTracks = new Array(); this.submittedTracks = new Array(); this.submitting = false; this.nowPlayingTimer = new Timer(); this.nowPlayingTimer.interval = 60; this.nowPlayingTimer.onTimerFired = this.requestNowPlaying; this.nowPlayingTimer.ticking = false; this.submitTracksTimer = new Timer(); this.submitTracksTimer.interval = 60; this.submitTracksTimer.onTimerFired = this.submitTracks; this.submitTracksTimer.ticking = true; } RecommendationManager.prototype.init = function(networkId, networkImage, channelId) { this.networkId = networkId; this.networkImage = networkImage; this.channelId = channelId; audioScrobbler.networkProfile(); }; RecommendationManager.prototype.requestNowPlaying = function(obj) { print("RecommendationManager.requestNowPlaying: "+ this.networkId); audioScrobbler.nowPlaying(recommendationManager.networkId); }; RecommendationManager.prototype.nowPlayingSuccessful = function(track) { print('nowPlayingSuccessful: ' + track.title + ' ' + this.nowPlaying.title); if (track.title != this.nowPlaying.title) { print('New now playing track'); this.nowPlaying = {}; this.nowPlaying.title = track.title; this.nowPlaying.artist = track.artist; this.nowPlaying.trackUrl = track.trackUrl; this.nowPlaying.artistUrl = track.artistUrl; this.nowPlaying.date = track.date; this.nowPlaying.utc = track.utc; print("revommendationManager.nowPlayingSuccessful: " + preferences.opt.value); if (this.checkTrackTime()) { if (preferences.opt.value == 'Automatic') { if (this.submitting) this.holdingTracks.push(this.nowPlaying); else this.tracks.push(this.nowPlaying); print("this.tracks: "+this.tracks.length); print("this.holdingTracks: "+this.holdingTracks.length); } //print(this.tracks.length+' tracks'); //print(this.holdingTracks.length+' holding tracks'); audioScrobbler.artistInfo(track.artist); } else { this.nowPlaying.title = "Track information is out of date"; this.nowPlaying.artist = "Waiting for up to date information..."; presentation.displayNowPlaying(this.nowPlaying); } } else { } }; RecommendationManager.prototype.nowPlayingArtistInfo = function(artist) { print('nowPlayingArtistInfo '+artist.image+' '+artist.artists.length); if (artist) { this.nowPlaying.image = artist.image; this.nowPlaying.similarArtists = artist.artists; } else { this.nowPlaying.image = null; this.nowPlaying.similarArtists = null; } presentation.displayNowPlaying(this.nowPlaying); }; RecommendationManager.prototype.checkTrackTime = function() { // time now var now = new Date(); // track must start within 15 minutes if (now.getTime() < this.nowPlaying.utc + (1000*60*15)) { return true; } return false; }; RecommendationManager.prototype.getProgramme = function() { print("RecommendationManager.getProgramme"); this.nowPlayingTimer.ticking = true; // start timer ajax.post('www0.rdthdo.bbc.co.uk/cgi-perl/api/query.pl?','getProgramme',this.getProgrammeCallback,'method=bbc.schedule.getProgrammes&limit=1&channel_id='+this.channelId+'&format=simple&details=simple'); }; RecommendationManager.prototype.getProgrammeCallback = function() { print("RecommendationManager.getProgrammeCallback"); var xmlhttp = ajax.getXmlhttp('getProgramme'); if (xmlhttp.readyState != 4) return; if (xmlhttp.status != 200) return; if (!xmlhttp.responseXML) { presentation.displayProgramme(audioScrobbler.username + ' is listening to ' + preferences.network.value); } else { var response = xmlhttp.responseXML; recommendationManager.programme = response.evaluate('rsp/schedule/programme').item(0).getAttribute('title'); presentation.displayProgramme(audioScrobbler.username + ' is listening to ' + recommendationManager.programme + ' on ' + preferences.network.value); //presentation.displayLogo(recommendationManager.networkImage); presentation.displayOptionButton(); } ajax.releaseXmlhttp('getProgramme'); recommendationManager.requestNowPlaying(); }; RecommendationManager.prototype.addCurrentTrack = function() { print('addCurrentTrack'); if (this.tracks.length > 0) { if (this.tracks[this.tracks.length-1].title != this.nowPlaying.title && this.submittedTracks[this.submittedTracks.length-1].title != this.nowPlaying.title) { this.tracks.push(this.nowPlaying); } else { print('Cannot add now playing track - already in list'); presentation.displayErrorMessage('Track has already been queued'); } } else if (this.submittedTracks.length > 0) { if (this.submittedTracks[this.submittedTracks.length-1].title != this.nowPlaying.title) { this.tracks.push(this.nowPlaying); presentation.displaySuccessMessage('Track queued'); } else { print('Cannot add now playing track - already posted'); presentation.displayErrorMessage('Track has already been queued'); } } else { this.tracks.push(this.nowPlaying); presentation.displaySuccessMessage('Track queued'); } print(this.tracks.length+' tracks to submit'); }; RecommendationManager.prototype.removeCurrentTrack = function() { print('Removing track '+this.nowPlaying.title); //var new_tracks = new Array(); var found = false; for (var i=0; i0) { recommendationManager.submitTracksTimer.reset(); print('Submitting '+recommendationManager.tracks.length+' tracks'); submitting = true; audioScrobbler.submitTracks(recommendationManager.tracks); //recommendationManager.submitTracksTimer.ticking = false; } }; RecommendationManager.prototype.submitTracksSuccessful = function() { submitting = false; if (this.tracks.length > 0) this.submittedTracks = this.tracks; this.tracks = new Array(); // remove tracks from list if (this.holdingTracks.length > 0) { this.tracks = this.holdingTracks; this.holdingTracks = null; } print('submitTracksSucce