converse.nojquery.js 1.3 MB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028310293103031031310323103331034310353103631037310383103931040310413104231043310443104531046310473104831049310503105131052310533105431055310563105731058310593106031061310623106331064310653106631067310683106931070310713107231073310743107531076310773107831079310803108131082310833108431085310863108731088310893109031091310923109331094310953109631097310983109931100311013110231103311043110531106311073110831109311103111131112311133111431115311163111731118311193112031121311223112331124311253112631127311283112931130311313113231133311343113531136311373113831139311403114131142311433114431145311463114731148311493115031151311523115331154311553115631157311583115931160311613116231163311643116531166311673116831169311703117131172311733117431175311763117731178311793118031181311823118331184311853118631187311883118931190311913119231193311943119531196311973119831199312003120131202312033120431205312063120731208312093121031211312123121331214312153121631217312183121931220312213122231223312243122531226312273122831229312303123131232312333123431235312363123731238312393124031241312423124331244312453124631247312483124931250312513125231253312543125531256312573125831259312603126131262312633126431265312663126731268312693127031271312723127331274312753127631277312783127931280312813128231283312843128531286312873128831289312903129131292312933129431295312963129731298312993130031301313023130331304313053130631307313083130931310313113131231313313143131531316313173131831319313203132131322313233132431325313263132731328313293133031331313323133331334313353133631337313383133931340313413134231343313443134531346313473134831349313503135131352313533135431355313563135731358313593136031361313623136331364313653136631367313683136931370313713137231373313743137531376313773137831379313803138131382313833138431385313863138731388313893139031391313923139331394313953139631397313983139931400314013140231403314043140531406314073140831409314103141131412314133141431415314163141731418314193142031421314223142331424314253142631427314283142931430314313143231433314343143531436314373143831439314403144131442314433144431445314463144731448314493145031451314523145331454314553145631457314583145931460314613146231463314643146531466314673146831469314703147131472314733147431475314763147731478314793148031481314823148331484314853148631487314883148931490314913149231493314943149531496314973149831499315003150131502315033150431505315063150731508315093151031511315123151331514315153151631517315183151931520315213152231523315243152531526315273152831529315303153131532315333153431535315363153731538315393154031541315423154331544315453154631547315483154931550315513155231553315543155531556315573155831559315603156131562315633156431565315663156731568315693157031571315723157331574315753157631577315783157931580315813158231583315843158531586315873158831589315903159131592315933159431595315963159731598315993160031601316023160331604316053160631607316083160931610316113161231613316143161531616316173161831619316203162131622316233162431625316263162731628316293163031631316323163331634316353163631637316383163931640316413164231643316443164531646316473164831649316503165131652316533165431655316563165731658316593166031661316623166331664316653166631667316683166931670316713167231673316743167531676316773167831679316803168131682316833168431685316863168731688316893169031691316923169331694316953169631697316983169931700317013170231703317043170531706317073170831709317103171131712317133171431715317163171731718317193172031721317223172331724317253172631727317283172931730317313173231733317343173531736317373173831739317403174131742317433174431745317463174731748317493175031751317523175331754317553175631757317583175931760317613176231763317643176531766317673176831769317703177131772317733177431775317763177731778317793178031781317823178331784317853178631787317883178931790317913179231793317943179531796317973179831799318003180131802318033180431805318063180731808318093181031811318123181331814318153181631817318183181931820318213182231823318243182531826318273182831829318303183131832318333183431835318363183731838318393184031841318423184331844318453184631847318483184931850318513185231853318543185531856318573185831859318603186131862318633186431865318663186731868318693187031871318723187331874318753187631877318783187931880318813188231883318843188531886318873188831889318903189131892318933189431895318963189731898318993190031901319023190331904319053190631907319083190931910319113191231913319143191531916319173191831919319203192131922319233192431925319263192731928319293193031931319323193331934319353193631937319383193931940319413194231943319443194531946319473194831949319503195131952319533195431955319563195731958319593196031961319623196331964319653196631967319683196931970319713197231973319743197531976319773197831979319803198131982319833198431985319863198731988319893199031991319923199331994319953199631997319983199932000320013200232003320043200532006320073200832009320103201132012320133201432015320163201732018320193202032021320223202332024320253202632027320283202932030320313203232033320343203532036320373203832039320403204132042320433204432045320463204732048320493205032051320523205332054320553205632057320583205932060320613206232063320643206532066320673206832069320703207132072320733207432075320763207732078320793208032081320823208332084320853208632087320883208932090320913209232093320943209532096320973209832099321003210132102321033210432105321063210732108321093211032111321123211332114321153211632117321183211932120321213212232123321243212532126321273212832129321303213132132321333213432135321363213732138321393214032141321423214332144321453214632147321483214932150321513215232153321543215532156321573215832159321603216132162321633216432165321663216732168321693217032171321723217332174321753217632177321783217932180321813218232183321843218532186321873218832189321903219132192321933219432195321963219732198321993220032201322023220332204322053220632207322083220932210322113221232213322143221532216322173221832219322203222132222322233222432225322263222732228322293223032231322323223332234322353223632237322383223932240322413224232243322443224532246322473224832249322503225132252322533225432255322563225732258322593226032261322623226332264322653226632267322683226932270322713227232273322743227532276322773227832279322803228132282322833228432285322863228732288322893229032291322923229332294322953229632297322983229932300323013230232303323043230532306323073230832309323103231132312323133231432315323163231732318323193232032321323223232332324323253232632327323283232932330323313233232333323343233532336323373233832339323403234132342323433234432345323463234732348323493235032351323523235332354323553235632357323583235932360323613236232363323643236532366323673236832369323703237132372323733237432375323763237732378323793238032381323823238332384323853238632387323883238932390323913239232393323943239532396323973239832399324003240132402324033240432405324063240732408324093241032411324123241332414324153241632417324183241932420324213242232423324243242532426324273242832429324303243132432324333243432435324363243732438324393244032441324423244332444324453244632447324483244932450324513245232453324543245532456324573245832459324603246132462324633246432465324663246732468324693247032471324723247332474324753247632477324783247932480324813248232483324843248532486324873248832489324903249132492324933249432495324963249732498324993250032501325023250332504325053250632507325083250932510325113251232513325143251532516325173251832519325203252132522325233252432525325263252732528325293253032531325323253332534325353253632537325383253932540325413254232543325443254532546325473254832549325503255132552325533255432555325563255732558325593256032561325623256332564325653256632567325683256932570325713257232573325743257532576325773257832579325803258132582325833258432585325863258732588325893259032591325923259332594325953259632597325983259932600326013260232603326043260532606326073260832609326103261132612326133261432615326163261732618326193262032621326223262332624326253262632627326283262932630326313263232633326343263532636326373263832639326403264132642326433264432645326463264732648326493265032651326523265332654326553265632657326583265932660326613266232663326643266532666326673266832669326703267132672326733267432675326763267732678326793268032681326823268332684326853268632687326883268932690326913269232693326943269532696326973269832699327003270132702327033270432705327063270732708327093271032711327123271332714327153271632717327183271932720327213272232723327243272532726327273272832729327303273132732327333273432735327363273732738327393274032741327423274332744327453274632747327483274932750327513275232753327543275532756327573275832759327603276132762327633276432765327663276732768327693277032771327723277332774327753277632777327783277932780327813278232783327843278532786327873278832789327903279132792327933279432795327963279732798327993280032801328023280332804328053280632807328083280932810328113281232813328143281532816328173281832819328203282132822328233282432825328263282732828328293283032831328323283332834328353283632837328383283932840328413284232843328443284532846328473284832849328503285132852328533285432855328563285732858328593286032861328623286332864328653286632867328683286932870328713287232873328743287532876328773287832879328803288132882328833288432885328863288732888328893289032891328923289332894328953289632897328983289932900329013290232903329043290532906329073290832909329103291132912329133291432915329163291732918329193292032921329223292332924329253292632927329283292932930329313293232933329343293532936329373293832939329403294132942329433294432945329463294732948329493295032951329523295332954329553295632957329583295932960329613296232963329643296532966329673296832969329703297132972329733297432975329763297732978329793298032981329823298332984329853298632987329883298932990329913299232993329943299532996329973299832999330003300133002330033300433005330063300733008330093301033011330123301333014330153301633017330183301933020330213302233023330243302533026330273302833029330303303133032330333303433035330363303733038330393304033041330423304333044330453304633047330483304933050330513305233053330543305533056330573305833059330603306133062330633306433065330663306733068330693307033071330723307333074330753307633077330783307933080330813308233083330843308533086330873308833089330903309133092330933309433095330963309733098330993310033101331023310333104331053310633107331083310933110331113311233113331143311533116331173311833119331203312133122331233312433125331263312733128331293313033131331323313333134331353313633137331383313933140331413314233143331443314533146331473314833149331503315133152331533315433155331563315733158331593316033161331623316333164331653316633167331683316933170331713317233173331743317533176331773317833179331803318133182331833318433185331863318733188331893319033191331923319333194331953319633197331983319933200332013320233203332043320533206332073320833209332103321133212332133321433215332163321733218332193322033221332223322333224332253322633227332283322933230332313323233233332343323533236332373323833239332403324133242332433324433245332463324733248332493325033251332523325333254332553325633257332583325933260332613326233263332643326533266332673326833269332703327133272332733327433275332763327733278332793328033281332823328333284332853328633287332883328933290332913329233293332943329533296332973329833299333003330133302333033330433305333063330733308333093331033311333123331333314333153331633317333183331933320333213332233323333243332533326333273332833329333303333133332333333333433335333363333733338333393334033341333423334333344333453334633347333483334933350333513335233353333543335533356333573335833359333603336133362333633336433365333663336733368333693337033371333723337333374333753337633377333783337933380333813338233383333843338533386333873338833389333903339133392333933339433395333963339733398333993340033401334023340333404334053340633407334083340933410334113341233413334143341533416334173341833419334203342133422334233342433425334263342733428334293343033431334323343333434334353343633437334383343933440334413344233443334443344533446334473344833449334503345133452334533345433455334563345733458334593346033461334623346333464334653346633467334683346933470334713347233473334743347533476334773347833479334803348133482334833348433485334863348733488334893349033491334923349333494334953349633497334983349933500335013350233503335043350533506335073350833509335103351133512335133351433515335163351733518335193352033521335223352333524335253352633527335283352933530335313353233533335343353533536335373353833539335403354133542335433354433545335463354733548335493355033551335523355333554335553355633557335583355933560335613356233563335643356533566335673356833569335703357133572335733357433575335763357733578335793358033581335823358333584335853358633587335883358933590335913359233593335943359533596335973359833599336003360133602336033360433605336063360733608336093361033611336123361333614336153361633617336183361933620336213362233623336243362533626336273362833629336303363133632336333363433635336363363733638336393364033641336423364333644336453364633647336483364933650336513365233653336543365533656336573365833659336603366133662336633366433665336663366733668336693367033671336723367333674336753367633677336783367933680336813368233683336843368533686336873368833689336903369133692336933369433695336963369733698336993370033701337023370333704337053370633707337083370933710337113371233713337143371533716337173371833719337203372133722337233372433725337263372733728337293373033731337323373333734337353373633737337383373933740337413374233743337443374533746337473374833749337503375133752337533375433755337563375733758337593376033761337623376333764337653376633767337683376933770337713377233773337743377533776337773377833779337803378133782337833378433785337863378733788337893379033791337923379333794337953379633797337983379933800338013380233803338043380533806338073380833809338103381133812338133381433815338163381733818338193382033821338223382333824338253382633827338283382933830338313383233833338343383533836338373383833839338403384133842338433384433845338463384733848338493385033851338523385333854338553385633857338583385933860338613386233863338643386533866338673386833869338703387133872338733387433875338763387733878338793388033881338823388333884338853388633887338883388933890338913389233893338943389533896338973389833899339003390133902339033390433905339063390733908339093391033911339123391333914339153391633917339183391933920339213392233923339243392533926339273392833929339303393133932339333393433935339363393733938339393394033941339423394333944339453394633947339483394933950339513395233953339543395533956339573395833959339603396133962339633396433965339663396733968339693397033971339723397333974339753397633977339783397933980339813398233983339843398533986339873398833989339903399133992339933399433995339963399733998339993400034001340023400334004340053400634007340083400934010340113401234013340143401534016340173401834019340203402134022340233402434025340263402734028340293403034031340323403334034340353403634037340383403934040340413404234043340443404534046340473404834049340503405134052340533405434055340563405734058340593406034061340623406334064340653406634067340683406934070340713407234073340743407534076340773407834079340803408134082340833408434085340863408734088340893409034091340923409334094340953409634097340983409934100341013410234103341043410534106341073410834109341103411134112341133411434115341163411734118341193412034121341223412334124341253412634127341283412934130341313413234133341343413534136341373413834139341403414134142341433414434145341463414734148341493415034151341523415334154341553415634157341583415934160341613416234163341643416534166341673416834169341703417134172341733417434175341763417734178341793418034181341823418334184341853418634187341883418934190341913419234193341943419534196341973419834199342003420134202342033420434205342063420734208342093421034211342123421334214342153421634217342183421934220342213422234223342243422534226342273422834229342303423134232342333423434235342363423734238342393424034241342423424334244342453424634247342483424934250342513425234253342543425534256342573425834259342603426134262342633426434265342663426734268342693427034271342723427334274342753427634277342783427934280342813428234283342843428534286342873428834289342903429134292342933429434295342963429734298342993430034301343023430334304343053430634307343083430934310343113431234313343143431534316343173431834319343203432134322343233432434325343263432734328343293433034331343323433334334343353433634337343383433934340343413434234343343443434534346343473434834349343503435134352343533435434355343563435734358343593436034361343623436334364343653436634367343683436934370343713437234373343743437534376343773437834379343803438134382343833438434385343863438734388343893439034391343923439334394343953439634397343983439934400344013440234403344043440534406344073440834409344103441134412344133441434415344163441734418344193442034421344223442334424344253442634427344283442934430344313443234433344343443534436344373443834439344403444134442344433444434445344463444734448344493445034451344523445334454344553445634457344583445934460344613446234463344643446534466344673446834469344703447134472344733447434475344763447734478344793448034481344823448334484344853448634487344883448934490344913449234493344943449534496344973449834499345003450134502345033450434505345063450734508345093451034511345123451334514345153451634517345183451934520345213452234523345243452534526345273452834529345303453134532345333453434535345363453734538345393454034541345423454334544345453454634547345483454934550345513455234553345543455534556345573455834559345603456134562345633456434565345663456734568345693457034571345723457334574345753457634577345783457934580345813458234583345843458534586345873458834589345903459134592345933459434595345963459734598345993460034601346023460334604346053460634607346083460934610346113461234613346143461534616346173461834619346203462134622346233462434625346263462734628346293463034631346323463334634346353463634637346383463934640346413464234643346443464534646346473464834649346503465134652346533465434655346563465734658346593466034661346623466334664346653466634667346683466934670346713467234673346743467534676346773467834679346803468134682346833468434685346863468734688346893469034691346923469334694346953469634697346983469934700347013470234703347043470534706347073470834709347103471134712347133471434715347163471734718347193472034721347223472334724347253472634727347283472934730347313473234733347343473534736347373473834739347403474134742347433474434745347463474734748347493475034751347523475334754347553475634757347583475934760347613476234763347643476534766347673476834769347703477134772347733477434775347763477734778347793478034781347823478334784347853478634787347883478934790347913479234793347943479534796347973479834799348003480134802348033480434805348063480734808348093481034811348123481334814348153481634817348183481934820348213482234823348243482534826348273482834829348303483134832348333483434835348363483734838348393484034841348423484334844348453484634847348483484934850348513485234853348543485534856348573485834859348603486134862348633486434865348663486734868348693487034871348723487334874348753487634877348783487934880348813488234883348843488534886348873488834889348903489134892348933489434895348963489734898348993490034901349023490334904349053490634907349083490934910349113491234913349143491534916349173491834919349203492134922349233492434925349263492734928349293493034931349323493334934349353493634937349383493934940349413494234943349443494534946349473494834949349503495134952349533495434955349563495734958349593496034961349623496334964349653496634967349683496934970349713497234973349743497534976349773497834979349803498134982349833498434985349863498734988349893499034991349923499334994349953499634997349983499935000350013500235003350043500535006350073500835009350103501135012350133501435015350163501735018350193502035021350223502335024350253502635027350283502935030350313503235033350343503535036350373503835039350403504135042350433504435045350463504735048350493505035051350523505335054350553505635057350583505935060350613506235063350643506535066350673506835069350703507135072350733507435075350763507735078350793508035081350823508335084350853508635087350883508935090350913509235093350943509535096350973509835099351003510135102351033510435105351063510735108351093511035111351123511335114351153511635117351183511935120351213512235123351243512535126351273512835129351303513135132351333513435135351363513735138351393514035141351423514335144351453514635147351483514935150351513515235153351543515535156351573515835159351603516135162351633516435165351663516735168351693517035171351723517335174351753517635177351783517935180351813518235183351843518535186351873518835189351903519135192351933519435195351963519735198351993520035201352023520335204352053520635207352083520935210352113521235213352143521535216352173521835219352203522135222352233522435225352263522735228352293523035231352323523335234352353523635237352383523935240352413524235243352443524535246352473524835249352503525135252352533525435255352563525735258352593526035261352623526335264352653526635267352683526935270352713527235273352743527535276352773527835279352803528135282352833528435285352863528735288352893529035291352923529335294352953529635297352983529935300353013530235303353043530535306353073530835309353103531135312353133531435315353163531735318353193532035321353223532335324353253532635327353283532935330353313533235333353343533535336353373533835339353403534135342353433534435345353463534735348353493535035351353523535335354353553535635357353583535935360353613536235363353643536535366353673536835369353703537135372353733537435375353763537735378353793538035381353823538335384353853538635387353883538935390353913539235393353943539535396353973539835399354003540135402354033540435405354063540735408354093541035411354123541335414354153541635417354183541935420354213542235423354243542535426354273542835429354303543135432354333543435435354363543735438354393544035441354423544335444354453544635447354483544935450354513545235453354543545535456354573545835459354603546135462354633546435465354663546735468354693547035471354723547335474354753547635477354783547935480354813548235483354843548535486354873548835489354903549135492354933549435495354963549735498354993550035501355023550335504355053550635507355083550935510355113551235513355143551535516355173551835519355203552135522355233552435525355263552735528355293553035531355323553335534355353553635537355383553935540355413554235543355443554535546355473554835549355503555135552355533555435555355563555735558355593556035561355623556335564355653556635567355683556935570355713557235573355743557535576355773557835579355803558135582355833558435585355863558735588355893559035591355923559335594355953559635597355983559935600356013560235603356043560535606356073560835609356103561135612356133561435615356163561735618356193562035621356223562335624356253562635627356283562935630356313563235633356343563535636356373563835639356403564135642356433564435645356463564735648356493565035651356523565335654356553565635657356583565935660356613566235663356643566535666356673566835669356703567135672356733567435675356763567735678356793568035681356823568335684356853568635687356883568935690356913569235693356943569535696356973569835699357003570135702357033570435705357063570735708357093571035711357123571335714357153571635717357183571935720357213572235723357243572535726357273572835729357303573135732357333573435735357363573735738357393574035741357423574335744357453574635747357483574935750357513575235753357543575535756357573575835759357603576135762357633576435765357663576735768357693577035771357723577335774357753577635777357783577935780357813578235783357843578535786357873578835789357903579135792357933579435795357963579735798357993580035801358023580335804358053580635807358083580935810358113581235813358143581535816358173581835819358203582135822358233582435825358263582735828358293583035831358323583335834358353583635837358383583935840358413584235843358443584535846358473584835849358503585135852358533585435855358563585735858358593586035861358623586335864358653586635867358683586935870358713587235873358743587535876358773587835879358803588135882358833588435885358863588735888358893589035891358923589335894358953589635897358983589935900359013590235903359043590535906359073590835909359103591135912359133591435915359163591735918359193592035921359223592335924359253592635927359283592935930359313593235933359343593535936359373593835939359403594135942359433594435945359463594735948359493595035951359523595335954359553595635957359583595935960359613596235963359643596535966359673596835969359703597135972359733597435975359763597735978359793598035981359823598335984359853598635987359883598935990359913599235993359943599535996359973599835999360003600136002360033600436005360063600736008360093601036011360123601336014360153601636017360183601936020360213602236023360243602536026360273602836029360303603136032360333603436035360363603736038360393604036041360423604336044360453604636047360483604936050360513605236053360543605536056360573605836059360603606136062360633606436065360663606736068360693607036071360723607336074360753607636077360783607936080360813608236083360843608536086360873608836089360903609136092360933609436095360963609736098360993610036101361023610336104361053610636107361083610936110361113611236113361143611536116361173611836119361203612136122361233612436125361263612736128361293613036131361323613336134361353613636137361383613936140361413614236143361443614536146361473614836149361503615136152361533615436155361563615736158361593616036161361623616336164361653616636167361683616936170361713617236173361743617536176361773617836179361803618136182361833618436185361863618736188361893619036191361923619336194361953619636197361983619936200362013620236203362043620536206362073620836209362103621136212362133621436215362163621736218362193622036221362223622336224362253622636227362283622936230362313623236233362343623536236362373623836239362403624136242362433624436245362463624736248362493625036251362523625336254362553625636257362583625936260362613626236263362643626536266362673626836269362703627136272362733627436275362763627736278362793628036281362823628336284362853628636287362883628936290362913629236293362943629536296362973629836299363003630136302363033630436305363063630736308363093631036311363123631336314363153631636317363183631936320363213632236323363243632536326363273632836329363303633136332363333633436335363363633736338363393634036341363423634336344363453634636347363483634936350363513635236353363543635536356363573635836359363603636136362363633636436365363663636736368363693637036371363723637336374363753637636377363783637936380363813638236383363843638536386363873638836389363903639136392363933639436395363963639736398363993640036401364023640336404364053640636407364083640936410364113641236413364143641536416364173641836419364203642136422364233642436425364263642736428364293643036431364323643336434364353643636437364383643936440364413644236443364443644536446364473644836449364503645136452364533645436455364563645736458364593646036461364623646336464364653646636467364683646936470364713647236473364743647536476364773647836479364803648136482364833648436485364863648736488364893649036491364923649336494364953649636497364983649936500365013650236503365043650536506365073650836509365103651136512365133651436515365163651736518365193652036521365223652336524365253652636527365283652936530365313653236533365343653536536365373653836539365403654136542365433654436545365463654736548365493655036551365523655336554365553655636557365583655936560365613656236563365643656536566365673656836569365703657136572365733657436575365763657736578365793658036581365823658336584365853658636587365883658936590365913659236593365943659536596365973659836599366003660136602366033660436605366063660736608366093661036611366123661336614366153661636617366183661936620366213662236623366243662536626366273662836629366303663136632366333663436635366363663736638366393664036641366423664336644366453664636647366483664936650366513665236653366543665536656366573665836659366603666136662366633666436665366663666736668366693667036671366723667336674366753667636677366783667936680366813668236683366843668536686366873668836689366903669136692366933669436695366963669736698366993670036701367023670336704367053670636707367083670936710367113671236713367143671536716367173671836719367203672136722367233672436725367263672736728367293673036731367323673336734367353673636737367383673936740367413674236743367443674536746367473674836749367503675136752367533675436755367563675736758367593676036761367623676336764367653676636767367683676936770367713677236773367743677536776367773677836779367803678136782367833678436785367863678736788367893679036791367923679336794367953679636797367983679936800368013680236803368043680536806368073680836809368103681136812368133681436815368163681736818368193682036821368223682336824368253682636827368283682936830368313683236833368343683536836368373683836839368403684136842368433684436845368463684736848368493685036851368523685336854368553685636857368583685936860368613686236863368643686536866368673686836869368703687136872368733687436875368763687736878368793688036881368823688336884368853688636887368883688936890368913689236893368943689536896368973689836899369003690136902369033690436905369063690736908369093691036911369123691336914369153691636917369183691936920369213692236923369243692536926369273692836929369303693136932369333693436935369363693736938369393694036941369423694336944369453694636947369483694936950369513695236953369543695536956369573695836959369603696136962369633696436965369663696736968369693697036971369723697336974369753697636977369783697936980369813698236983369843698536986369873698836989369903699136992369933699436995369963699736998369993700037001370023700337004370053700637007370083700937010370113701237013370143701537016370173701837019370203702137022370233702437025370263702737028370293703037031370323703337034370353703637037370383703937040370413704237043370443704537046370473704837049370503705137052370533705437055370563705737058370593706037061370623706337064370653706637067370683706937070370713707237073370743707537076370773707837079370803708137082370833708437085370863708737088370893709037091370923709337094370953709637097370983709937100371013710237103371043710537106371073710837109371103711137112371133711437115371163711737118371193712037121371223712337124371253712637127371283712937130371313713237133371343713537136371373713837139371403714137142371433714437145371463714737148371493715037151371523715337154371553715637157371583715937160371613716237163371643716537166371673716837169371703717137172371733717437175371763717737178371793718037181371823718337184371853718637187
  1. /**
  2. * @license almond 0.2.9 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.
  3. * Available via the MIT or new BSD license.
  4. * see: http://github.com/jrburke/almond for details
  5. */
  6. //Going sloppy to avoid 'use strict' string cost, but strict practices should
  7. //be followed.
  8. /*jslint sloppy: true */
  9. /*global setTimeout: false */
  10. var requirejs, require, define;
  11. (function (undef) {
  12. var main, req, makeMap, handlers,
  13. defined = {},
  14. waiting = {},
  15. config = {},
  16. defining = {},
  17. hasOwn = Object.prototype.hasOwnProperty,
  18. aps = [].slice,
  19. jsSuffixRegExp = /\.js$/;
  20. function hasProp(obj, prop) {
  21. return hasOwn.call(obj, prop);
  22. }
  23. /**
  24. * Given a relative module name, like ./something, normalize it to
  25. * a real name that can be mapped to a path.
  26. * @param {String} name the relative name
  27. * @param {String} baseName a real name that the name arg is relative
  28. * to.
  29. * @returns {String} normalized name
  30. */
  31. function normalize(name, baseName) {
  32. var nameParts, nameSegment, mapValue, foundMap, lastIndex,
  33. foundI, foundStarMap, starI, i, j, part,
  34. baseParts = baseName && baseName.split("/"),
  35. map = config.map,
  36. starMap = (map && map['*']) || {};
  37. //Adjust any relative paths.
  38. if (name && name.charAt(0) === ".") {
  39. //If have a base name, try to normalize against it,
  40. //otherwise, assume it is a top-level require that will
  41. //be relative to baseUrl in the end.
  42. if (baseName) {
  43. //Convert baseName to array, and lop off the last part,
  44. //so that . matches that "directory" and not name of the baseName's
  45. //module. For instance, baseName of "one/two/three", maps to
  46. //"one/two/three.js", but we want the directory, "one/two" for
  47. //this normalization.
  48. baseParts = baseParts.slice(0, baseParts.length - 1);
  49. name = name.split('/');
  50. lastIndex = name.length - 1;
  51. // Node .js allowance:
  52. if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
  53. name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
  54. }
  55. name = baseParts.concat(name);
  56. //start trimDots
  57. for (i = 0; i < name.length; i += 1) {
  58. part = name[i];
  59. if (part === ".") {
  60. name.splice(i, 1);
  61. i -= 1;
  62. } else if (part === "..") {
  63. if (i === 1 && (name[2] === '..' || name[0] === '..')) {
  64. //End of the line. Keep at least one non-dot
  65. //path segment at the front so it can be mapped
  66. //correctly to disk. Otherwise, there is likely
  67. //no path mapping for a path starting with '..'.
  68. //This can still fail, but catches the most reasonable
  69. //uses of ..
  70. break;
  71. } else if (i > 0) {
  72. name.splice(i - 1, 2);
  73. i -= 2;
  74. }
  75. }
  76. }
  77. //end trimDots
  78. name = name.join("/");
  79. } else if (name.indexOf('./') === 0) {
  80. // No baseName, so this is ID is resolved relative
  81. // to baseUrl, pull off the leading dot.
  82. name = name.substring(2);
  83. }
  84. }
  85. //Apply map config if available.
  86. if ((baseParts || starMap) && map) {
  87. nameParts = name.split('/');
  88. for (i = nameParts.length; i > 0; i -= 1) {
  89. nameSegment = nameParts.slice(0, i).join("/");
  90. if (baseParts) {
  91. //Find the longest baseName segment match in the config.
  92. //So, do joins on the biggest to smallest lengths of baseParts.
  93. for (j = baseParts.length; j > 0; j -= 1) {
  94. mapValue = map[baseParts.slice(0, j).join('/')];
  95. //baseName segment has config, find if it has one for
  96. //this name.
  97. if (mapValue) {
  98. mapValue = mapValue[nameSegment];
  99. if (mapValue) {
  100. //Match, update name to the new value.
  101. foundMap = mapValue;
  102. foundI = i;
  103. break;
  104. }
  105. }
  106. }
  107. }
  108. if (foundMap) {
  109. break;
  110. }
  111. //Check for a star map match, but just hold on to it,
  112. //if there is a shorter segment match later in a matching
  113. //config, then favor over this star map.
  114. if (!foundStarMap && starMap && starMap[nameSegment]) {
  115. foundStarMap = starMap[nameSegment];
  116. starI = i;
  117. }
  118. }
  119. if (!foundMap && foundStarMap) {
  120. foundMap = foundStarMap;
  121. foundI = starI;
  122. }
  123. if (foundMap) {
  124. nameParts.splice(0, foundI, foundMap);
  125. name = nameParts.join('/');
  126. }
  127. }
  128. return name;
  129. }
  130. function makeRequire(relName, forceSync) {
  131. return function () {
  132. //A version of a require function that passes a moduleName
  133. //value for items that may need to
  134. //look up paths relative to the moduleName
  135. return req.apply(undef, aps.call(arguments, 0).concat([relName, forceSync]));
  136. };
  137. }
  138. function makeNormalize(relName) {
  139. return function (name) {
  140. return normalize(name, relName);
  141. };
  142. }
  143. function makeLoad(depName) {
  144. return function (value) {
  145. defined[depName] = value;
  146. };
  147. }
  148. function callDep(name) {
  149. if (hasProp(waiting, name)) {
  150. var args = waiting[name];
  151. delete waiting[name];
  152. defining[name] = true;
  153. main.apply(undef, args);
  154. }
  155. if (!hasProp(defined, name) && !hasProp(defining, name)) {
  156. throw new Error('No ' + name);
  157. }
  158. return defined[name];
  159. }
  160. //Turns a plugin!resource to [plugin, resource]
  161. //with the plugin being undefined if the name
  162. //did not have a plugin prefix.
  163. function splitPrefix(name) {
  164. var prefix,
  165. index = name ? name.indexOf('!') : -1;
  166. if (index > -1) {
  167. prefix = name.substring(0, index);
  168. name = name.substring(index + 1, name.length);
  169. }
  170. return [prefix, name];
  171. }
  172. /**
  173. * Makes a name map, normalizing the name, and using a plugin
  174. * for normalization if necessary. Grabs a ref to plugin
  175. * too, as an optimization.
  176. */
  177. makeMap = function (name, relName) {
  178. var plugin,
  179. parts = splitPrefix(name),
  180. prefix = parts[0];
  181. name = parts[1];
  182. if (prefix) {
  183. prefix = normalize(prefix, relName);
  184. plugin = callDep(prefix);
  185. }
  186. //Normalize according
  187. if (prefix) {
  188. if (plugin && plugin.normalize) {
  189. name = plugin.normalize(name, makeNormalize(relName));
  190. } else {
  191. name = normalize(name, relName);
  192. }
  193. } else {
  194. name = normalize(name, relName);
  195. parts = splitPrefix(name);
  196. prefix = parts[0];
  197. name = parts[1];
  198. if (prefix) {
  199. plugin = callDep(prefix);
  200. }
  201. }
  202. //Using ridiculous property names for space reasons
  203. return {
  204. f: prefix ? prefix + '!' + name : name, //fullName
  205. n: name,
  206. pr: prefix,
  207. p: plugin
  208. };
  209. };
  210. function makeConfig(name) {
  211. return function () {
  212. return (config && config.config && config.config[name]) || {};
  213. };
  214. }
  215. handlers = {
  216. require: function (name) {
  217. return makeRequire(name);
  218. },
  219. exports: function (name) {
  220. var e = defined[name];
  221. if (typeof e !== 'undefined') {
  222. return e;
  223. } else {
  224. return (defined[name] = {});
  225. }
  226. },
  227. module: function (name) {
  228. return {
  229. id: name,
  230. uri: '',
  231. exports: defined[name],
  232. config: makeConfig(name)
  233. };
  234. }
  235. };
  236. main = function (name, deps, callback, relName) {
  237. var cjsModule, depName, ret, map, i,
  238. args = [],
  239. callbackType = typeof callback,
  240. usingExports;
  241. //Use name if no relName
  242. relName = relName || name;
  243. //Call the callback to define the module, if necessary.
  244. if (callbackType === 'undefined' || callbackType === 'function') {
  245. //Pull out the defined dependencies and pass the ordered
  246. //values to the callback.
  247. //Default to [require, exports, module] if no deps
  248. deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
  249. for (i = 0; i < deps.length; i += 1) {
  250. map = makeMap(deps[i], relName);
  251. depName = map.f;
  252. //Fast path CommonJS standard dependencies.
  253. if (depName === "require") {
  254. args[i] = handlers.require(name);
  255. } else if (depName === "exports") {
  256. //CommonJS module spec 1.1
  257. args[i] = handlers.exports(name);
  258. usingExports = true;
  259. } else if (depName === "module") {
  260. //CommonJS module spec 1.1
  261. cjsModule = args[i] = handlers.module(name);
  262. } else if (hasProp(defined, depName) ||
  263. hasProp(waiting, depName) ||
  264. hasProp(defining, depName)) {
  265. args[i] = callDep(depName);
  266. } else if (map.p) {
  267. map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
  268. args[i] = defined[depName];
  269. } else {
  270. throw new Error(name + ' missing ' + depName);
  271. }
  272. }
  273. ret = callback ? callback.apply(defined[name], args) : undefined;
  274. if (name) {
  275. //If setting exports via "module" is in play,
  276. //favor that over return value and exports. After that,
  277. //favor a non-undefined return value over exports use.
  278. if (cjsModule && cjsModule.exports !== undef &&
  279. cjsModule.exports !== defined[name]) {
  280. defined[name] = cjsModule.exports;
  281. } else if (ret !== undef || !usingExports) {
  282. //Use the return value from the function.
  283. defined[name] = ret;
  284. }
  285. }
  286. } else if (name) {
  287. //May just be an object definition for the module. Only
  288. //worry about defining if have a module name.
  289. defined[name] = callback;
  290. }
  291. };
  292. requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
  293. if (typeof deps === "string") {
  294. if (handlers[deps]) {
  295. //callback in this case is really relName
  296. return handlers[deps](callback);
  297. }
  298. //Just return the module wanted. In this scenario, the
  299. //deps arg is the module name, and second arg (if passed)
  300. //is just the relName.
  301. //Normalize module name, if it contains . or ..
  302. return callDep(makeMap(deps, callback).f);
  303. } else if (!deps.splice) {
  304. //deps is a config object, not an array.
  305. config = deps;
  306. if (config.deps) {
  307. req(config.deps, config.callback);
  308. }
  309. if (!callback) {
  310. return;
  311. }
  312. if (callback.splice) {
  313. //callback is an array, which means it is a dependency list.
  314. //Adjust args if there are dependencies
  315. deps = callback;
  316. callback = relName;
  317. relName = null;
  318. } else {
  319. deps = undef;
  320. }
  321. }
  322. //Support require(['a'])
  323. callback = callback || function () {};
  324. //If relName is a function, it is an errback handler,
  325. //so remove it.
  326. if (typeof relName === 'function') {
  327. relName = forceSync;
  328. forceSync = alt;
  329. }
  330. //Simulate async callback;
  331. if (forceSync) {
  332. main(undef, deps, callback, relName);
  333. } else {
  334. //Using a non-zero value because of concern for what old browsers
  335. //do, and latest browsers "upgrade" to 4 if lower value is used:
  336. //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
  337. //If want a value immediately, use require('id') instead -- something
  338. //that works in almond on the global level, but not guaranteed and
  339. //unlikely to work in other AMD implementations.
  340. setTimeout(function () {
  341. main(undef, deps, callback, relName);
  342. }, 4);
  343. }
  344. return req;
  345. };
  346. /**
  347. * Just drops the config on the floor, but returns req in case
  348. * the config return value is used.
  349. */
  350. req.config = function (cfg) {
  351. return req(cfg);
  352. };
  353. /**
  354. * Expose module registry for debugging and tooling
  355. */
  356. requirejs._defined = defined;
  357. define = function (name, deps, callback) {
  358. //This module may not have dependencies
  359. if (!deps.splice) {
  360. //deps is not an array, so probably means
  361. //an object literal or factory function for
  362. //the value. Adjust args.
  363. callback = deps;
  364. deps = [];
  365. }
  366. if (!hasProp(defined, name) && !hasProp(waiting, name)) {
  367. waiting[name] = [name, deps, callback];
  368. }
  369. };
  370. define.amd = {
  371. jQuery: true
  372. };
  373. }());
  374. define("components/almond/almond.js", function(){});
  375. define('jquery', [], function () {
  376. return jQuery;
  377. });
  378. define('jquery-private',['jquery'], function (jq) {
  379. return jq;
  380. });
  381. /**
  382. * @license RequireJS text 2.0.12 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
  383. * Available via the MIT or new BSD license.
  384. * see: http://github.com/requirejs/text for details
  385. */
  386. /*jslint regexp: true */
  387. /*global require, XMLHttpRequest, ActiveXObject,
  388. define, window, process, Packages,
  389. java, location, Components, FileUtils */
  390. define('text',['module'], function (module) {
  391. var text, fs, Cc, Ci, xpcIsWindows,
  392. progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],
  393. xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,
  394. bodyRegExp = /<body[^>]*>\s*([\s\S]+)\s*<\/body>/im,
  395. hasLocation = typeof location !== 'undefined' && location.href,
  396. defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''),
  397. defaultHostName = hasLocation && location.hostname,
  398. defaultPort = hasLocation && (location.port || undefined),
  399. buildMap = {},
  400. masterConfig = (module.config && module.config()) || {};
  401. text = {
  402. version: '2.0.12',
  403. strip: function (content) {
  404. //Strips <?xml ...?> declarations so that external SVG and XML
  405. //documents can be added to a document without worry. Also, if the string
  406. //is an HTML document, only the part inside the body tag is returned.
  407. if (content) {
  408. content = content.replace(xmlRegExp, "");
  409. var matches = content.match(bodyRegExp);
  410. if (matches) {
  411. content = matches[1];
  412. }
  413. } else {
  414. content = "";
  415. }
  416. return content;
  417. },
  418. jsEscape: function (content) {
  419. return content.replace(/(['\\])/g, '\\$1')
  420. .replace(/[\f]/g, "\\f")
  421. .replace(/[\b]/g, "\\b")
  422. .replace(/[\n]/g, "\\n")
  423. .replace(/[\t]/g, "\\t")
  424. .replace(/[\r]/g, "\\r")
  425. .replace(/[\u2028]/g, "\\u2028")
  426. .replace(/[\u2029]/g, "\\u2029");
  427. },
  428. createXhr: masterConfig.createXhr || function () {
  429. //Would love to dump the ActiveX crap in here. Need IE 6 to die first.
  430. var xhr, i, progId;
  431. if (typeof XMLHttpRequest !== "undefined") {
  432. return new XMLHttpRequest();
  433. } else if (typeof ActiveXObject !== "undefined") {
  434. for (i = 0; i < 3; i += 1) {
  435. progId = progIds[i];
  436. try {
  437. xhr = new ActiveXObject(progId);
  438. } catch (e) {}
  439. if (xhr) {
  440. progIds = [progId]; // so faster next time
  441. break;
  442. }
  443. }
  444. }
  445. return xhr;
  446. },
  447. /**
  448. * Parses a resource name into its component parts. Resource names
  449. * look like: module/name.ext!strip, where the !strip part is
  450. * optional.
  451. * @param {String} name the resource name
  452. * @returns {Object} with properties "moduleName", "ext" and "strip"
  453. * where strip is a boolean.
  454. */
  455. parseName: function (name) {
  456. var modName, ext, temp,
  457. strip = false,
  458. index = name.indexOf("."),
  459. isRelative = name.indexOf('./') === 0 ||
  460. name.indexOf('../') === 0;
  461. if (index !== -1 && (!isRelative || index > 1)) {
  462. modName = name.substring(0, index);
  463. ext = name.substring(index + 1, name.length);
  464. } else {
  465. modName = name;
  466. }
  467. temp = ext || modName;
  468. index = temp.indexOf("!");
  469. if (index !== -1) {
  470. //Pull off the strip arg.
  471. strip = temp.substring(index + 1) === "strip";
  472. temp = temp.substring(0, index);
  473. if (ext) {
  474. ext = temp;
  475. } else {
  476. modName = temp;
  477. }
  478. }
  479. return {
  480. moduleName: modName,
  481. ext: ext,
  482. strip: strip
  483. };
  484. },
  485. xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/,
  486. /**
  487. * Is an URL on another domain. Only works for browser use, returns
  488. * false in non-browser environments. Only used to know if an
  489. * optimized .js version of a text resource should be loaded
  490. * instead.
  491. * @param {String} url
  492. * @returns Boolean
  493. */
  494. useXhr: function (url, protocol, hostname, port) {
  495. var uProtocol, uHostName, uPort,
  496. match = text.xdRegExp.exec(url);
  497. if (!match) {
  498. return true;
  499. }
  500. uProtocol = match[2];
  501. uHostName = match[3];
  502. uHostName = uHostName.split(':');
  503. uPort = uHostName[1];
  504. uHostName = uHostName[0];
  505. return (!uProtocol || uProtocol === protocol) &&
  506. (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) &&
  507. ((!uPort && !uHostName) || uPort === port);
  508. },
  509. finishLoad: function (name, strip, content, onLoad) {
  510. content = strip ? text.strip(content) : content;
  511. if (masterConfig.isBuild) {
  512. buildMap[name] = content;
  513. }
  514. onLoad(content);
  515. },
  516. load: function (name, req, onLoad, config) {
  517. //Name has format: some.module.filext!strip
  518. //The strip part is optional.
  519. //if strip is present, then that means only get the string contents
  520. //inside a body tag in an HTML string. For XML/SVG content it means
  521. //removing the <?xml ...?> declarations so the content can be inserted
  522. //into the current doc without problems.
  523. // Do not bother with the work if a build and text will
  524. // not be inlined.
  525. if (config && config.isBuild && !config.inlineText) {
  526. onLoad();
  527. return;
  528. }
  529. masterConfig.isBuild = config && config.isBuild;
  530. var parsed = text.parseName(name),
  531. nonStripName = parsed.moduleName +
  532. (parsed.ext ? '.' + parsed.ext : ''),
  533. url = req.toUrl(nonStripName),
  534. useXhr = (masterConfig.useXhr) ||
  535. text.useXhr;
  536. // Do not load if it is an empty: url
  537. if (url.indexOf('empty:') === 0) {
  538. onLoad();
  539. return;
  540. }
  541. //Load the text. Use XHR if possible and in a browser.
  542. if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) {
  543. text.get(url, function (content) {
  544. text.finishLoad(name, parsed.strip, content, onLoad);
  545. }, function (err) {
  546. if (onLoad.error) {
  547. onLoad.error(err);
  548. }
  549. });
  550. } else {
  551. //Need to fetch the resource across domains. Assume
  552. //the resource has been optimized into a JS module. Fetch
  553. //by the module name + extension, but do not include the
  554. //!strip part to avoid file system issues.
  555. req([nonStripName], function (content) {
  556. text.finishLoad(parsed.moduleName + '.' + parsed.ext,
  557. parsed.strip, content, onLoad);
  558. });
  559. }
  560. },
  561. write: function (pluginName, moduleName, write, config) {
  562. if (buildMap.hasOwnProperty(moduleName)) {
  563. var content = text.jsEscape(buildMap[moduleName]);
  564. write.asModule(pluginName + "!" + moduleName,
  565. "define(function () { return '" +
  566. content +
  567. "';});\n");
  568. }
  569. },
  570. writeFile: function (pluginName, moduleName, req, write, config) {
  571. var parsed = text.parseName(moduleName),
  572. extPart = parsed.ext ? '.' + parsed.ext : '',
  573. nonStripName = parsed.moduleName + extPart,
  574. //Use a '.js' file name so that it indicates it is a
  575. //script that can be loaded across domains.
  576. fileName = req.toUrl(parsed.moduleName + extPart) + '.js';
  577. //Leverage own load() method to load plugin value, but only
  578. //write out values that do not have the strip argument,
  579. //to avoid any potential issues with ! in file names.
  580. text.load(nonStripName, req, function (value) {
  581. //Use own write() method to construct full module value.
  582. //But need to create shell that translates writeFile's
  583. //write() to the right interface.
  584. var textWrite = function (contents) {
  585. return write(fileName, contents);
  586. };
  587. textWrite.asModule = function (moduleName, contents) {
  588. return write.asModule(moduleName, fileName, contents);
  589. };
  590. text.write(pluginName, nonStripName, textWrite, config);
  591. }, config);
  592. }
  593. };
  594. if (masterConfig.env === 'node' || (!masterConfig.env &&
  595. typeof process !== "undefined" &&
  596. process.versions &&
  597. !!process.versions.node &&
  598. !process.versions['node-webkit'])) {
  599. //Using special require.nodeRequire, something added by r.js.
  600. fs = require.nodeRequire('fs');
  601. text.get = function (url, callback, errback) {
  602. try {
  603. var file = fs.readFileSync(url, 'utf8');
  604. //Remove BOM (Byte Mark Order) from utf8 files if it is there.
  605. if (file.indexOf('\uFEFF') === 0) {
  606. file = file.substring(1);
  607. }
  608. callback(file);
  609. } catch (e) {
  610. if (errback) {
  611. errback(e);
  612. }
  613. }
  614. };
  615. } else if (masterConfig.env === 'xhr' || (!masterConfig.env &&
  616. text.createXhr())) {
  617. text.get = function (url, callback, errback, headers) {
  618. var xhr = text.createXhr(), header;
  619. xhr.open('GET', url, true);
  620. //Allow plugins direct access to xhr headers
  621. if (headers) {
  622. for (header in headers) {
  623. if (headers.hasOwnProperty(header)) {
  624. xhr.setRequestHeader(header.toLowerCase(), headers[header]);
  625. }
  626. }
  627. }
  628. //Allow overrides specified in config
  629. if (masterConfig.onXhr) {
  630. masterConfig.onXhr(xhr, url);
  631. }
  632. xhr.onreadystatechange = function (evt) {
  633. var status, err;
  634. //Do not explicitly handle errors, those should be
  635. //visible via console output in the browser.
  636. if (xhr.readyState === 4) {
  637. status = xhr.status || 0;
  638. if (status > 399 && status < 600) {
  639. //An http 4xx or 5xx error. Signal an error.
  640. err = new Error(url + ' HTTP status: ' + status);
  641. err.xhr = xhr;
  642. if (errback) {
  643. errback(err);
  644. }
  645. } else {
  646. callback(xhr.responseText);
  647. }
  648. if (masterConfig.onXhrComplete) {
  649. masterConfig.onXhrComplete(xhr, url);
  650. }
  651. }
  652. };
  653. xhr.send(null);
  654. };
  655. } else if (masterConfig.env === 'rhino' || (!masterConfig.env &&
  656. typeof Packages !== 'undefined' && typeof java !== 'undefined')) {
  657. //Why Java, why is this so awkward?
  658. text.get = function (url, callback) {
  659. var stringBuffer, line,
  660. encoding = "utf-8",
  661. file = new java.io.File(url),
  662. lineSeparator = java.lang.System.getProperty("line.separator"),
  663. input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)),
  664. content = '';
  665. try {
  666. stringBuffer = new java.lang.StringBuffer();
  667. line = input.readLine();
  668. // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324
  669. // http://www.unicode.org/faq/utf_bom.html
  670. // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK:
  671. // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058
  672. if (line && line.length() && line.charAt(0) === 0xfeff) {
  673. // Eat the BOM, since we've already found the encoding on this file,
  674. // and we plan to concatenating this buffer with others; the BOM should
  675. // only appear at the top of a file.
  676. line = line.substring(1);
  677. }
  678. if (line !== null) {
  679. stringBuffer.append(line);
  680. }
  681. while ((line = input.readLine()) !== null) {
  682. stringBuffer.append(lineSeparator);
  683. stringBuffer.append(line);
  684. }
  685. //Make sure we return a JavaScript string and not a Java string.
  686. content = String(stringBuffer.toString()); //String
  687. } finally {
  688. input.close();
  689. }
  690. callback(content);
  691. };
  692. } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env &&
  693. typeof Components !== 'undefined' && Components.classes &&
  694. Components.interfaces)) {
  695. //Avert your gaze!
  696. Cc = Components.classes;
  697. Ci = Components.interfaces;
  698. Components.utils['import']('resource://gre/modules/FileUtils.jsm');
  699. xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc);
  700. text.get = function (url, callback) {
  701. var inStream, convertStream, fileObj,
  702. readData = {};
  703. if (xpcIsWindows) {
  704. url = url.replace(/\//g, '\\');
  705. }
  706. fileObj = new FileUtils.File(url);
  707. //XPCOM, you so crazy
  708. try {
  709. inStream = Cc['@mozilla.org/network/file-input-stream;1']
  710. .createInstance(Ci.nsIFileInputStream);
  711. inStream.init(fileObj, 1, 0, false);
  712. convertStream = Cc['@mozilla.org/intl/converter-input-stream;1']
  713. .createInstance(Ci.nsIConverterInputStream);
  714. convertStream.init(inStream, "utf-8", inStream.available(),
  715. Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
  716. convertStream.readString(inStream.available(), readData);
  717. convertStream.close();
  718. inStream.close();
  719. callback(readData.value);
  720. } catch (e) {
  721. throw new Error((fileObj && fileObj.path || '') + ': ' + e);
  722. }
  723. };
  724. }
  725. return text;
  726. });
  727. // Underscore.js 1.6.0
  728. // http://underscorejs.org
  729. // (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
  730. // Underscore may be freely distributed under the MIT license.
  731. (function() {
  732. // Baseline setup
  733. // --------------
  734. // Establish the root object, `window` in the browser, or `exports` on the server.
  735. var root = this;
  736. // Save the previous value of the `_` variable.
  737. var previousUnderscore = root._;
  738. // Establish the object that gets returned to break out of a loop iteration.
  739. var breaker = {};
  740. // Save bytes in the minified (but not gzipped) version:
  741. var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
  742. // Create quick reference variables for speed access to core prototypes.
  743. var
  744. push = ArrayProto.push,
  745. slice = ArrayProto.slice,
  746. concat = ArrayProto.concat,
  747. toString = ObjProto.toString,
  748. hasOwnProperty = ObjProto.hasOwnProperty;
  749. // All **ECMAScript 5** native function implementations that we hope to use
  750. // are declared here.
  751. var
  752. nativeForEach = ArrayProto.forEach,
  753. nativeMap = ArrayProto.map,
  754. nativeReduce = ArrayProto.reduce,
  755. nativeReduceRight = ArrayProto.reduceRight,
  756. nativeFilter = ArrayProto.filter,
  757. nativeEvery = ArrayProto.every,
  758. nativeSome = ArrayProto.some,
  759. nativeIndexOf = ArrayProto.indexOf,
  760. nativeLastIndexOf = ArrayProto.lastIndexOf,
  761. nativeIsArray = Array.isArray,
  762. nativeKeys = Object.keys,
  763. nativeBind = FuncProto.bind;
  764. // Create a safe reference to the Underscore object for use below.
  765. var _ = function(obj) {
  766. if (obj instanceof _) return obj;
  767. if (!(this instanceof _)) return new _(obj);
  768. this._wrapped = obj;
  769. };
  770. // Export the Underscore object for **Node.js**, with
  771. // backwards-compatibility for the old `require()` API. If we're in
  772. // the browser, add `_` as a global object via a string identifier,
  773. // for Closure Compiler "advanced" mode.
  774. if (typeof exports !== 'undefined') {
  775. if (typeof module !== 'undefined' && module.exports) {
  776. exports = module.exports = _;
  777. }
  778. exports._ = _;
  779. } else {
  780. root._ = _;
  781. }
  782. // Current version.
  783. _.VERSION = '1.6.0';
  784. // Collection Functions
  785. // --------------------
  786. // The cornerstone, an `each` implementation, aka `forEach`.
  787. // Handles objects with the built-in `forEach`, arrays, and raw objects.
  788. // Delegates to **ECMAScript 5**'s native `forEach` if available.
  789. var each = _.each = _.forEach = function(obj, iterator, context) {
  790. if (obj == null) return obj;
  791. if (nativeForEach && obj.forEach === nativeForEach) {
  792. obj.forEach(iterator, context);
  793. } else if (obj.length === +obj.length) {
  794. for (var i = 0, length = obj.length; i < length; i++) {
  795. if (iterator.call(context, obj[i], i, obj) === breaker) return;
  796. }
  797. } else {
  798. var keys = _.keys(obj);
  799. for (var i = 0, length = keys.length; i < length; i++) {
  800. if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
  801. }
  802. }
  803. return obj;
  804. };
  805. // Return the results of applying the iterator to each element.
  806. // Delegates to **ECMAScript 5**'s native `map` if available.
  807. _.map = _.collect = function(obj, iterator, context) {
  808. var results = [];
  809. if (obj == null) return results;
  810. if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
  811. each(obj, function(value, index, list) {
  812. results.push(iterator.call(context, value, index, list));
  813. });
  814. return results;
  815. };
  816. var reduceError = 'Reduce of empty array with no initial value';
  817. // **Reduce** builds up a single result from a list of values, aka `inject`,
  818. // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
  819. _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
  820. var initial = arguments.length > 2;
  821. if (obj == null) obj = [];
  822. if (nativeReduce && obj.reduce === nativeReduce) {
  823. if (context) iterator = _.bind(iterator, context);
  824. return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
  825. }
  826. each(obj, function(value, index, list) {
  827. if (!initial) {
  828. memo = value;
  829. initial = true;
  830. } else {
  831. memo = iterator.call(context, memo, value, index, list);
  832. }
  833. });
  834. if (!initial) throw new TypeError(reduceError);
  835. return memo;
  836. };
  837. // The right-associative version of reduce, also known as `foldr`.
  838. // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
  839. _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
  840. var initial = arguments.length > 2;
  841. if (obj == null) obj = [];
  842. if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
  843. if (context) iterator = _.bind(iterator, context);
  844. return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
  845. }
  846. var length = obj.length;
  847. if (length !== +length) {
  848. var keys = _.keys(obj);
  849. length = keys.length;
  850. }
  851. each(obj, function(value, index, list) {
  852. index = keys ? keys[--length] : --length;
  853. if (!initial) {
  854. memo = obj[index];
  855. initial = true;
  856. } else {
  857. memo = iterator.call(context, memo, obj[index], index, list);
  858. }
  859. });
  860. if (!initial) throw new TypeError(reduceError);
  861. return memo;
  862. };
  863. // Return the first value which passes a truth test. Aliased as `detect`.
  864. _.find = _.detect = function(obj, predicate, context) {
  865. var result;
  866. any(obj, function(value, index, list) {
  867. if (predicate.call(context, value, index, list)) {
  868. result = value;
  869. return true;
  870. }
  871. });
  872. return result;
  873. };
  874. // Return all the elements that pass a truth test.
  875. // Delegates to **ECMAScript 5**'s native `filter` if available.
  876. // Aliased as `select`.
  877. _.filter = _.select = function(obj, predicate, context) {
  878. var results = [];
  879. if (obj == null) return results;
  880. if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context);
  881. each(obj, function(value, index, list) {
  882. if (predicate.call(context, value, index, list)) results.push(value);
  883. });
  884. return results;
  885. };
  886. // Return all the elements for which a truth test fails.
  887. _.reject = function(obj, predicate, context) {
  888. return _.filter(obj, function(value, index, list) {
  889. return !predicate.call(context, value, index, list);
  890. }, context);
  891. };
  892. // Determine whether all of the elements match a truth test.
  893. // Delegates to **ECMAScript 5**'s native `every` if available.
  894. // Aliased as `all`.
  895. _.every = _.all = function(obj, predicate, context) {
  896. predicate || (predicate = _.identity);
  897. var result = true;
  898. if (obj == null) return result;
  899. if (nativeEvery && obj.every === nativeEvery) return obj.every(predicate, context);
  900. each(obj, function(value, index, list) {
  901. if (!(result = result && predicate.call(context, value, index, list))) return breaker;
  902. });
  903. return !!result;
  904. };
  905. // Determine if at least one element in the object matches a truth test.
  906. // Delegates to **ECMAScript 5**'s native `some` if available.
  907. // Aliased as `any`.
  908. var any = _.some = _.any = function(obj, predicate, context) {
  909. predicate || (predicate = _.identity);
  910. var result = false;
  911. if (obj == null) return result;
  912. if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context);
  913. each(obj, function(value, index, list) {
  914. if (result || (result = predicate.call(context, value, index, list))) return breaker;
  915. });
  916. return !!result;
  917. };
  918. // Determine if the array or object contains a given value (using `===`).
  919. // Aliased as `include`.
  920. _.contains = _.include = function(obj, target) {
  921. if (obj == null) return false;
  922. if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
  923. return any(obj, function(value) {
  924. return value === target;
  925. });
  926. };
  927. // Invoke a method (with arguments) on every item in a collection.
  928. _.invoke = function(obj, method) {
  929. var args = slice.call(arguments, 2);
  930. var isFunc = _.isFunction(method);
  931. return _.map(obj, function(value) {
  932. return (isFunc ? method : value[method]).apply(value, args);
  933. });
  934. };
  935. // Convenience version of a common use case of `map`: fetching a property.
  936. _.pluck = function(obj, key) {
  937. return _.map(obj, _.property(key));
  938. };
  939. // Convenience version of a common use case of `filter`: selecting only objects
  940. // containing specific `key:value` pairs.
  941. _.where = function(obj, attrs) {
  942. return _.filter(obj, _.matches(attrs));
  943. };
  944. // Convenience version of a common use case of `find`: getting the first object
  945. // containing specific `key:value` pairs.
  946. _.findWhere = function(obj, attrs) {
  947. return _.find(obj, _.matches(attrs));
  948. };
  949. // Return the maximum element or (element-based computation).
  950. // Can't optimize arrays of integers longer than 65,535 elements.
  951. // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
  952. _.max = function(obj, iterator, context) {
  953. if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
  954. return Math.max.apply(Math, obj);
  955. }
  956. var result = -Infinity, lastComputed = -Infinity;
  957. each(obj, function(value, index, list) {
  958. var computed = iterator ? iterator.call(context, value, index, list) : value;
  959. if (computed > lastComputed) {
  960. result = value;
  961. lastComputed = computed;
  962. }
  963. });
  964. return result;
  965. };
  966. // Return the minimum element (or element-based computation).
  967. _.min = function(obj, iterator, context) {
  968. if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
  969. return Math.min.apply(Math, obj);
  970. }
  971. var result = Infinity, lastComputed = Infinity;
  972. each(obj, function(value, index, list) {
  973. var computed = iterator ? iterator.call(context, value, index, list) : value;
  974. if (computed < lastComputed) {
  975. result = value;
  976. lastComputed = computed;
  977. }
  978. });
  979. return result;
  980. };
  981. // Shuffle an array, using the modern version of the
  982. // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
  983. _.shuffle = function(obj) {
  984. var rand;
  985. var index = 0;
  986. var shuffled = [];
  987. each(obj, function(value) {
  988. rand = _.random(index++);
  989. shuffled[index - 1] = shuffled[rand];
  990. shuffled[rand] = value;
  991. });
  992. return shuffled;
  993. };
  994. // Sample **n** random values from a collection.
  995. // If **n** is not specified, returns a single random element.
  996. // The internal `guard` argument allows it to work with `map`.
  997. _.sample = function(obj, n, guard) {
  998. if (n == null || guard) {
  999. if (obj.length !== +obj.length) obj = _.values(obj);
  1000. return obj[_.random(obj.length - 1)];
  1001. }
  1002. return _.shuffle(obj).slice(0, Math.max(0, n));
  1003. };
  1004. // An internal function to generate lookup iterators.
  1005. var lookupIterator = function(value) {
  1006. if (value == null) return _.identity;
  1007. if (_.isFunction(value)) return value;
  1008. return _.property(value);
  1009. };
  1010. // Sort the object's values by a criterion produced by an iterator.
  1011. _.sortBy = function(obj, iterator, context) {
  1012. iterator = lookupIterator(iterator);
  1013. return _.pluck(_.map(obj, function(value, index, list) {
  1014. return {
  1015. value: value,
  1016. index: index,
  1017. criteria: iterator.call(context, value, index, list)
  1018. };
  1019. }).sort(function(left, right) {
  1020. var a = left.criteria;
  1021. var b = right.criteria;
  1022. if (a !== b) {
  1023. if (a > b || a === void 0) return 1;
  1024. if (a < b || b === void 0) return -1;
  1025. }
  1026. return left.index - right.index;
  1027. }), 'value');
  1028. };
  1029. // An internal function used for aggregate "group by" operations.
  1030. var group = function(behavior) {
  1031. return function(obj, iterator, context) {
  1032. var result = {};
  1033. iterator = lookupIterator(iterator);
  1034. each(obj, function(value, index) {
  1035. var key = iterator.call(context, value, index, obj);
  1036. behavior(result, key, value);
  1037. });
  1038. return result;
  1039. };
  1040. };
  1041. // Groups the object's values by a criterion. Pass either a string attribute
  1042. // to group by, or a function that returns the criterion.
  1043. _.groupBy = group(function(result, key, value) {
  1044. _.has(result, key) ? result[key].push(value) : result[key] = [value];
  1045. });
  1046. // Indexes the object's values by a criterion, similar to `groupBy`, but for
  1047. // when you know that your index values will be unique.
  1048. _.indexBy = group(function(result, key, value) {
  1049. result[key] = value;
  1050. });
  1051. // Counts instances of an object that group by a certain criterion. Pass
  1052. // either a string attribute to count by, or a function that returns the
  1053. // criterion.
  1054. _.countBy = group(function(result, key) {
  1055. _.has(result, key) ? result[key]++ : result[key] = 1;
  1056. });
  1057. // Use a comparator function to figure out the smallest index at which
  1058. // an object should be inserted so as to maintain order. Uses binary search.
  1059. _.sortedIndex = function(array, obj, iterator, context) {
  1060. iterator = lookupIterator(iterator);
  1061. var value = iterator.call(context, obj);
  1062. var low = 0, high = array.length;
  1063. while (low < high) {
  1064. var mid = (low + high) >>> 1;
  1065. iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
  1066. }
  1067. return low;
  1068. };
  1069. // Safely create a real, live array from anything iterable.
  1070. _.toArray = function(obj) {
  1071. if (!obj) return [];
  1072. if (_.isArray(obj)) return slice.call(obj);
  1073. if (obj.length === +obj.length) return _.map(obj, _.identity);
  1074. return _.values(obj);
  1075. };
  1076. // Return the number of elements in an object.
  1077. _.size = function(obj) {
  1078. if (obj == null) return 0;
  1079. return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
  1080. };
  1081. // Array Functions
  1082. // ---------------
  1083. // Get the first element of an array. Passing **n** will return the first N
  1084. // values in the array. Aliased as `head` and `take`. The **guard** check
  1085. // allows it to work with `_.map`.
  1086. _.first = _.head = _.take = function(array, n, guard) {
  1087. if (array == null) return void 0;
  1088. if ((n == null) || guard) return array[0];
  1089. if (n < 0) return [];
  1090. return slice.call(array, 0, n);
  1091. };
  1092. // Returns everything but the last entry of the array. Especially useful on
  1093. // the arguments object. Passing **n** will return all the values in
  1094. // the array, excluding the last N. The **guard** check allows it to work with
  1095. // `_.map`.
  1096. _.initial = function(array, n, guard) {
  1097. return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
  1098. };
  1099. // Get the last element of an array. Passing **n** will return the last N
  1100. // values in the array. The **guard** check allows it to work with `_.map`.
  1101. _.last = function(array, n, guard) {
  1102. if (array == null) return void 0;
  1103. if ((n == null) || guard) return array[array.length - 1];
  1104. return slice.call(array, Math.max(array.length - n, 0));
  1105. };
  1106. // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
  1107. // Especially useful on the arguments object. Passing an **n** will return
  1108. // the rest N values in the array. The **guard**
  1109. // check allows it to work with `_.map`.
  1110. _.rest = _.tail = _.drop = function(array, n, guard) {
  1111. return slice.call(array, (n == null) || guard ? 1 : n);
  1112. };
  1113. // Trim out all falsy values from an array.
  1114. _.compact = function(array) {
  1115. return _.filter(array, _.identity);
  1116. };
  1117. // Internal implementation of a recursive `flatten` function.
  1118. var flatten = function(input, shallow, output) {
  1119. if (shallow && _.every(input, _.isArray)) {
  1120. return concat.apply(output, input);
  1121. }
  1122. each(input, function(value) {
  1123. if (_.isArray(value) || _.isArguments(value)) {
  1124. shallow ? push.apply(output, value) : flatten(value, shallow, output);
  1125. } else {
  1126. output.push(value);
  1127. }
  1128. });
  1129. return output;
  1130. };
  1131. // Flatten out an array, either recursively (by default), or just one level.
  1132. _.flatten = function(array, shallow) {
  1133. return flatten(array, shallow, []);
  1134. };
  1135. // Return a version of the array that does not contain the specified value(s).
  1136. _.without = function(array) {
  1137. return _.difference(array, slice.call(arguments, 1));
  1138. };
  1139. // Split an array into two arrays: one whose elements all satisfy the given
  1140. // predicate, and one whose elements all do not satisfy the predicate.
  1141. _.partition = function(array, predicate) {
  1142. var pass = [], fail = [];
  1143. each(array, function(elem) {
  1144. (predicate(elem) ? pass : fail).push(elem);
  1145. });
  1146. return [pass, fail];
  1147. };
  1148. // Produce a duplicate-free version of the array. If the array has already
  1149. // been sorted, you have the option of using a faster algorithm.
  1150. // Aliased as `unique`.
  1151. _.uniq = _.unique = function(array, isSorted, iterator, context) {
  1152. if (_.isFunction(isSorted)) {
  1153. context = iterator;
  1154. iterator = isSorted;
  1155. isSorted = false;
  1156. }
  1157. var initial = iterator ? _.map(array, iterator, context) : array;
  1158. var results = [];
  1159. var seen = [];
  1160. each(initial, function(value, index) {
  1161. if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
  1162. seen.push(value);
  1163. results.push(array[index]);
  1164. }
  1165. });
  1166. return results;
  1167. };
  1168. // Produce an array that contains the union: each distinct element from all of
  1169. // the passed-in arrays.
  1170. _.union = function() {
  1171. return _.uniq(_.flatten(arguments, true));
  1172. };
  1173. // Produce an array that contains every item shared between all the
  1174. // passed-in arrays.
  1175. _.intersection = function(array) {
  1176. var rest = slice.call(arguments, 1);
  1177. return _.filter(_.uniq(array), function(item) {
  1178. return _.every(rest, function(other) {
  1179. return _.contains(other, item);
  1180. });
  1181. });
  1182. };
  1183. // Take the difference between one array and a number of other arrays.
  1184. // Only the elements present in just the first array will remain.
  1185. _.difference = function(array) {
  1186. var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
  1187. return _.filter(array, function(value){ return !_.contains(rest, value); });
  1188. };
  1189. // Zip together multiple lists into a single array -- elements that share
  1190. // an index go together.
  1191. _.zip = function() {
  1192. var length = _.max(_.pluck(arguments, 'length').concat(0));
  1193. var results = new Array(length);
  1194. for (var i = 0; i < length; i++) {
  1195. results[i] = _.pluck(arguments, '' + i);
  1196. }
  1197. return results;
  1198. };
  1199. // Converts lists into objects. Pass either a single array of `[key, value]`
  1200. // pairs, or two parallel arrays of the same length -- one of keys, and one of
  1201. // the corresponding values.
  1202. _.object = function(list, values) {
  1203. if (list == null) return {};
  1204. var result = {};
  1205. for (var i = 0, length = list.length; i < length; i++) {
  1206. if (values) {
  1207. result[list[i]] = values[i];
  1208. } else {
  1209. result[list[i][0]] = list[i][1];
  1210. }
  1211. }
  1212. return result;
  1213. };
  1214. // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
  1215. // we need this function. Return the position of the first occurrence of an
  1216. // item in an array, or -1 if the item is not included in the array.
  1217. // Delegates to **ECMAScript 5**'s native `indexOf` if available.
  1218. // If the array is large and already in sort order, pass `true`
  1219. // for **isSorted** to use binary search.
  1220. _.indexOf = function(array, item, isSorted) {
  1221. if (array == null) return -1;
  1222. var i = 0, length = array.length;
  1223. if (isSorted) {
  1224. if (typeof isSorted == 'number') {
  1225. i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
  1226. } else {
  1227. i = _.sortedIndex(array, item);
  1228. return array[i] === item ? i : -1;
  1229. }
  1230. }
  1231. if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
  1232. for (; i < length; i++) if (array[i] === item) return i;
  1233. return -1;
  1234. };
  1235. // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
  1236. _.lastIndexOf = function(array, item, from) {
  1237. if (array == null) return -1;
  1238. var hasIndex = from != null;
  1239. if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
  1240. return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
  1241. }
  1242. var i = (hasIndex ? from : array.length);
  1243. while (i--) if (array[i] === item) return i;
  1244. return -1;
  1245. };
  1246. // Generate an integer Array containing an arithmetic progression. A port of
  1247. // the native Python `range()` function. See
  1248. // [the Python documentation](http://docs.python.org/library/functions.html#range).
  1249. _.range = function(start, stop, step) {
  1250. if (arguments.length <= 1) {
  1251. stop = start || 0;
  1252. start = 0;
  1253. }
  1254. step = arguments[2] || 1;
  1255. var length = Math.max(Math.ceil((stop - start) / step), 0);
  1256. var idx = 0;
  1257. var range = new Array(length);
  1258. while(idx < length) {
  1259. range[idx++] = start;
  1260. start += step;
  1261. }
  1262. return range;
  1263. };
  1264. // Function (ahem) Functions
  1265. // ------------------
  1266. // Reusable constructor function for prototype setting.
  1267. var ctor = function(){};
  1268. // Create a function bound to a given object (assigning `this`, and arguments,
  1269. // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
  1270. // available.
  1271. _.bind = function(func, context) {
  1272. var args, bound;
  1273. if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
  1274. if (!_.isFunction(func)) throw new TypeError;
  1275. args = slice.call(arguments, 2);
  1276. return bound = function() {
  1277. if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
  1278. ctor.prototype = func.prototype;
  1279. var self = new ctor;
  1280. ctor.prototype = null;
  1281. var result = func.apply(self, args.concat(slice.call(arguments)));
  1282. if (Object(result) === result) return result;
  1283. return self;
  1284. };
  1285. };
  1286. // Partially apply a function by creating a version that has had some of its
  1287. // arguments pre-filled, without changing its dynamic `this` context. _ acts
  1288. // as a placeholder, allowing any combination of arguments to be pre-filled.
  1289. _.partial = function(func) {
  1290. var boundArgs = slice.call(arguments, 1);
  1291. return function() {
  1292. var position = 0;
  1293. var args = boundArgs.slice();
  1294. for (var i = 0, length = args.length; i < length; i++) {
  1295. if (args[i] === _) args[i] = arguments[position++];
  1296. }
  1297. while (position < arguments.length) args.push(arguments[position++]);
  1298. return func.apply(this, args);
  1299. };
  1300. };
  1301. // Bind a number of an object's methods to that object. Remaining arguments
  1302. // are the method names to be bound. Useful for ensuring that all callbacks
  1303. // defined on an object belong to it.
  1304. _.bindAll = function(obj) {
  1305. var funcs = slice.call(arguments, 1);
  1306. if (funcs.length === 0) throw new Error('bindAll must be passed function names');
  1307. each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
  1308. return obj;
  1309. };
  1310. // Memoize an expensive function by storing its results.
  1311. _.memoize = function(func, hasher) {
  1312. var memo = {};
  1313. hasher || (hasher = _.identity);
  1314. return function() {
  1315. var key = hasher.apply(this, arguments);
  1316. return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
  1317. };
  1318. };
  1319. // Delays a function for the given number of milliseconds, and then calls
  1320. // it with the arguments supplied.
  1321. _.delay = function(func, wait) {
  1322. var args = slice.call(arguments, 2);
  1323. return setTimeout(function(){ return func.apply(null, args); }, wait);
  1324. };
  1325. // Defers a function, scheduling it to run after the current call stack has
  1326. // cleared.
  1327. _.defer = function(func) {
  1328. return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
  1329. };
  1330. // Returns a function, that, when invoked, will only be triggered at most once
  1331. // during a given window of time. Normally, the throttled function will run
  1332. // as much as it can, without ever going more than once per `wait` duration;
  1333. // but if you'd like to disable the execution on the leading edge, pass
  1334. // `{leading: false}`. To disable execution on the trailing edge, ditto.
  1335. _.throttle = function(func, wait, options) {
  1336. var context, args, result;
  1337. var timeout = null;
  1338. var previous = 0;
  1339. options || (options = {});
  1340. var later = function() {
  1341. previous = options.leading === false ? 0 : _.now();
  1342. timeout = null;
  1343. result = func.apply(context, args);
  1344. context = args = null;
  1345. };
  1346. return function() {
  1347. var now = _.now();
  1348. if (!previous && options.leading === false) previous = now;
  1349. var remaining = wait - (now - previous);
  1350. context = this;
  1351. args = arguments;
  1352. if (remaining <= 0) {
  1353. clearTimeout(timeout);
  1354. timeout = null;
  1355. previous = now;
  1356. result = func.apply(context, args);
  1357. context = args = null;
  1358. } else if (!timeout && options.trailing !== false) {
  1359. timeout = setTimeout(later, remaining);
  1360. }
  1361. return result;
  1362. };
  1363. };
  1364. // Returns a function, that, as long as it continues to be invoked, will not
  1365. // be triggered. The function will be called after it stops being called for
  1366. // N milliseconds. If `immediate` is passed, trigger the function on the
  1367. // leading edge, instead of the trailing.
  1368. _.debounce = function(func, wait, immediate) {
  1369. var timeout, args, context, timestamp, result;
  1370. var later = function() {
  1371. var last = _.now() - timestamp;
  1372. if (last < wait) {
  1373. timeout = setTimeout(later, wait - last);
  1374. } else {
  1375. timeout = null;
  1376. if (!immediate) {
  1377. result = func.apply(context, args);
  1378. context = args = null;
  1379. }
  1380. }
  1381. };
  1382. return function() {
  1383. context = this;
  1384. args = arguments;
  1385. timestamp = _.now();
  1386. var callNow = immediate && !timeout;
  1387. if (!timeout) {
  1388. timeout = setTimeout(later, wait);
  1389. }
  1390. if (callNow) {
  1391. result = func.apply(context, args);
  1392. context = args = null;
  1393. }
  1394. return result;
  1395. };
  1396. };
  1397. // Returns a function that will be executed at most one time, no matter how
  1398. // often you call it. Useful for lazy initialization.
  1399. _.once = function(func) {
  1400. var ran = false, memo;
  1401. return function() {
  1402. if (ran) return memo;
  1403. ran = true;
  1404. memo = func.apply(this, arguments);
  1405. func = null;
  1406. return memo;
  1407. };
  1408. };
  1409. // Returns the first function passed as an argument to the second,
  1410. // allowing you to adjust arguments, run code before and after, and
  1411. // conditionally execute the original function.
  1412. _.wrap = function(func, wrapper) {
  1413. return _.partial(wrapper, func);
  1414. };
  1415. // Returns a function that is the composition of a list of functions, each
  1416. // consuming the return value of the function that follows.
  1417. _.compose = function() {
  1418. var funcs = arguments;
  1419. return function() {
  1420. var args = arguments;
  1421. for (var i = funcs.length - 1; i >= 0; i--) {
  1422. args = [funcs[i].apply(this, args)];
  1423. }
  1424. return args[0];
  1425. };
  1426. };
  1427. // Returns a function that will only be executed after being called N times.
  1428. _.after = function(times, func) {
  1429. return function() {
  1430. if (--times < 1) {
  1431. return func.apply(this, arguments);
  1432. }
  1433. };
  1434. };
  1435. // Object Functions
  1436. // ----------------
  1437. // Retrieve the names of an object's properties.
  1438. // Delegates to **ECMAScript 5**'s native `Object.keys`
  1439. _.keys = function(obj) {
  1440. if (!_.isObject(obj)) return [];
  1441. if (nativeKeys) return nativeKeys(obj);
  1442. var keys = [];
  1443. for (var key in obj) if (_.has(obj, key)) keys.push(key);
  1444. return keys;
  1445. };
  1446. // Retrieve the values of an object's properties.
  1447. _.values = function(obj) {
  1448. var keys = _.keys(obj);
  1449. var length = keys.length;
  1450. var values = new Array(length);
  1451. for (var i = 0; i < length; i++) {
  1452. values[i] = obj[keys[i]];
  1453. }
  1454. return values;
  1455. };
  1456. // Convert an object into a list of `[key, value]` pairs.
  1457. _.pairs = function(obj) {
  1458. var keys = _.keys(obj);
  1459. var length = keys.length;
  1460. var pairs = new Array(length);
  1461. for (var i = 0; i < length; i++) {
  1462. pairs[i] = [keys[i], obj[keys[i]]];
  1463. }
  1464. return pairs;
  1465. };
  1466. // Invert the keys and values of an object. The values must be serializable.
  1467. _.invert = function(obj) {
  1468. var result = {};
  1469. var keys = _.keys(obj);
  1470. for (var i = 0, length = keys.length; i < length; i++) {
  1471. result[obj[keys[i]]] = keys[i];
  1472. }
  1473. return result;
  1474. };
  1475. // Return a sorted list of the function names available on the object.
  1476. // Aliased as `methods`
  1477. _.functions = _.methods = function(obj) {
  1478. var names = [];
  1479. for (var key in obj) {
  1480. if (_.isFunction(obj[key])) names.push(key);
  1481. }
  1482. return names.sort();
  1483. };
  1484. // Extend a given object with all the properties in passed-in object(s).
  1485. _.extend = function(obj) {
  1486. each(slice.call(arguments, 1), function(source) {
  1487. if (source) {
  1488. for (var prop in source) {
  1489. obj[prop] = source[prop];
  1490. }
  1491. }
  1492. });
  1493. return obj;
  1494. };
  1495. // Return a copy of the object only containing the whitelisted properties.
  1496. _.pick = function(obj) {
  1497. var copy = {};
  1498. var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
  1499. each(keys, function(key) {
  1500. if (key in obj) copy[key] = obj[key];
  1501. });
  1502. return copy;
  1503. };
  1504. // Return a copy of the object without the blacklisted properties.
  1505. _.omit = function(obj) {
  1506. var copy = {};
  1507. var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
  1508. for (var key in obj) {
  1509. if (!_.contains(keys, key)) copy[key] = obj[key];
  1510. }
  1511. return copy;
  1512. };
  1513. // Fill in a given object with default properties.
  1514. _.defaults = function(obj) {
  1515. each(slice.call(arguments, 1), function(source) {
  1516. if (source) {
  1517. for (var prop in source) {
  1518. if (obj[prop] === void 0) obj[prop] = source[prop];
  1519. }
  1520. }
  1521. });
  1522. return obj;
  1523. };
  1524. // Create a (shallow-cloned) duplicate of an object.
  1525. _.clone = function(obj) {
  1526. if (!_.isObject(obj)) return obj;
  1527. return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
  1528. };
  1529. // Invokes interceptor with the obj, and then returns obj.
  1530. // The primary purpose of this method is to "tap into" a method chain, in
  1531. // order to perform operations on intermediate results within the chain.
  1532. _.tap = function(obj, interceptor) {
  1533. interceptor(obj);
  1534. return obj;
  1535. };
  1536. // Internal recursive comparison function for `isEqual`.
  1537. var eq = function(a, b, aStack, bStack) {
  1538. // Identical objects are equal. `0 === -0`, but they aren't identical.
  1539. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
  1540. if (a === b) return a !== 0 || 1 / a == 1 / b;
  1541. // A strict comparison is necessary because `null == undefined`.
  1542. if (a == null || b == null) return a === b;
  1543. // Unwrap any wrapped objects.
  1544. if (a instanceof _) a = a._wrapped;
  1545. if (b instanceof _) b = b._wrapped;
  1546. // Compare `[[Class]]` names.
  1547. var className = toString.call(a);
  1548. if (className != toString.call(b)) return false;
  1549. switch (className) {
  1550. // Strings, numbers, dates, and booleans are compared by value.
  1551. case '[object String]':
  1552. // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
  1553. // equivalent to `new String("5")`.
  1554. return a == String(b);
  1555. case '[object Number]':
  1556. // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
  1557. // other numeric values.
  1558. return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
  1559. case '[object Date]':
  1560. case '[object Boolean]':
  1561. // Coerce dates and booleans to numeric primitive values. Dates are compared by their
  1562. // millisecond representations. Note that invalid dates with millisecond representations
  1563. // of `NaN` are not equivalent.
  1564. return +a == +b;
  1565. // RegExps are compared by their source patterns and flags.
  1566. case '[object RegExp]':
  1567. return a.source == b.source &&
  1568. a.global == b.global &&
  1569. a.multiline == b.multiline &&
  1570. a.ignoreCase == b.ignoreCase;
  1571. }
  1572. if (typeof a != 'object' || typeof b != 'object') return false;
  1573. // Assume equality for cyclic structures. The algorithm for detecting cyclic
  1574. // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
  1575. var length = aStack.length;
  1576. while (length--) {
  1577. // Linear search. Performance is inversely proportional to the number of
  1578. // unique nested structures.
  1579. if (aStack[length] == a) return bStack[length] == b;
  1580. }
  1581. // Objects with different constructors are not equivalent, but `Object`s
  1582. // from different frames are.
  1583. var aCtor = a.constructor, bCtor = b.constructor;
  1584. if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
  1585. _.isFunction(bCtor) && (bCtor instanceof bCtor))
  1586. && ('constructor' in a && 'constructor' in b)) {
  1587. return false;
  1588. }
  1589. // Add the first object to the stack of traversed objects.
  1590. aStack.push(a);
  1591. bStack.push(b);
  1592. var size = 0, result = true;
  1593. // Recursively compare objects and arrays.
  1594. if (className == '[object Array]') {
  1595. // Compare array lengths to determine if a deep comparison is necessary.
  1596. size = a.length;
  1597. result = size == b.length;
  1598. if (result) {
  1599. // Deep compare the contents, ignoring non-numeric properties.
  1600. while (size--) {
  1601. if (!(result = eq(a[size], b[size], aStack, bStack))) break;
  1602. }
  1603. }
  1604. } else {
  1605. // Deep compare objects.
  1606. for (var key in a) {
  1607. if (_.has(a, key)) {
  1608. // Count the expected number of properties.
  1609. size++;
  1610. // Deep compare each member.
  1611. if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
  1612. }
  1613. }
  1614. // Ensure that both objects contain the same number of properties.
  1615. if (result) {
  1616. for (key in b) {
  1617. if (_.has(b, key) && !(size--)) break;
  1618. }
  1619. result = !size;
  1620. }
  1621. }
  1622. // Remove the first object from the stack of traversed objects.
  1623. aStack.pop();
  1624. bStack.pop();
  1625. return result;
  1626. };
  1627. // Perform a deep comparison to check if two objects are equal.
  1628. _.isEqual = function(a, b) {
  1629. return eq(a, b, [], []);
  1630. };
  1631. // Is a given array, string, or object empty?
  1632. // An "empty" object has no enumerable own-properties.
  1633. _.isEmpty = function(obj) {
  1634. if (obj == null) return true;
  1635. if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
  1636. for (var key in obj) if (_.has(obj, key)) return false;
  1637. return true;
  1638. };
  1639. // Is a given value a DOM element?
  1640. _.isElement = function(obj) {
  1641. return !!(obj && obj.nodeType === 1);
  1642. };
  1643. // Is a given value an array?
  1644. // Delegates to ECMA5's native Array.isArray
  1645. _.isArray = nativeIsArray || function(obj) {
  1646. return toString.call(obj) == '[object Array]';
  1647. };
  1648. // Is a given variable an object?
  1649. _.isObject = function(obj) {
  1650. return obj === Object(obj);
  1651. };
  1652. // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
  1653. each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
  1654. _['is' + name] = function(obj) {
  1655. return toString.call(obj) == '[object ' + name + ']';
  1656. };
  1657. });
  1658. // Define a fallback version of the method in browsers (ahem, IE), where
  1659. // there isn't any inspectable "Arguments" type.
  1660. if (!_.isArguments(arguments)) {
  1661. _.isArguments = function(obj) {
  1662. return !!(obj && _.has(obj, 'callee'));
  1663. };
  1664. }
  1665. // Optimize `isFunction` if appropriate.
  1666. if (typeof (/./) !== 'function') {
  1667. _.isFunction = function(obj) {
  1668. return typeof obj === 'function';
  1669. };
  1670. }
  1671. // Is a given object a finite number?
  1672. _.isFinite = function(obj) {
  1673. return isFinite(obj) && !isNaN(parseFloat(obj));
  1674. };
  1675. // Is the given value `NaN`? (NaN is the only number which does not equal itself).
  1676. _.isNaN = function(obj) {
  1677. return _.isNumber(obj) && obj != +obj;
  1678. };
  1679. // Is a given value a boolean?
  1680. _.isBoolean = function(obj) {
  1681. return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
  1682. };
  1683. // Is a given value equal to null?
  1684. _.isNull = function(obj) {
  1685. return obj === null;
  1686. };
  1687. // Is a given variable undefined?
  1688. _.isUndefined = function(obj) {
  1689. return obj === void 0;
  1690. };
  1691. // Shortcut function for checking if an object has a given property directly
  1692. // on itself (in other words, not on a prototype).
  1693. _.has = function(obj, key) {
  1694. return hasOwnProperty.call(obj, key);
  1695. };
  1696. // Utility Functions
  1697. // -----------------
  1698. // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
  1699. // previous owner. Returns a reference to the Underscore object.
  1700. _.noConflict = function() {
  1701. root._ = previousUnderscore;
  1702. return this;
  1703. };
  1704. // Keep the identity function around for default iterators.
  1705. _.identity = function(value) {
  1706. return value;
  1707. };
  1708. _.constant = function(value) {
  1709. return function () {
  1710. return value;
  1711. };
  1712. };
  1713. _.property = function(key) {
  1714. return function(obj) {
  1715. return obj[key];
  1716. };
  1717. };
  1718. // Returns a predicate for checking whether an object has a given set of `key:value` pairs.
  1719. _.matches = function(attrs) {
  1720. return function(obj) {
  1721. if (obj === attrs) return true; //avoid comparing an object to itself.
  1722. for (var key in attrs) {
  1723. if (attrs[key] !== obj[key])
  1724. return false;
  1725. }
  1726. return true;
  1727. }
  1728. };
  1729. // Run a function **n** times.
  1730. _.times = function(n, iterator, context) {
  1731. var accum = Array(Math.max(0, n));
  1732. for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
  1733. return accum;
  1734. };
  1735. // Return a random integer between min and max (inclusive).
  1736. _.random = function(min, max) {
  1737. if (max == null) {
  1738. max = min;
  1739. min = 0;
  1740. }
  1741. return min + Math.floor(Math.random() * (max - min + 1));
  1742. };
  1743. // A (possibly faster) way to get the current timestamp as an integer.
  1744. _.now = Date.now || function() { return new Date().getTime(); };
  1745. // List of HTML entities for escaping.
  1746. var entityMap = {
  1747. escape: {
  1748. '&': '&amp;',
  1749. '<': '&lt;',
  1750. '>': '&gt;',
  1751. '"': '&quot;',
  1752. "'": '&#x27;'
  1753. }
  1754. };
  1755. entityMap.unescape = _.invert(entityMap.escape);
  1756. // Regexes containing the keys and values listed immediately above.
  1757. var entityRegexes = {
  1758. escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
  1759. unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
  1760. };
  1761. // Functions for escaping and unescaping strings to/from HTML interpolation.
  1762. _.each(['escape', 'unescape'], function(method) {
  1763. _[method] = function(string) {
  1764. if (string == null) return '';
  1765. return ('' + string).replace(entityRegexes[method], function(match) {
  1766. return entityMap[method][match];
  1767. });
  1768. };
  1769. });
  1770. // If the value of the named `property` is a function then invoke it with the
  1771. // `object` as context; otherwise, return it.
  1772. _.result = function(object, property) {
  1773. if (object == null) return void 0;
  1774. var value = object[property];
  1775. return _.isFunction(value) ? value.call(object) : value;
  1776. };
  1777. // Add your own custom functions to the Underscore object.
  1778. _.mixin = function(obj) {
  1779. each(_.functions(obj), function(name) {
  1780. var func = _[name] = obj[name];
  1781. _.prototype[name] = function() {
  1782. var args = [this._wrapped];
  1783. push.apply(args, arguments);
  1784. return result.call(this, func.apply(_, args));
  1785. };
  1786. });
  1787. };
  1788. // Generate a unique integer id (unique within the entire client session).
  1789. // Useful for temporary DOM ids.
  1790. var idCounter = 0;
  1791. _.uniqueId = function(prefix) {
  1792. var id = ++idCounter + '';
  1793. return prefix ? prefix + id : id;
  1794. };
  1795. // By default, Underscore uses ERB-style template delimiters, change the
  1796. // following template settings to use alternative delimiters.
  1797. _.templateSettings = {
  1798. evaluate : /<%([\s\S]+?)%>/g,
  1799. interpolate : /<%=([\s\S]+?)%>/g,
  1800. escape : /<%-([\s\S]+?)%>/g
  1801. };
  1802. // When customizing `templateSettings`, if you don't want to define an
  1803. // interpolation, evaluation or escaping regex, we need one that is
  1804. // guaranteed not to match.
  1805. var noMatch = /(.)^/;
  1806. // Certain characters need to be escaped so that they can be put into a
  1807. // string literal.
  1808. var escapes = {
  1809. "'": "'",
  1810. '\\': '\\',
  1811. '\r': 'r',
  1812. '\n': 'n',
  1813. '\t': 't',
  1814. '\u2028': 'u2028',
  1815. '\u2029': 'u2029'
  1816. };
  1817. var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
  1818. // JavaScript micro-templating, similar to John Resig's implementation.
  1819. // Underscore templating handles arbitrary delimiters, preserves whitespace,
  1820. // and correctly escapes quotes within interpolated code.
  1821. _.template = function(text, data, settings) {
  1822. var render;
  1823. settings = _.defaults({}, settings, _.templateSettings);
  1824. // Combine delimiters into one regular expression via alternation.
  1825. var matcher = new RegExp([
  1826. (settings.escape || noMatch).source,
  1827. (settings.interpolate || noMatch).source,
  1828. (settings.evaluate || noMatch).source
  1829. ].join('|') + '|$', 'g');
  1830. // Compile the template source, escaping string literals appropriately.
  1831. var index = 0;
  1832. var source = "__p+='";
  1833. text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
  1834. source += text.slice(index, offset)
  1835. .replace(escaper, function(match) { return '\\' + escapes[match]; });
  1836. if (escape) {
  1837. source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
  1838. }
  1839. if (interpolate) {
  1840. source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
  1841. }
  1842. if (evaluate) {
  1843. source += "';\n" + evaluate + "\n__p+='";
  1844. }
  1845. index = offset + match.length;
  1846. return match;
  1847. });
  1848. source += "';\n";
  1849. // If a variable is not specified, place data values in local scope.
  1850. if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
  1851. source = "var __t,__p='',__j=Array.prototype.join," +
  1852. "print=function(){__p+=__j.call(arguments,'');};\n" +
  1853. source + "return __p;\n";
  1854. try {
  1855. render = new Function(settings.variable || 'obj', '_', source);
  1856. } catch (e) {
  1857. e.source = source;
  1858. throw e;
  1859. }
  1860. if (data) return render(data, _);
  1861. var template = function(data) {
  1862. return render.call(this, data, _);
  1863. };
  1864. // Provide the compiled function source as a convenience for precompilation.
  1865. template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
  1866. return template;
  1867. };
  1868. // Add a "chain" function, which will delegate to the wrapper.
  1869. _.chain = function(obj) {
  1870. return _(obj).chain();
  1871. };
  1872. // OOP
  1873. // ---------------
  1874. // If Underscore is called as a function, it returns a wrapped object that
  1875. // can be used OO-style. This wrapper holds altered versions of all the
  1876. // underscore functions. Wrapped objects may be chained.
  1877. // Helper function to continue chaining intermediate results.
  1878. var result = function(obj) {
  1879. return this._chain ? _(obj).chain() : obj;
  1880. };
  1881. // Add all of the Underscore functions to the wrapper object.
  1882. _.mixin(_);
  1883. // Add all mutator Array functions to the wrapper.
  1884. each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
  1885. var method = ArrayProto[name];
  1886. _.prototype[name] = function() {
  1887. var obj = this._wrapped;
  1888. method.apply(obj, arguments);
  1889. if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
  1890. return result.call(this, obj);
  1891. };
  1892. });
  1893. // Add all accessor Array functions to the wrapper.
  1894. each(['concat', 'join', 'slice'], function(name) {
  1895. var method = ArrayProto[name];
  1896. _.prototype[name] = function() {
  1897. return result.call(this, method.apply(this._wrapped, arguments));
  1898. };
  1899. });
  1900. _.extend(_.prototype, {
  1901. // Start chaining a wrapped Underscore object.
  1902. chain: function() {
  1903. this._chain = true;
  1904. return this;
  1905. },
  1906. // Extracts the result from a wrapped and chained object.
  1907. value: function() {
  1908. return this._wrapped;
  1909. }
  1910. });
  1911. // AMD registration happens at the end for compatibility with AMD loaders
  1912. // that may not enforce next-turn semantics on modules. Even though general
  1913. // practice for AMD registration is to be anonymous, underscore registers
  1914. // as a named module because, like jQuery, it is a base library that is
  1915. // popular enough to be bundled in a third party lib, but not be part of
  1916. // an AMD load request. Those cases could generate an error when an
  1917. // anonymous define() is called outside of a loader request.
  1918. if (typeof define === 'function' && define.amd) {
  1919. define('underscore', [], function() {
  1920. return _;
  1921. });
  1922. }
  1923. }).call(this);
  1924. // RequireJS UnderscoreJS template plugin
  1925. // http://github.com/jfparadis/requirejs-tpl
  1926. //
  1927. // An alternative to http://github.com/ZeeAgency/requirejs-tpl
  1928. //
  1929. // Using UnderscoreJS micro-templates at http://underscorejs.org/#template
  1930. // Using and RequireJS text.js at http://requirejs.org/docs/api.html#text
  1931. // @author JF Paradis
  1932. // @version 0.0.2
  1933. //
  1934. // Released under the MIT license
  1935. //
  1936. // Usage:
  1937. // require(['backbone', 'tpl!mytemplate'], function (Backbone, mytemplate) {
  1938. // return Backbone.View.extend({
  1939. // initialize: function(){
  1940. // this.render();
  1941. // },
  1942. // render: function(){
  1943. // this.$el.html(mytemplate({message: 'hello'}));
  1944. // });
  1945. // });
  1946. //
  1947. // Configuration: (optional)
  1948. // require.config({
  1949. // tpl: {
  1950. // extension: '.tpl' // default = '.html'
  1951. // }
  1952. // });
  1953. /*jslint nomen: true */
  1954. /*global define: false */
  1955. define('tpl',['text', 'underscore'], function (text, _) {
  1956. var buildMap = {},
  1957. buildTemplateSource = "define('{pluginName}!{moduleName}', function () { return {source}; });\n";
  1958. return {
  1959. version: '0.0.2',
  1960. load: function (moduleName, parentRequire, onload, config) {
  1961. if (config.tpl && config.tpl.templateSettings) {
  1962. _.templateSettings = config.tpl.templateSettings;
  1963. }
  1964. if (buildMap[moduleName]) {
  1965. onload(buildMap[moduleName]);
  1966. } else {
  1967. var ext = (config.tpl && config.tpl.extension) || '.html';
  1968. var path = (config.tpl && config.tpl.path) || '';
  1969. text.load(path + moduleName + ext, parentRequire, function (source) {
  1970. buildMap[moduleName] = _.template(source);
  1971. onload(buildMap[moduleName]);
  1972. }, config);
  1973. }
  1974. },
  1975. write: function (pluginName, moduleName, write) {
  1976. var build = buildMap[moduleName],
  1977. source = build && build.source;
  1978. if (source) {
  1979. write.asModule(pluginName + '!' + moduleName,
  1980. buildTemplateSource
  1981. .replace('{pluginName}', pluginName)
  1982. .replace('{moduleName}', moduleName)
  1983. .replace('{source}', source));
  1984. }
  1985. }
  1986. };
  1987. });
  1988. define('tpl!action', [],function () { return function(obj){
  1989. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  1990. with(obj||{}){
  1991. __p+='<div class="chat-message '+
  1992. ((__t=(extra_classes))==null?'':__t)+
  1993. '">\n <span class="chat-message-'+
  1994. ((__t=(sender))==null?'':__t)+
  1995. '">'+
  1996. ((__t=(time))==null?'':__t)+
  1997. ' **'+
  1998. ((__t=(username))==null?'':__t)+
  1999. ' </span>\n <span class="chat-message-content">'+
  2000. ((__t=(message))==null?'':__t)+
  2001. '</span>\n</div>\n';
  2002. }
  2003. return __p;
  2004. }; });
  2005. define('tpl!add_contact_dropdown', [],function () { return function(obj){
  2006. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2007. with(obj||{}){
  2008. __p+='<dl class="add-converse-contact dropdown">\n <dt id="xmpp-contact-search" class="fancy-dropdown">\n <a class="toggle-xmpp-contact-form" href="#"\n title="'+
  2009. ((__t=(label_click_to_chat))==null?'':__t)+
  2010. '">\n <span class="icon-plus"></span>'+
  2011. ((__t=(label_add_contact))==null?'':__t)+
  2012. '</a>\n </dt>\n <dd class="search-xmpp" style="display:none"><ul></ul></dd>\n</dl>\n';
  2013. }
  2014. return __p;
  2015. }; });
  2016. define('tpl!add_contact_form', [],function () { return function(obj){
  2017. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2018. with(obj||{}){
  2019. __p+='<li>\n <form class="add-xmpp-contact">\n <input type="text"\n name="identifier"\n class="username"\n placeholder="'+
  2020. ((__t=(label_contact_username))==null?'':__t)+
  2021. '"/>\n <button type="submit">'+
  2022. ((__t=(label_add))==null?'':__t)+
  2023. '</button>\n </form>\n</li>\n';
  2024. }
  2025. return __p;
  2026. }; });
  2027. define('tpl!change_status_message', [],function () { return function(obj){
  2028. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2029. with(obj||{}){
  2030. __p+='<form id="set-custom-xmpp-status">\n <input type="text" class="custom-xmpp-status" '+
  2031. ((__t=(status_message))==null?'':__t)+
  2032. '\n placeholder="'+
  2033. ((__t=(label_custom_status))==null?'':__t)+
  2034. '"/>\n <button type="submit">'+
  2035. ((__t=(label_save))==null?'':__t)+
  2036. '</button>\n</form>\n';
  2037. }
  2038. return __p;
  2039. }; });
  2040. define('tpl!chat_status', [],function () { return function(obj){
  2041. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2042. with(obj||{}){
  2043. __p+='<div class="xmpp-status">\n <a class="choose-xmpp-status '+
  2044. ((__t=(chat_status))==null?'':__t)+
  2045. '"\n data-value="'+
  2046. ((__t=(status_message))==null?'':__t)+
  2047. '"\n href="#" title="'+
  2048. ((__t=(desc_change_status))==null?'':__t)+
  2049. '">\n\n <span class="icon-'+
  2050. ((__t=(chat_status))==null?'':__t)+
  2051. '"></span>'+
  2052. ((__t=(status_message))==null?'':__t)+
  2053. '\n </a>\n <a class="change-xmpp-status-message icon-pencil"\n href="#"\n title="'+
  2054. ((__t=(desc_custom_status))==null?'':__t)+
  2055. '"></a>\n</div>\n';
  2056. }
  2057. return __p;
  2058. }; });
  2059. define('tpl!chatarea', [],function () { return function(obj){
  2060. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2061. with(obj||{}){
  2062. __p+='<div class="chat-area">\n <div class="chat-content"></div>\n <form class="sendXMPPMessage" action="" method="post">\n ';
  2063. if (show_toolbar) {
  2064. __p+='\n <ul class="chat-toolbar no-text-select"></ul>\n ';
  2065. }
  2066. __p+='\n <textarea type="text" class="chat-textarea" \n placeholder="'+
  2067. ((__t=(label_message))==null?'':__t)+
  2068. '"/>\n </form>\n</div>\n';
  2069. }
  2070. return __p;
  2071. }; });
  2072. define('tpl!chatbox', [],function () { return function(obj){
  2073. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2074. with(obj||{}){
  2075. __p+='<div class="box-flyout" style="height: '+
  2076. ((__t=(height))==null?'':__t)+
  2077. 'px">\n <div class="dragresize dragresize-tm"></div>\n <div class="chat-head chat-head-chatbox">\n <a class="close-chatbox-button icon-close"></a>\n <a class="toggle-chatbox-button icon-minus"></a>\n <div class="chat-title">\n ';
  2078. if (url) {
  2079. __p+='\n <a href="'+
  2080. ((__t=(url))==null?'':__t)+
  2081. '" target="_blank" class="user">\n ';
  2082. }
  2083. __p+='\n '+
  2084. ((__t=( fullname ))==null?'':__t)+
  2085. '\n ';
  2086. if (url) {
  2087. __p+='\n </a>\n ';
  2088. }
  2089. __p+='\n </div>\n <p class="user-custom-message"><p/>\n </div>\n <div class="chat-body">\n <div class="chat-content"></div>\n <form class="sendXMPPMessage" action="" method="post">\n ';
  2090. if (show_toolbar) {
  2091. __p+='\n <ul class="chat-toolbar no-text-select"></ul>\n ';
  2092. }
  2093. __p+='\n <textarea\n type="text"\n class="chat-textarea"\n placeholder="'+
  2094. ((__t=(label_personal_message))==null?'':__t)+
  2095. '"/>\n </form>\n </div>\n</div>\n';
  2096. }
  2097. return __p;
  2098. }; });
  2099. define('tpl!chatroom', [],function () { return function(obj){
  2100. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2101. with(obj||{}){
  2102. __p+='<div class="box-flyout" style="height: '+
  2103. ((__t=(height))==null?'':__t)+
  2104. 'px"\n ';
  2105. if (minimized) {
  2106. __p+=' style="display:none" ';
  2107. }
  2108. __p+='>\n <div class="dragresize dragresize-tm"></div>\n <div class="chat-head chat-head-chatroom">\n <a class="close-chatbox-button icon-close"></a>\n <a class="toggle-chatbox-button icon-minus"></a>\n <a class="configure-chatroom-button icon-wrench" style="display:none"></a>\n <div class="chat-title"> '+
  2109. ((__t=( name ))==null?'':__t)+
  2110. ' </div>\n <p class="chatroom-topic"><p/>\n </div>\n <div class="chat-body"><span class="spinner centered"/></div>\n</div>\n';
  2111. }
  2112. return __p;
  2113. }; });
  2114. define('tpl!chatroom_password_form', [],function () { return function(obj){
  2115. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2116. with(obj||{}){
  2117. __p+='<div class="chatroom-form-container">\n <form class="chatroom-form">\n <legend>'+
  2118. ((__t=(heading))==null?'':__t)+
  2119. '</legend>\n <label>'+
  2120. ((__t=(label_password))==null?'':__t)+
  2121. '<input type="password" name="password"/></label>\n <input type="submit" value="'+
  2122. ((__t=(label_submit))==null?'':__t)+
  2123. '"/>\n </form>\n</div>\n';
  2124. }
  2125. return __p;
  2126. }; });
  2127. define('tpl!chatroom_sidebar', [],function () { return function(obj){
  2128. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2129. with(obj||{}){
  2130. __p+='<!-- <div class="participants"> -->\n<form class="room-invite">\n <input class="invited-contact" placeholder="'+
  2131. ((__t=(label_invitation))==null?'':__t)+
  2132. '" type="text"/>\n</form>\n<label>'+
  2133. ((__t=(label_occupants))==null?'':__t)+
  2134. ':</label>\n<ul class="participant-list"></ul>\n<!-- </div> -->\n';
  2135. }
  2136. return __p;
  2137. }; });
  2138. define('tpl!chatrooms_tab', [],function () { return function(obj){
  2139. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2140. with(obj||{}){
  2141. __p+='<li><a class="s" href="#chatrooms">'+
  2142. ((__t=(label_rooms))==null?'':__t)+
  2143. '</a></li>\n';
  2144. }
  2145. return __p;
  2146. }; });
  2147. define('tpl!chats_panel', [],function () { return function(obj){
  2148. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2149. with(obj||{}){
  2150. __p+='<div id="minimized-chats">\n <a id="toggle-minimized-chats" href="#"></a>\n <div class="minimized-chats-flyout"></div>\n</div>\n';
  2151. }
  2152. return __p;
  2153. }; });
  2154. define('tpl!choose_status', [],function () { return function(obj){
  2155. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2156. with(obj||{}){
  2157. __p+='<dl id="target" class="dropdown">\n <dt id="fancy-xmpp-status-select" class="fancy-dropdown"></dt>\n <dd><ul class="xmpp-status-menu"></ul></dd>\n</dl>\n';
  2158. }
  2159. return __p;
  2160. }; });
  2161. define('tpl!contacts_panel', [],function () { return function(obj){
  2162. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2163. with(obj||{}){
  2164. __p+='<form class="set-xmpp-status" action="" method="post">\n <span id="xmpp-status-holder">\n <select id="select-xmpp-status" style="display:none">\n <option value="online">'+
  2165. ((__t=(label_online))==null?'':__t)+
  2166. '</option>\n <option value="dnd">'+
  2167. ((__t=(label_busy))==null?'':__t)+
  2168. '</option>\n <option value="away">'+
  2169. ((__t=(label_away))==null?'':__t)+
  2170. '</option>\n <option value="offline">'+
  2171. ((__t=(label_offline))==null?'':__t)+
  2172. '</option>\n ';
  2173. if (allow_logout) {
  2174. __p+='\n <option value="logout">'+
  2175. ((__t=(label_logout))==null?'':__t)+
  2176. '</option>\n ';
  2177. }
  2178. __p+='\n </select>\n </span>\n</form>\n';
  2179. }
  2180. return __p;
  2181. }; });
  2182. define('tpl!contacts_tab', [],function () { return function(obj){
  2183. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2184. with(obj||{}){
  2185. __p+='<li><a class="s current" href="#users">'+
  2186. ((__t=(label_contacts))==null?'':__t)+
  2187. '</a></li>\n';
  2188. }
  2189. return __p;
  2190. }; });
  2191. define('tpl!controlbox', [],function () { return function(obj){
  2192. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2193. with(obj||{}){
  2194. __p+='<div class="box-flyout" style="height: '+
  2195. ((__t=(height))==null?'':__t)+
  2196. 'px">\n <div class="dragresize dragresize-tm"></div>\n <div class="chat-head controlbox-head">\n <ul id="controlbox-tabs"></ul>\n <a class="close-chatbox-button icon-close"></a>\n </div>\n <div class="controlbox-panes"></div>\n</div>\n';
  2197. }
  2198. return __p;
  2199. }; });
  2200. define('tpl!controlbox_toggle', [],function () { return function(obj){
  2201. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2202. with(obj||{}){
  2203. __p+='<span class="conn-feedback">'+
  2204. ((__t=(label_toggle))==null?'':__t)+
  2205. '</span>\n<span style="display: none" id="online-count">(0)</span>\n';
  2206. }
  2207. return __p;
  2208. }; });
  2209. define('tpl!field', [],function () { return function(obj){
  2210. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2211. with(obj||{}){
  2212. __p+='<field var="'+
  2213. ((__t=(name))==null?'':__t)+
  2214. '">';
  2215. if (_.isArray(value)) {
  2216. __p+='\n ';
  2217. _.each(value,function(arrayValue) {
  2218. __p+='<value>'+
  2219. ((__t=(arrayValue))==null?'':__t)+
  2220. '</value>';
  2221. });
  2222. __p+='\n';
  2223. } else {
  2224. __p+='\n <value>'+
  2225. ((__t=(value))==null?'':__t)+
  2226. '</value>\n';
  2227. }
  2228. __p+='</field>\n';
  2229. }
  2230. return __p;
  2231. }; });
  2232. define('tpl!form_captcha', [],function () { return function(obj){
  2233. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2234. with(obj||{}){
  2235. __p+='';
  2236. if (label) {
  2237. __p+='\n<label>\n '+
  2238. ((__t=(label))==null?'':__t)+
  2239. '\n</label>\n';
  2240. }
  2241. __p+='\n<img src="data:'+
  2242. ((__t=(type))==null?'':__t)+
  2243. ';base64,'+
  2244. ((__t=(data))==null?'':__t)+
  2245. '">\n<input name="'+
  2246. ((__t=(name))==null?'':__t)+
  2247. '" type="text" ';
  2248. if (required) {
  2249. __p+=' class="required" ';
  2250. }
  2251. __p+=' >\n\n\n';
  2252. }
  2253. return __p;
  2254. }; });
  2255. define('tpl!form_checkbox', [],function () { return function(obj){
  2256. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2257. with(obj||{}){
  2258. __p+='<label>'+
  2259. ((__t=(label))==null?'':__t)+
  2260. '</label>\n<input name="'+
  2261. ((__t=(name))==null?'':__t)+
  2262. '" type="'+
  2263. ((__t=(type))==null?'':__t)+
  2264. '" '+
  2265. ((__t=(checked))==null?'':__t)+
  2266. '>\n';
  2267. }
  2268. return __p;
  2269. }; });
  2270. define('tpl!form_input', [],function () { return function(obj){
  2271. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2272. with(obj||{}){
  2273. __p+='';
  2274. if (label) {
  2275. __p+='\n<label>\n '+
  2276. ((__t=(label))==null?'':__t)+
  2277. '\n</label>\n';
  2278. }
  2279. __p+='\n<input name="'+
  2280. ((__t=(name))==null?'':__t)+
  2281. '" type="'+
  2282. ((__t=(type))==null?'':__t)+
  2283. '" \n ';
  2284. if (value) {
  2285. __p+=' value="'+
  2286. ((__t=(value))==null?'':__t)+
  2287. '" ';
  2288. }
  2289. __p+='\n ';
  2290. if (required) {
  2291. __p+=' class="required" ';
  2292. }
  2293. __p+=' >\n';
  2294. }
  2295. return __p;
  2296. }; });
  2297. define('tpl!form_select', [],function () { return function(obj){
  2298. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2299. with(obj||{}){
  2300. __p+='<label>'+
  2301. ((__t=(label))==null?'':__t)+
  2302. '</label>\n<select name="'+
  2303. ((__t=(name))==null?'':__t)+
  2304. '" ';
  2305. if (multiple) {
  2306. __p+=' multiple="multiple" ';
  2307. }
  2308. __p+='>'+
  2309. ((__t=(options))==null?'':__t)+
  2310. '</select>\n';
  2311. }
  2312. return __p;
  2313. }; });
  2314. define('tpl!form_textarea', [],function () { return function(obj){
  2315. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2316. with(obj||{}){
  2317. __p+='<label class="label-ta">'+
  2318. ((__t=(label))==null?'':__t)+
  2319. '</label>\n<textarea name="'+
  2320. ((__t=(name))==null?'':__t)+
  2321. '">'+
  2322. ((__t=(value))==null?'':__t)+
  2323. '</textarea>\n';
  2324. }
  2325. return __p;
  2326. }; });
  2327. define('tpl!form_username', [],function () { return function(obj){
  2328. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2329. with(obj||{}){
  2330. __p+='';
  2331. if (label) {
  2332. __p+='\n<label>\n '+
  2333. ((__t=(label))==null?'':__t)+
  2334. '\n</label>\n';
  2335. }
  2336. __p+='\n<div class="input-group">\n <input name="'+
  2337. ((__t=(name))==null?'':__t)+
  2338. '" type="'+
  2339. ((__t=(type))==null?'':__t)+
  2340. '" \n ';
  2341. if (value) {
  2342. __p+=' value="'+
  2343. ((__t=(value))==null?'':__t)+
  2344. '" ';
  2345. }
  2346. __p+='\n ';
  2347. if (required) {
  2348. __p+=' class="required" ';
  2349. }
  2350. __p+=' />\n <span>'+
  2351. ((__t=(domain))==null?'':__t)+
  2352. '</span>\n</div>\n';
  2353. }
  2354. return __p;
  2355. }; });
  2356. define('tpl!group_header', [],function () { return function(obj){
  2357. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2358. with(obj||{}){
  2359. __p+='<a href="#" class="group-toggle icon-'+
  2360. ((__t=(toggle_state))==null?'':__t)+
  2361. '" title="'+
  2362. ((__t=(desc_group_toggle))==null?'':__t)+
  2363. '">'+
  2364. ((__t=(label_group))==null?'':__t)+
  2365. '</a>\n';
  2366. }
  2367. return __p;
  2368. }; });
  2369. define('tpl!info', [],function () { return function(obj){
  2370. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2371. with(obj||{}){
  2372. __p+='<div class="chat-info">'+
  2373. ((__t=(message))==null?'':__t)+
  2374. '</div>\n';
  2375. }
  2376. return __p;
  2377. }; });
  2378. define('tpl!login_panel', [],function () { return function(obj){
  2379. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2380. with(obj||{}){
  2381. __p+='<form id="converse-login" method="post">\n <label>'+
  2382. ((__t=(label_username))==null?'':__t)+
  2383. '</label>\n <input type="username" name="jid" placeholder="user@server">\n <label>'+
  2384. ((__t=(label_password))==null?'':__t)+
  2385. '</label>\n <input type="password" name="password" placeholder="password">\n <input class="submit" type="submit" value="'+
  2386. ((__t=(label_login))==null?'':__t)+
  2387. '">\n <span class="conn-feedback"></span>\n</form>\n';
  2388. }
  2389. return __p;
  2390. }; });
  2391. define('tpl!login_tab', [],function () { return function(obj){
  2392. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2393. with(obj||{}){
  2394. __p+='<li><a class="current" href="#login-dialog">'+
  2395. ((__t=(label_sign_in))==null?'':__t)+
  2396. '</a></li>\n';
  2397. }
  2398. return __p;
  2399. }; });
  2400. define('tpl!message', [],function () { return function(obj){
  2401. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2402. with(obj||{}){
  2403. __p+='<div class="chat-message '+
  2404. ((__t=(extra_classes))==null?'':__t)+
  2405. '">\n <span class="chat-message-'+
  2406. ((__t=(sender))==null?'':__t)+
  2407. '">'+
  2408. ((__t=(time))==null?'':__t)+
  2409. ' '+
  2410. ((__t=(username))==null?'':__t)+
  2411. ':&nbsp;</span>\n <span class="chat-message-content">'+
  2412. ((__t=(message))==null?'':__t)+
  2413. '</span>\n</div>\n';
  2414. }
  2415. return __p;
  2416. }; });
  2417. define('tpl!new_day', [],function () { return function(obj){
  2418. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2419. with(obj||{}){
  2420. __p+='<time class="chat-date" datetime="'+
  2421. ((__t=(isodate))==null?'':__t)+
  2422. '">'+
  2423. ((__t=(datestring))==null?'':__t)+
  2424. '</time>\n';
  2425. }
  2426. return __p;
  2427. }; });
  2428. define('tpl!occupant', [],function () { return function(obj){
  2429. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2430. with(obj||{}){
  2431. __p+='<li class="'+
  2432. ((__t=(role))==null?'':__t)+
  2433. '"\n ';
  2434. if (role === "moderator") {
  2435. __p+='\n title="'+
  2436. ((__t=(desc_moderator))==null?'':__t)+
  2437. '"\n ';
  2438. }
  2439. __p+='\n ';
  2440. if (role === "participant") {
  2441. __p+='\n title="'+
  2442. ((__t=(desc_participant))==null?'':__t)+
  2443. '"\n ';
  2444. }
  2445. __p+='\n ';
  2446. if (role === "visitor") {
  2447. __p+='\n title="'+
  2448. ((__t=(desc_visitor))==null?'':__t)+
  2449. '"\n ';
  2450. }
  2451. __p+='\n>'+
  2452. ((__t=(nick))==null?'':__t)+
  2453. '</li>\n';
  2454. }
  2455. return __p;
  2456. }; });
  2457. define('tpl!pending_contact', [],function () { return function(obj){
  2458. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2459. with(obj||{}){
  2460. __p+='<span class="pending-contact-name">'+
  2461. ((__t=(fullname))==null?'':__t)+
  2462. '</span> <a class="remove-xmpp-contact icon-remove" title="'+
  2463. ((__t=(desc_remove))==null?'':__t)+
  2464. '" href="#"></a>\n';
  2465. }
  2466. return __p;
  2467. }; });
  2468. define('tpl!pending_contacts', [],function () { return function(obj){
  2469. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2470. with(obj||{}){
  2471. __p+='<dt id="pending-xmpp-contacts"><a href="#" class="group-toggle icon-'+
  2472. ((__t=(toggle_state))==null?'':__t)+
  2473. '" title="'+
  2474. ((__t=(desc_group_toggle))==null?'':__t)+
  2475. '">'+
  2476. ((__t=(label_pending_contacts))==null?'':__t)+
  2477. '</a></dt>\n';
  2478. }
  2479. return __p;
  2480. }; });
  2481. define('tpl!register_panel', [],function () { return function(obj){
  2482. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2483. with(obj||{}){
  2484. __p+='<form id="converse-register">\n <span class="reg-feedback"></span>\n <label>'+
  2485. ((__t=(label_domain))==null?'':__t)+
  2486. '</label>\n <input type="text" name="domain" placeholder="'+
  2487. ((__t=(domain_placeholder))==null?'':__t)+
  2488. '">\n <p class="form-help">'+
  2489. ((__t=(help_providers))==null?'':__t)+
  2490. ' <a href="'+
  2491. ((__t=(href_providers))==null?'':__t)+
  2492. '" class="url" target="_blank">'+
  2493. ((__t=(help_providers_link))==null?'':__t)+
  2494. '</a>.</p>\n <input class="submit" type="submit" value="'+
  2495. ((__t=(label_register))==null?'':__t)+
  2496. '">\n</form>\n';
  2497. }
  2498. return __p;
  2499. }; });
  2500. define('tpl!register_tab', [],function () { return function(obj){
  2501. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2502. with(obj||{}){
  2503. __p+='<li><a class="s" href="#register">'+
  2504. ((__t=(label_register))==null?'':__t)+
  2505. '</a></li>\n';
  2506. }
  2507. return __p;
  2508. }; });
  2509. define('tpl!registration_form', [],function () { return function(obj){
  2510. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2511. with(obj||{}){
  2512. __p+='<p class="provider-title">'+
  2513. ((__t=(domain))==null?'':__t)+
  2514. '</p>\n<a href=\'https://xmpp.net/result.php?domain='+
  2515. ((__t=(domain))==null?'':__t)+
  2516. '&amp;type=client\'>\n <img class="provider-score" src=\'https://xmpp.net/badge.php?domain='+
  2517. ((__t=(domain))==null?'':__t)+
  2518. '\' alt=\'xmpp.net score\' />\n</a>\n<p class="title">'+
  2519. ((__t=(title))==null?'':__t)+
  2520. '</p>\n<p class="instructions">'+
  2521. ((__t=(instructions))==null?'':__t)+
  2522. '</p>\n';
  2523. }
  2524. return __p;
  2525. }; });
  2526. define('tpl!registration_request', [],function () { return function(obj){
  2527. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2528. with(obj||{}){
  2529. __p+='<span class="spinner login-submit"/>\n<p class="info">'+
  2530. ((__t=(info_message))==null?'':__t)+
  2531. '</p>\n<button class="cancel hor_centered">'+
  2532. ((__t=(cancel))==null?'':__t)+
  2533. '</button>\n';
  2534. }
  2535. return __p;
  2536. }; });
  2537. define('tpl!requesting_contact', [],function () { return function(obj){
  2538. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2539. with(obj||{}){
  2540. __p+='<span class="req-contact-name">'+
  2541. ((__t=(fullname))==null?'':__t)+
  2542. '</span>\n<span class="request-actions">\n <a class="accept-xmpp-request icon-checkmark" title="'+
  2543. ((__t=(desc_accept))==null?'':__t)+
  2544. '" href="#"></a>\n <a class="decline-xmpp-request icon-close" title="'+
  2545. ((__t=(desc_decline))==null?'':__t)+
  2546. '" href="#"></a>\n</span>\n';
  2547. }
  2548. return __p;
  2549. }; });
  2550. define('tpl!requesting_contacts', [],function () { return function(obj){
  2551. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2552. with(obj||{}){
  2553. __p+='<dt id="xmpp-contact-requests"><a href="#" class="group-toggle icon-'+
  2554. ((__t=(toggle_state))==null?'':__t)+
  2555. '" title="'+
  2556. ((__t=(desc_group_toggle))==null?'':__t)+
  2557. '">'+
  2558. ((__t=(label_contact_requests))==null?'':__t)+
  2559. '</a></dt>\n';
  2560. }
  2561. return __p;
  2562. }; });
  2563. define('tpl!room_description', [],function () { return function(obj){
  2564. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2565. with(obj||{}){
  2566. __p+='<!-- FIXME: check markup in mockup -->\n<div class="room-info">\n<p class="room-info"><strong>'+
  2567. ((__t=(label_desc))==null?'':__t)+
  2568. '</strong> '+
  2569. ((__t=(desc))==null?'':__t)+
  2570. '</p>\n<p class="room-info"><strong>'+
  2571. ((__t=(label_occ))==null?'':__t)+
  2572. '</strong> '+
  2573. ((__t=(occ))==null?'':__t)+
  2574. '</p>\n<p class="room-info"><strong>'+
  2575. ((__t=(label_features))==null?'':__t)+
  2576. '</strong>\n <ul>\n ';
  2577. if (passwordprotected) {
  2578. __p+='\n <li class="room-info locked">'+
  2579. ((__t=(label_requires_auth))==null?'':__t)+
  2580. '</li>\n ';
  2581. }
  2582. __p+='\n ';
  2583. if (hidden) {
  2584. __p+='\n <li class="room-info">'+
  2585. ((__t=(label_hidden))==null?'':__t)+
  2586. '</li>\n ';
  2587. }
  2588. __p+='\n ';
  2589. if (membersonly) {
  2590. __p+='\n <li class="room-info">'+
  2591. ((__t=(label_requires_invite))==null?'':__t)+
  2592. '</li>\n ';
  2593. }
  2594. __p+='\n ';
  2595. if (moderated) {
  2596. __p+='\n <li class="room-info">'+
  2597. ((__t=(label_moderated))==null?'':__t)+
  2598. '</li>\n ';
  2599. }
  2600. __p+='\n ';
  2601. if (nonanonymous) {
  2602. __p+='\n <li class="room-info">'+
  2603. ((__t=(label_non_anon))==null?'':__t)+
  2604. '</li>\n ';
  2605. }
  2606. __p+='\n ';
  2607. if (open) {
  2608. __p+='\n <li class="room-info">'+
  2609. ((__t=(label_open_room))==null?'':__t)+
  2610. '</li>\n ';
  2611. }
  2612. __p+='\n ';
  2613. if (persistent) {
  2614. __p+='\n <li class="room-info">'+
  2615. ((__t=(label_permanent_room))==null?'':__t)+
  2616. '</li>\n ';
  2617. }
  2618. __p+='\n ';
  2619. if (publicroom) {
  2620. __p+='\n <li class="room-info">'+
  2621. ((__t=(label_public))==null?'':__t)+
  2622. '</li>\n ';
  2623. }
  2624. __p+='\n ';
  2625. if (semianonymous) {
  2626. __p+='\n <li class="room-info">'+
  2627. ((__t=(label_semi_anon))==null?'':__t)+
  2628. '</li>\n ';
  2629. }
  2630. __p+='\n ';
  2631. if (temporary) {
  2632. __p+='\n <li class="room-info">'+
  2633. ((__t=(label_temp_room))==null?'':__t)+
  2634. '</li>\n ';
  2635. }
  2636. __p+='\n ';
  2637. if (unmoderated) {
  2638. __p+='\n <li class="room-info">'+
  2639. ((__t=(label_unmoderated))==null?'':__t)+
  2640. '</li>\n ';
  2641. }
  2642. __p+='\n </ul>\n</p>\n</div>\n';
  2643. }
  2644. return __p;
  2645. }; });
  2646. define('tpl!room_item', [],function () { return function(obj){
  2647. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2648. with(obj||{}){
  2649. __p+='<dd class="available-chatroom">\n<a class="open-room" data-room-jid="'+
  2650. ((__t=(jid))==null?'':__t)+
  2651. '"\n title="'+
  2652. ((__t=(open_title))==null?'':__t)+
  2653. '" href="#">'+
  2654. ((__t=(name))==null?'':__t)+
  2655. '</a>\n<a class="room-info icon-room-info" data-room-jid="'+
  2656. ((__t=(jid))==null?'':__t)+
  2657. '"\n title="'+
  2658. ((__t=(info_title))==null?'':__t)+
  2659. '" href="#">&nbsp;</a>\n</dd>\n';
  2660. }
  2661. return __p;
  2662. }; });
  2663. define('tpl!room_panel', [],function () { return function(obj){
  2664. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2665. with(obj||{}){
  2666. __p+='<form class="add-chatroom" action="" method="post">\n <input type="text" name="chatroom" class="new-chatroom-name"\n placeholder="'+
  2667. ((__t=(label_room_name))==null?'':__t)+
  2668. '"/>\n <input type="text" name="nick" class="new-chatroom-nick"\n placeholder="'+
  2669. ((__t=(label_nickname))==null?'':__t)+
  2670. '"/>\n <input type="'+
  2671. ((__t=(server_input_type))==null?'':__t)+
  2672. '" name="server" class="new-chatroom-server"\n placeholder="'+
  2673. ((__t=(label_server))==null?'':__t)+
  2674. '"/>\n <input type="submit" name="join" value="'+
  2675. ((__t=(label_join))==null?'':__t)+
  2676. '"/>\n <input type="button" name="show" id="show-rooms" value="'+
  2677. ((__t=(label_show_rooms))==null?'':__t)+
  2678. '"/>\n</form>\n<dl id="available-chatrooms"></dl>\n';
  2679. }
  2680. return __p;
  2681. }; });
  2682. define('tpl!roster', [],function () { return function(obj){
  2683. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2684. with(obj||{}){
  2685. __p+='<input style="display: none;" class="roster-filter" placeholder="'+
  2686. ((__t=(placeholder))==null?'':__t)+
  2687. '">\n<select style="display: none;" class="filter-type">\n <option value="contacts">'+
  2688. ((__t=(label_contacts))==null?'':__t)+
  2689. '</option>\n <option value="groups">'+
  2690. ((__t=(label_groups))==null?'':__t)+
  2691. '</option>\n</select>\n';
  2692. }
  2693. return __p;
  2694. }; });
  2695. define('tpl!roster_item', [],function () { return function(obj){
  2696. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2697. with(obj||{}){
  2698. __p+='<a class="open-chat" title="'+
  2699. ((__t=(desc_chat))==null?'':__t)+
  2700. '" href="#"><span class="icon-'+
  2701. ((__t=(chat_status))==null?'':__t)+
  2702. '" title="'+
  2703. ((__t=(desc_status))==null?'':__t)+
  2704. '"></span>'+
  2705. ((__t=(fullname))==null?'':__t)+
  2706. '</a>\n<a class="remove-xmpp-contact icon-remove" title="'+
  2707. ((__t=(desc_remove))==null?'':__t)+
  2708. '" href="#"></a>\n';
  2709. }
  2710. return __p;
  2711. }; });
  2712. define('tpl!search_contact', [],function () { return function(obj){
  2713. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2714. with(obj||{}){
  2715. __p+='<li>\n <form class="search-xmpp-contact">\n <input type="text"\n name="identifier"\n class="username"\n placeholder="'+
  2716. ((__t=(label_contact_name))==null?'':__t)+
  2717. '"/>\n <button type="submit">'+
  2718. ((__t=(label_search))==null?'':__t)+
  2719. '</button>\n </form>\n</li>\n';
  2720. }
  2721. return __p;
  2722. }; });
  2723. define('tpl!select_option', [],function () { return function(obj){
  2724. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2725. with(obj||{}){
  2726. __p+='<option value="'+
  2727. ((__t=(value))==null?'':__t)+
  2728. '" ';
  2729. if (selected) {
  2730. __p+=' selected="selected" ';
  2731. }
  2732. __p+=' >'+
  2733. ((__t=(label))==null?'':__t)+
  2734. '</option>\n';
  2735. }
  2736. return __p;
  2737. }; });
  2738. define('tpl!status_option', [],function () { return function(obj){
  2739. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2740. with(obj||{}){
  2741. __p+='<li>\n <a href="#" class="'+
  2742. ((__t=( value ))==null?'':__t)+
  2743. '" data-value="'+
  2744. ((__t=( value ))==null?'':__t)+
  2745. '">\n <span class="icon-'+
  2746. ((__t=( value ))==null?'':__t)+
  2747. '"></span>\n '+
  2748. ((__t=( text ))==null?'':__t)+
  2749. '\n </a>\n</li>\n';
  2750. }
  2751. return __p;
  2752. }; });
  2753. define('tpl!toggle_chats', [],function () { return function(obj){
  2754. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2755. with(obj||{}){
  2756. __p+=''+
  2757. ((__t=(Minimized))==null?'':__t)+
  2758. ' <span id="minimized-count">('+
  2759. ((__t=(num_minimized))==null?'':__t)+
  2760. ')</span>\n<span class="unread-message-count"\n ';
  2761. if (!num_unread) {
  2762. __p+=' style="display: none" ';
  2763. }
  2764. __p+='\n href="#">'+
  2765. ((__t=(num_unread))==null?'':__t)+
  2766. '</span>\n';
  2767. }
  2768. return __p;
  2769. }; });
  2770. define('tpl!toolbar', [],function () { return function(obj){
  2771. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2772. with(obj||{}){
  2773. __p+='';
  2774. if (show_emoticons) {
  2775. __p+='\n <li class="toggle-smiley icon-happy" title="Insert a smilery">\n <ul>\n <li><a class="icon-smiley" href="#" data-emoticon=":)"></a></li>\n <li><a class="icon-wink" href="#" data-emoticon=";)"></a></li>\n <li><a class="icon-grin" href="#" data-emoticon=":D"></a></li>\n <li><a class="icon-tongue" href="#" data-emoticon=":P"></a></li>\n <li><a class="icon-cool" href="#" data-emoticon="8)"></a></li>\n <li><a class="icon-evil" href="#" data-emoticon=">:)"></a></li>\n <li><a class="icon-confused" href="#" data-emoticon=":S"></a></li>\n <li><a class="icon-wondering" href="#" data-emoticon=":\\"></a></li>\n <li><a class="icon-angry" href="#" data-emoticon=">:("></a></li>\n <li><a class="icon-sad" href="#" data-emoticon=":("></a></li>\n <li><a class="icon-shocked" href="#" data-emoticon=":O"></a></li>\n <li><a class="icon-thumbs-up" href="#" data-emoticon="(^.^)b"></a></li>\n <li><a class="icon-heart" href="#" data-emoticon="<3"></a></li>\n </ul>\n </li>\n';
  2776. }
  2777. __p+='\n';
  2778. if (show_call_button) {
  2779. __p+='\n<li class="toggle-call"><a class="icon-phone" title="'+
  2780. ((__t=(label_start_call))==null?'':__t)+
  2781. '"></a></li>\n';
  2782. }
  2783. __p+='\n';
  2784. if (show_participants_toggle) {
  2785. __p+='\n<li class="toggle-participants"><a class="icon-hide-users" title="'+
  2786. ((__t=(label_hide_participants))==null?'':__t)+
  2787. '"></a></li>\n';
  2788. }
  2789. __p+='\n';
  2790. if (show_clear_button) {
  2791. __p+='\n<li class="toggle-clear"><a class="icon-remove" title="'+
  2792. ((__t=(label_clear))==null?'':__t)+
  2793. '"></a></li>\n';
  2794. }
  2795. __p+='\n';
  2796. if (allow_otr) {
  2797. __p+='\n <li class="toggle-otr '+
  2798. ((__t=(otr_status_class))==null?'':__t)+
  2799. '" title="'+
  2800. ((__t=(otr_tooltip))==null?'':__t)+
  2801. '">\n <span class="chat-toolbar-text">'+
  2802. ((__t=(otr_translated_status))==null?'':__t)+
  2803. '</span>\n ';
  2804. if (otr_status == UNENCRYPTED) {
  2805. __p+='\n <span class="icon-unlocked"></span>\n ';
  2806. }
  2807. __p+='\n ';
  2808. if (otr_status == UNVERIFIED) {
  2809. __p+='\n <span class="icon-lock"></span>\n ';
  2810. }
  2811. __p+='\n ';
  2812. if (otr_status == VERIFIED) {
  2813. __p+='\n <span class="icon-lock"></span>\n ';
  2814. }
  2815. __p+='\n ';
  2816. if (otr_status == FINISHED) {
  2817. __p+='\n <span class="icon-unlocked"></span>\n ';
  2818. }
  2819. __p+='\n <ul>\n ';
  2820. if (otr_status == UNENCRYPTED) {
  2821. __p+='\n <li><a class="start-otr" href="#">'+
  2822. ((__t=(label_start_encrypted_conversation))==null?'':__t)+
  2823. '</a></li>\n ';
  2824. }
  2825. __p+='\n ';
  2826. if (otr_status != UNENCRYPTED) {
  2827. __p+='\n <li><a class="start-otr" href="#">'+
  2828. ((__t=(label_refresh_encrypted_conversation))==null?'':__t)+
  2829. '</a></li>\n <li><a class="end-otr" href="#">'+
  2830. ((__t=(label_end_encrypted_conversation))==null?'':__t)+
  2831. '</a></li>\n <li><a class="auth-otr" data-scheme="smp" href="#">'+
  2832. ((__t=(label_verify_with_smp))==null?'':__t)+
  2833. '</a></li>\n ';
  2834. }
  2835. __p+='\n ';
  2836. if (otr_status == UNVERIFIED) {
  2837. __p+='\n <li><a class="auth-otr" data-scheme="fingerprint" href="#">'+
  2838. ((__t=(label_verify_with_fingerprints))==null?'':__t)+
  2839. '</a></li>\n ';
  2840. }
  2841. __p+='\n <li><a href="http://www.cypherpunks.ca/otr/help/3.2.0/levels.php" target="_blank">'+
  2842. ((__t=(label_whats_this))==null?'':__t)+
  2843. '</a></li>\n </ul>\n </li>\n';
  2844. }
  2845. __p+='\n';
  2846. }
  2847. return __p;
  2848. }; });
  2849. define('tpl!trimmed_chat', [],function () { return function(obj){
  2850. var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
  2851. with(obj||{}){
  2852. __p+='<a class="close-chatbox-button icon-close"></a>\n<a class="chat-head-message-count" \n ';
  2853. if (!num_unread) {
  2854. __p+=' style="display: none" ';
  2855. }
  2856. __p+='\n href="#">'+
  2857. ((__t=(num_unread))==null?'':__t)+
  2858. '</a>\n<a href="#" class="restore-chat" title="'+
  2859. ((__t=(tooltip))==null?'':__t)+
  2860. '">\n '+
  2861. ((__t=( title ))==null?'':__t)+
  2862. '\n</a>\n';
  2863. }
  2864. return __p;
  2865. }; });
  2866. define("converse-templates", [
  2867. "tpl!action",
  2868. "tpl!add_contact_dropdown",
  2869. "tpl!add_contact_form",
  2870. "tpl!change_status_message",
  2871. "tpl!chat_status",
  2872. "tpl!chatarea",
  2873. "tpl!chatbox",
  2874. "tpl!chatroom",
  2875. "tpl!chatroom_password_form",
  2876. "tpl!chatroom_sidebar",
  2877. "tpl!chatrooms_tab",
  2878. "tpl!chats_panel",
  2879. "tpl!choose_status",
  2880. "tpl!contacts_panel",
  2881. "tpl!contacts_tab",
  2882. "tpl!controlbox",
  2883. "tpl!controlbox_toggle",
  2884. "tpl!field",
  2885. "tpl!form_captcha",
  2886. "tpl!form_checkbox",
  2887. "tpl!form_input",
  2888. "tpl!form_select",
  2889. "tpl!form_textarea",
  2890. "tpl!form_username",
  2891. "tpl!group_header",
  2892. "tpl!info",
  2893. "tpl!login_panel",
  2894. "tpl!login_tab",
  2895. "tpl!message",
  2896. "tpl!new_day",
  2897. "tpl!occupant",
  2898. "tpl!pending_contact",
  2899. "tpl!pending_contacts",
  2900. "tpl!register_panel",
  2901. "tpl!register_tab",
  2902. "tpl!registration_form",
  2903. "tpl!registration_request",
  2904. "tpl!requesting_contact",
  2905. "tpl!requesting_contacts",
  2906. "tpl!room_description",
  2907. "tpl!room_item",
  2908. "tpl!room_panel",
  2909. "tpl!roster",
  2910. "tpl!roster_item",
  2911. "tpl!search_contact",
  2912. "tpl!select_option",
  2913. "tpl!status_option",
  2914. "tpl!toggle_chats",
  2915. "tpl!toolbar",
  2916. "tpl!trimmed_chat"
  2917. ], function () {
  2918. return {
  2919. action: arguments[0],
  2920. add_contact_dropdown: arguments[1],
  2921. add_contact_form: arguments[2],
  2922. change_status_message: arguments[3],
  2923. chat_status: arguments[4],
  2924. chatarea: arguments[5],
  2925. chatbox: arguments[6],
  2926. chatroom: arguments[7],
  2927. chatroom_password_form: arguments[8],
  2928. chatroom_sidebar: arguments[9],
  2929. chatrooms_tab: arguments[10],
  2930. chats_panel: arguments[11],
  2931. choose_status: arguments[12],
  2932. contacts_panel: arguments[13],
  2933. contacts_tab: arguments[14],
  2934. controlbox: arguments[15],
  2935. controlbox_toggle: arguments[16],
  2936. field: arguments[17],
  2937. form_captcha: arguments[18],
  2938. form_checkbox: arguments[19],
  2939. form_input: arguments[20],
  2940. form_select: arguments[21],
  2941. form_textarea: arguments[22],
  2942. form_username: arguments[23],
  2943. group_header: arguments[24],
  2944. info: arguments[25],
  2945. login_panel: arguments[26],
  2946. login_tab: arguments[27],
  2947. message: arguments[28],
  2948. new_day: arguments[29],
  2949. occupant: arguments[30],
  2950. pending_contact: arguments[31],
  2951. pending_contacts: arguments[32],
  2952. register_panel: arguments[33],
  2953. register_tab: arguments[34],
  2954. registration_form: arguments[35],
  2955. registration_request: arguments[36],
  2956. requesting_contact: arguments[37],
  2957. requesting_contacts: arguments[38],
  2958. room_description: arguments[39],
  2959. room_item: arguments[40],
  2960. room_panel: arguments[41],
  2961. roster: arguments[42],
  2962. roster_item: arguments[43],
  2963. search_contact: arguments[44],
  2964. select_option: arguments[45],
  2965. status_option: arguments[46],
  2966. toggle_chats: arguments[47],
  2967. toolbar: arguments[48],
  2968. trimmed_chat: arguments[49]
  2969. };
  2970. });
  2971. define('utils',["jquery", "converse-templates"], function ($, templates) {
  2972. var XFORM_TYPE_MAP = {
  2973. 'text-private': 'password',
  2974. 'text-single': 'textline',
  2975. 'fixed': 'label',
  2976. 'boolean': 'checkbox',
  2977. 'hidden': 'hidden',
  2978. 'jid-multi': 'textarea',
  2979. 'list-single': 'dropdown',
  2980. 'list-multi': 'dropdown'
  2981. };
  2982. $.expr[':'].emptyVal = function(obj){
  2983. return obj.value === '';
  2984. };
  2985. $.fn.hasScrollBar = function() {
  2986. if (!$.contains(document, this.get(0))) {
  2987. return false;
  2988. }
  2989. if(this.parent().height() < this.get(0).scrollHeight) {
  2990. return true;
  2991. }
  2992. return false;
  2993. };
  2994. $.fn.addHyperlinks = function () {
  2995. if (this.length > 0) {
  2996. this.each(function (i, obj) {
  2997. var x = $(obj).html();
  2998. var list = x.match(/\b(https?:\/\/|www\.|https?:\/\/www\.)[^\s<]{2,200}\b/g );
  2999. if (list) {
  3000. for (i=0; i<list.length; i++) {
  3001. var prot = list[i].indexOf('http://') === 0 || list[i].indexOf('https://') === 0 ? '' : 'http://';
  3002. var escaped_url = encodeURI(decodeURI(list[i])).replace(/[!'()]/g, escape).replace(/\*/g, "%2A");
  3003. x = x.replace(list[i], "<a target='_blank' href='" + prot + escaped_url + "'>"+ list[i] + "</a>" );
  3004. }
  3005. }
  3006. $(obj).html(x);
  3007. });
  3008. }
  3009. return this;
  3010. };
  3011. var utils = {
  3012. // Translation machinery
  3013. // ---------------------
  3014. __: function (str) {
  3015. // Translation factory
  3016. if (this.i18n === undefined) {
  3017. this.i18n = locales.en;
  3018. }
  3019. var t = this.i18n.translate(str);
  3020. if (arguments.length>1) {
  3021. return t.fetch.apply(t, [].slice.call(arguments,1));
  3022. } else {
  3023. return t.fetch();
  3024. }
  3025. },
  3026. ___: function (str) {
  3027. /* XXX: This is part of a hack to get gettext to scan strings to be
  3028. * translated. Strings we cannot send to the function above because
  3029. * they require variable interpolation and we don't yet have the
  3030. * variables at scan time.
  3031. *
  3032. * See actionInfoMessages
  3033. */
  3034. return str;
  3035. },
  3036. webForm2xForm: function (field) {
  3037. /* Takes an HTML DOM and turns it into an XForm field.
  3038. *
  3039. * Parameters:
  3040. * (DOMElement) field - the field to convert
  3041. */
  3042. var $input = $(field), value;
  3043. if ($input.is('[type=checkbox]')) {
  3044. value = $input.is(':checked') && 1 || 0;
  3045. } else if ($input.is('textarea')) {
  3046. value = [];
  3047. var lines = $input.val().split('\n');
  3048. for( var vk=0; vk<lines.length; vk++) {
  3049. var val = $.trim(lines[vk]);
  3050. if (val === '')
  3051. continue;
  3052. value.push(val);
  3053. }
  3054. } else {
  3055. value = $input.val();
  3056. }
  3057. return $(templates.field({
  3058. name: $input.attr('name'),
  3059. value: value
  3060. }))[0];
  3061. },
  3062. xForm2webForm: function ($field, $stanza) {
  3063. /* Takes a field in XMPP XForm (XEP-004: Data Forms) format
  3064. * and turns it into a HTML DOM field.
  3065. *
  3066. * Parameters:
  3067. * (XMLElement) field - the field to convert
  3068. */
  3069. // FIXME: take <required> into consideration
  3070. var options = [], j, $options, $values, value, values;
  3071. if ($field.attr('type') == 'list-single' || $field.attr('type') == 'list-multi') {
  3072. values = [];
  3073. $values = $field.children('value');
  3074. for (j=0; j<$values.length; j++) {
  3075. values.push($($values[j]).text());
  3076. }
  3077. $options = $field.children('option');
  3078. for (j=0; j<$options.length; j++) {
  3079. value = $($options[j]).find('value').text();
  3080. options.push(templates.select_option({
  3081. value: value,
  3082. label: $($options[j]).attr('label'),
  3083. selected: (values.indexOf(value) >= 0),
  3084. required: $field.find('required').length
  3085. }));
  3086. }
  3087. return templates.form_select({
  3088. name: $field.attr('var'),
  3089. label: $field.attr('label'),
  3090. options: options.join(''),
  3091. multiple: ($field.attr('type') == 'list-multi'),
  3092. required: $field.find('required').length
  3093. });
  3094. } else if ($field.attr('type') == 'fixed') {
  3095. return $('<p class="form-help">').text($field.find('value').text());
  3096. } else if ($field.attr('type') == 'jid-multi') {
  3097. return templates.form_textarea({
  3098. name: $field.attr('var'),
  3099. label: $field.attr('label') || '',
  3100. value: $field.find('value').text(),
  3101. required: $field.find('required').length
  3102. });
  3103. } else if ($field.attr('type') == 'boolean') {
  3104. return templates.form_checkbox({
  3105. name: $field.attr('var'),
  3106. type: XFORM_TYPE_MAP[$field.attr('type')],
  3107. label: $field.attr('label') || '',
  3108. checked: $field.find('value').text() === "1" && 'checked="1"' || '',
  3109. required: $field.find('required').length
  3110. });
  3111. } else if ($field.attr('type') && $field.attr('var') === 'username') {
  3112. return templates.form_username({
  3113. domain: ' @'+this.domain,
  3114. name: $field.attr('var'),
  3115. type: XFORM_TYPE_MAP[$field.attr('type')],
  3116. label: $field.attr('label') || '',
  3117. value: $field.find('value').text(),
  3118. required: $field.find('required').length
  3119. });
  3120. } else if ($field.attr('type')) {
  3121. return templates.form_input({
  3122. name: $field.attr('var'),
  3123. type: XFORM_TYPE_MAP[$field.attr('type')],
  3124. label: $field.attr('label') || '',
  3125. value: $field.find('value').text(),
  3126. required: $field.find('required').length
  3127. });
  3128. } else {
  3129. if ($field.attr('var') === 'ocr') { // Captcha
  3130. return _.reduce(_.map($field.find('uri'),
  3131. $.proxy(function (uri) {
  3132. return templates.form_captcha({
  3133. label: this.$field.attr('label'),
  3134. name: this.$field.attr('var'),
  3135. data: this.$stanza.find('data[cid="'+uri.textContent.replace(/^cid:/, '')+'"]').text(),
  3136. type: uri.getAttribute('type'),
  3137. required: this.$field.find('required').length
  3138. });
  3139. }, {'$stanza': $stanza, '$field': $field})
  3140. ),
  3141. function (memo, num) { return memo + num; }, ''
  3142. );
  3143. }
  3144. }
  3145. }
  3146. };
  3147. return utils;
  3148. });
  3149. /*!
  3150. * jQuery Browser Plugin v0.0.6
  3151. * https://github.com/gabceb/jquery-browser-plugin
  3152. *
  3153. * Original jquery-browser code Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors
  3154. * http://jquery.org/license
  3155. *
  3156. * Modifications Copyright 2013 Gabriel Cebrian
  3157. * https://github.com/gabceb
  3158. *
  3159. * Released under the MIT license
  3160. *
  3161. * Date: 2013-07-29T17:23:27-07:00
  3162. */
  3163. (function (root, factory) {
  3164. if (typeof define === 'function' && define.amd) {
  3165. // AMD. Register as an anonymous module.
  3166. define('jquery.browser',['jquery'], function ($) {
  3167. factory($, root);
  3168. });
  3169. } else {
  3170. // Browser globals
  3171. factory(jQuery, root);
  3172. }
  3173. }(this, function(jQuery, window) {
  3174. var matched, browser;
  3175. jQuery.uaMatch = function( ua ) {
  3176. ua = ua.toLowerCase();
  3177. var match = /(opr)[\/]([\w.]+)/.exec( ua ) ||
  3178. /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
  3179. /(version)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec( ua ) ||
  3180. /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
  3181. /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
  3182. /(msie) ([\w.]+)/.exec( ua ) ||
  3183. ua.indexOf("trident") >= 0 && /(rv)(?::| )([\w.]+)/.exec( ua ) ||
  3184. ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
  3185. [];
  3186. var platform_match = /(ipad)/.exec( ua ) ||
  3187. /(iphone)/.exec( ua ) ||
  3188. /(android)/.exec( ua ) ||
  3189. /(windows phone)/.exec( ua ) ||
  3190. /(win)/.exec( ua ) ||
  3191. /(mac)/.exec( ua ) ||
  3192. /(linux)/.exec( ua ) ||
  3193. /(cros)/i.exec( ua ) ||
  3194. [];
  3195. return {
  3196. browser: match[ 3 ] || match[ 1 ] || "",
  3197. version: match[ 2 ] || "0",
  3198. platform: platform_match[ 0 ] || ""
  3199. };
  3200. };
  3201. matched = jQuery.uaMatch( window.navigator.userAgent );
  3202. browser = {};
  3203. if ( matched.browser ) {
  3204. browser[ matched.browser ] = true;
  3205. browser.version = matched.version;
  3206. browser.versionNumber = parseInt(matched.version);
  3207. }
  3208. if ( matched.platform ) {
  3209. browser[ matched.platform ] = true;
  3210. }
  3211. // These are all considered mobile platforms, meaning they run a mobile browser
  3212. if ( browser.android || browser.ipad || browser.iphone || browser[ "windows phone" ] ) {
  3213. browser.mobile = true;
  3214. }
  3215. // These are all considered desktop platforms, meaning they run a desktop browser
  3216. if ( browser.cros || browser.mac || browser.linux || browser.win ) {
  3217. browser.desktop = true;
  3218. }
  3219. // Chrome, Opera 15+ and Safari are webkit based browsers
  3220. if ( browser.chrome || browser.opr || browser.safari ) {
  3221. browser.webkit = true;
  3222. }
  3223. // IE11 has a new token so we will assign it msie to avoid breaking changes
  3224. if ( browser.rv )
  3225. {
  3226. var ie = "msie";
  3227. matched.browser = ie;
  3228. browser[ie] = true;
  3229. }
  3230. // Opera 15+ are identified as opr
  3231. if ( browser.opr )
  3232. {
  3233. var opera = "opera";
  3234. matched.browser = opera;
  3235. browser[opera] = true;
  3236. }
  3237. // Stock Android browsers are marked as Safari on Android.
  3238. if ( browser.safari && browser.android )
  3239. {
  3240. var android = "android";
  3241. matched.browser = android;
  3242. browser[android] = true;
  3243. }
  3244. // Assign the name and platform variable
  3245. browser.name = matched.browser;
  3246. browser.platform = matched.platform;
  3247. jQuery.browser = browser;
  3248. return browser;
  3249. }));
  3250. /*
  3251. CryptoJS v3.1.2
  3252. code.google.com/p/crypto-js
  3253. (c) 2009-2013 by Jeff Mott. All rights reserved.
  3254. code.google.com/p/crypto-js/wiki/License
  3255. */
  3256. /**
  3257. * CryptoJS core components.
  3258. */
  3259. var CryptoJS = CryptoJS || (function (Math, undefined) {
  3260. /**
  3261. * CryptoJS namespace.
  3262. */
  3263. var C = {};
  3264. /**
  3265. * Library namespace.
  3266. */
  3267. var C_lib = C.lib = {};
  3268. /**
  3269. * Base object for prototypal inheritance.
  3270. */
  3271. var Base = C_lib.Base = (function () {
  3272. function F() {}
  3273. return {
  3274. /**
  3275. * Creates a new object that inherits from this object.
  3276. *
  3277. * @param {Object} overrides Properties to copy into the new object.
  3278. *
  3279. * @return {Object} The new object.
  3280. *
  3281. * @static
  3282. *
  3283. * @example
  3284. *
  3285. * var MyType = CryptoJS.lib.Base.extend({
  3286. * field: 'value',
  3287. *
  3288. * method: function () {
  3289. * }
  3290. * });
  3291. */
  3292. extend: function (overrides) {
  3293. // Spawn
  3294. F.prototype = this;
  3295. var subtype = new F();
  3296. // Augment
  3297. if (overrides) {
  3298. subtype.mixIn(overrides);
  3299. }
  3300. // Create default initializer
  3301. if (!subtype.hasOwnProperty('init')) {
  3302. subtype.init = function () {
  3303. subtype.$super.init.apply(this, arguments);
  3304. };
  3305. }
  3306. // Initializer's prototype is the subtype object
  3307. subtype.init.prototype = subtype;
  3308. // Reference supertype
  3309. subtype.$super = this;
  3310. return subtype;
  3311. },
  3312. /**
  3313. * Extends this object and runs the init method.
  3314. * Arguments to create() will be passed to init().
  3315. *
  3316. * @return {Object} The new object.
  3317. *
  3318. * @static
  3319. *
  3320. * @example
  3321. *
  3322. * var instance = MyType.create();
  3323. */
  3324. create: function () {
  3325. var instance = this.extend();
  3326. instance.init.apply(instance, arguments);
  3327. return instance;
  3328. },
  3329. /**
  3330. * Initializes a newly created object.
  3331. * Override this method to add some logic when your objects are created.
  3332. *
  3333. * @example
  3334. *
  3335. * var MyType = CryptoJS.lib.Base.extend({
  3336. * init: function () {
  3337. * // ...
  3338. * }
  3339. * });
  3340. */
  3341. init: function () {
  3342. },
  3343. /**
  3344. * Copies properties into this object.
  3345. *
  3346. * @param {Object} properties The properties to mix in.
  3347. *
  3348. * @example
  3349. *
  3350. * MyType.mixIn({
  3351. * field: 'value'
  3352. * });
  3353. */
  3354. mixIn: function (properties) {
  3355. for (var propertyName in properties) {
  3356. if (properties.hasOwnProperty(propertyName)) {
  3357. this[propertyName] = properties[propertyName];
  3358. }
  3359. }
  3360. // IE won't copy toString using the loop above
  3361. if (properties.hasOwnProperty('toString')) {
  3362. this.toString = properties.toString;
  3363. }
  3364. },
  3365. /**
  3366. * Creates a copy of this object.
  3367. *
  3368. * @return {Object} The clone.
  3369. *
  3370. * @example
  3371. *
  3372. * var clone = instance.clone();
  3373. */
  3374. clone: function () {
  3375. return this.init.prototype.extend(this);
  3376. }
  3377. };
  3378. }());
  3379. /**
  3380. * An array of 32-bit words.
  3381. *
  3382. * @property {Array} words The array of 32-bit words.
  3383. * @property {number} sigBytes The number of significant bytes in this word array.
  3384. */
  3385. var WordArray = C_lib.WordArray = Base.extend({
  3386. /**
  3387. * Initializes a newly created word array.
  3388. *
  3389. * @param {Array} words (Optional) An array of 32-bit words.
  3390. * @param {number} sigBytes (Optional) The number of significant bytes in the words.
  3391. *
  3392. * @example
  3393. *
  3394. * var wordArray = CryptoJS.lib.WordArray.create();
  3395. * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]);
  3396. * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6);
  3397. */
  3398. init: function (words, sigBytes) {
  3399. words = this.words = words || [];
  3400. if (sigBytes != undefined) {
  3401. this.sigBytes = sigBytes;
  3402. } else {
  3403. this.sigBytes = words.length * 4;
  3404. }
  3405. },
  3406. /**
  3407. * Converts this word array to a string.
  3408. *
  3409. * @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex
  3410. *
  3411. * @return {string} The stringified word array.
  3412. *
  3413. * @example
  3414. *
  3415. * var string = wordArray + '';
  3416. * var string = wordArray.toString();
  3417. * var string = wordArray.toString(CryptoJS.enc.Utf8);
  3418. */
  3419. toString: function (encoder) {
  3420. return (encoder || Hex).stringify(this);
  3421. },
  3422. /**
  3423. * Concatenates a word array to this word array.
  3424. *
  3425. * @param {WordArray} wordArray The word array to append.
  3426. *
  3427. * @return {WordArray} This word array.
  3428. *
  3429. * @example
  3430. *
  3431. * wordArray1.concat(wordArray2);
  3432. */
  3433. concat: function (wordArray) {
  3434. // Shortcuts
  3435. var thisWords = this.words;
  3436. var thatWords = wordArray.words;
  3437. var thisSigBytes = this.sigBytes;
  3438. var thatSigBytes = wordArray.sigBytes;
  3439. // Clamp excess bits
  3440. this.clamp();
  3441. // Concat
  3442. if (thisSigBytes % 4) {
  3443. // Copy one byte at a time
  3444. for (var i = 0; i < thatSigBytes; i++) {
  3445. var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
  3446. thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8);
  3447. }
  3448. } else if (thatWords.length > 0xffff) {
  3449. // Copy one word at a time
  3450. for (var i = 0; i < thatSigBytes; i += 4) {
  3451. thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2];
  3452. }
  3453. } else {
  3454. // Copy all words at once
  3455. thisWords.push.apply(thisWords, thatWords);
  3456. }
  3457. this.sigBytes += thatSigBytes;
  3458. // Chainable
  3459. return this;
  3460. },
  3461. /**
  3462. * Removes insignificant bits.
  3463. *
  3464. * @example
  3465. *
  3466. * wordArray.clamp();
  3467. */
  3468. clamp: function () {
  3469. // Shortcuts
  3470. var words = this.words;
  3471. var sigBytes = this.sigBytes;
  3472. // Clamp
  3473. words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8);
  3474. words.length = Math.ceil(sigBytes / 4);
  3475. },
  3476. /**
  3477. * Creates a copy of this word array.
  3478. *
  3479. * @return {WordArray} The clone.
  3480. *
  3481. * @example
  3482. *
  3483. * var clone = wordArray.clone();
  3484. */
  3485. clone: function () {
  3486. var clone = Base.clone.call(this);
  3487. clone.words = this.words.slice(0);
  3488. return clone;
  3489. },
  3490. /**
  3491. * Creates a word array filled with random bytes.
  3492. *
  3493. * @param {number} nBytes The number of random bytes to generate.
  3494. *
  3495. * @return {WordArray} The random word array.
  3496. *
  3497. * @static
  3498. *
  3499. * @example
  3500. *
  3501. * var wordArray = CryptoJS.lib.WordArray.random(16);
  3502. */
  3503. random: function (nBytes) {
  3504. var words = [];
  3505. for (var i = 0; i < nBytes; i += 4) {
  3506. words.push((Math.random() * 0x100000000) | 0);
  3507. }
  3508. return new WordArray.init(words, nBytes);
  3509. }
  3510. });
  3511. /**
  3512. * Encoder namespace.
  3513. */
  3514. var C_enc = C.enc = {};
  3515. /**
  3516. * Hex encoding strategy.
  3517. */
  3518. var Hex = C_enc.Hex = {
  3519. /**
  3520. * Converts a word array to a hex string.
  3521. *
  3522. * @param {WordArray} wordArray The word array.
  3523. *
  3524. * @return {string} The hex string.
  3525. *
  3526. * @static
  3527. *
  3528. * @example
  3529. *
  3530. * var hexString = CryptoJS.enc.Hex.stringify(wordArray);
  3531. */
  3532. stringify: function (wordArray) {
  3533. // Shortcuts
  3534. var words = wordArray.words;
  3535. var sigBytes = wordArray.sigBytes;
  3536. // Convert
  3537. var hexChars = [];
  3538. for (var i = 0; i < sigBytes; i++) {
  3539. var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
  3540. hexChars.push((bite >>> 4).toString(16));
  3541. hexChars.push((bite & 0x0f).toString(16));
  3542. }
  3543. return hexChars.join('');
  3544. },
  3545. /**
  3546. * Converts a hex string to a word array.
  3547. *
  3548. * @param {string} hexStr The hex string.
  3549. *
  3550. * @return {WordArray} The word array.
  3551. *
  3552. * @static
  3553. *
  3554. * @example
  3555. *
  3556. * var wordArray = CryptoJS.enc.Hex.parse(hexString);
  3557. */
  3558. parse: function (hexStr) {
  3559. // Shortcut
  3560. var hexStrLength = hexStr.length;
  3561. // Convert
  3562. var words = [];
  3563. for (var i = 0; i < hexStrLength; i += 2) {
  3564. words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
  3565. }
  3566. return new WordArray.init(words, hexStrLength / 2);
  3567. }
  3568. };
  3569. /**
  3570. * Latin1 encoding strategy.
  3571. */
  3572. var Latin1 = C_enc.Latin1 = {
  3573. /**
  3574. * Converts a word array to a Latin1 string.
  3575. *
  3576. * @param {WordArray} wordArray The word array.
  3577. *
  3578. * @return {string} The Latin1 string.
  3579. *
  3580. * @static
  3581. *
  3582. * @example
  3583. *
  3584. * var latin1String = CryptoJS.enc.Latin1.stringify(wordArray);
  3585. */
  3586. stringify: function (wordArray) {
  3587. // Shortcuts
  3588. var words = wordArray.words;
  3589. var sigBytes = wordArray.sigBytes;
  3590. // Convert
  3591. var latin1Chars = [];
  3592. for (var i = 0; i < sigBytes; i++) {
  3593. var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
  3594. latin1Chars.push(String.fromCharCode(bite));
  3595. }
  3596. return latin1Chars.join('');
  3597. },
  3598. /**
  3599. * Converts a Latin1 string to a word array.
  3600. *
  3601. * @param {string} latin1Str The Latin1 string.
  3602. *
  3603. * @return {WordArray} The word array.
  3604. *
  3605. * @static
  3606. *
  3607. * @example
  3608. *
  3609. * var wordArray = CryptoJS.enc.Latin1.parse(latin1String);
  3610. */
  3611. parse: function (latin1Str) {
  3612. // Shortcut
  3613. var latin1StrLength = latin1Str.length;
  3614. // Convert
  3615. var words = [];
  3616. for (var i = 0; i < latin1StrLength; i++) {
  3617. words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8);
  3618. }
  3619. return new WordArray.init(words, latin1StrLength);
  3620. }
  3621. };
  3622. /**
  3623. * UTF-8 encoding strategy.
  3624. */
  3625. var Utf8 = C_enc.Utf8 = {
  3626. /**
  3627. * Converts a word array to a UTF-8 string.
  3628. *
  3629. * @param {WordArray} wordArray The word array.
  3630. *
  3631. * @return {string} The UTF-8 string.
  3632. *
  3633. * @static
  3634. *
  3635. * @example
  3636. *
  3637. * var utf8String = CryptoJS.enc.Utf8.stringify(wordArray);
  3638. */
  3639. stringify: function (wordArray) {
  3640. try {
  3641. return decodeURIComponent(escape(Latin1.stringify(wordArray)));
  3642. } catch (e) {
  3643. throw new Error('Malformed UTF-8 data');
  3644. }
  3645. },
  3646. /**
  3647. * Converts a UTF-8 string to a word array.
  3648. *
  3649. * @param {string} utf8Str The UTF-8 string.
  3650. *
  3651. * @return {WordArray} The word array.
  3652. *
  3653. * @static
  3654. *
  3655. * @example
  3656. *
  3657. * var wordArray = CryptoJS.enc.Utf8.parse(utf8String);
  3658. */
  3659. parse: function (utf8Str) {
  3660. return Latin1.parse(unescape(encodeURIComponent(utf8Str)));
  3661. }
  3662. };
  3663. /**
  3664. * Abstract buffered block algorithm template.
  3665. *
  3666. * The property blockSize must be implemented in a concrete subtype.
  3667. *
  3668. * @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0
  3669. */
  3670. var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({
  3671. /**
  3672. * Resets this block algorithm's data buffer to its initial state.
  3673. *
  3674. * @example
  3675. *
  3676. * bufferedBlockAlgorithm.reset();
  3677. */
  3678. reset: function () {
  3679. // Initial values
  3680. this._data = new WordArray.init();
  3681. this._nDataBytes = 0;
  3682. },
  3683. /**
  3684. * Adds new data to this block algorithm's buffer.
  3685. *
  3686. * @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8.
  3687. *
  3688. * @example
  3689. *
  3690. * bufferedBlockAlgorithm._append('data');
  3691. * bufferedBlockAlgorithm._append(wordArray);
  3692. */
  3693. _append: function (data) {
  3694. // Convert string to WordArray, else assume WordArray already
  3695. if (typeof data == 'string') {
  3696. data = Utf8.parse(data);
  3697. }
  3698. // Append
  3699. this._data.concat(data);
  3700. this._nDataBytes += data.sigBytes;
  3701. },
  3702. /**
  3703. * Processes available data blocks.
  3704. *
  3705. * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype.
  3706. *
  3707. * @param {boolean} doFlush Whether all blocks and partial blocks should be processed.
  3708. *
  3709. * @return {WordArray} The processed data.
  3710. *
  3711. * @example
  3712. *
  3713. * var processedData = bufferedBlockAlgorithm._process();
  3714. * var processedData = bufferedBlockAlgorithm._process(!!'flush');
  3715. */
  3716. _process: function (doFlush) {
  3717. // Shortcuts
  3718. var data = this._data;
  3719. var dataWords = data.words;
  3720. var dataSigBytes = data.sigBytes;
  3721. var blockSize = this.blockSize;
  3722. var blockSizeBytes = blockSize * 4;
  3723. // Count blocks ready
  3724. var nBlocksReady = dataSigBytes / blockSizeBytes;
  3725. if (doFlush) {
  3726. // Round up to include partial blocks
  3727. nBlocksReady = Math.ceil(nBlocksReady);
  3728. } else {
  3729. // Round down to include only full blocks,
  3730. // less the number of blocks that must remain in the buffer
  3731. nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0);
  3732. }
  3733. // Count words ready
  3734. var nWordsReady = nBlocksReady * blockSize;
  3735. // Count bytes ready
  3736. var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes);
  3737. // Process blocks
  3738. if (nWordsReady) {
  3739. for (var offset = 0; offset < nWordsReady; offset += blockSize) {
  3740. // Perform concrete-algorithm logic
  3741. this._doProcessBlock(dataWords, offset);
  3742. }
  3743. // Remove processed words
  3744. var processedWords = dataWords.splice(0, nWordsReady);
  3745. data.sigBytes -= nBytesReady;
  3746. }
  3747. // Return processed words
  3748. return new WordArray.init(processedWords, nBytesReady);
  3749. },
  3750. /**
  3751. * Creates a copy of this object.
  3752. *
  3753. * @return {Object} The clone.
  3754. *
  3755. * @example
  3756. *
  3757. * var clone = bufferedBlockAlgorithm.clone();
  3758. */
  3759. clone: function () {
  3760. var clone = Base.clone.call(this);
  3761. clone._data = this._data.clone();
  3762. return clone;
  3763. },
  3764. _minBufferSize: 0
  3765. });
  3766. /**
  3767. * Abstract hasher template.
  3768. *
  3769. * @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits)
  3770. */
  3771. var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({
  3772. /**
  3773. * Configuration options.
  3774. */
  3775. cfg: Base.extend(),
  3776. /**
  3777. * Initializes a newly created hasher.
  3778. *
  3779. * @param {Object} cfg (Optional) The configuration options to use for this hash computation.
  3780. *
  3781. * @example
  3782. *
  3783. * var hasher = CryptoJS.algo.SHA256.create();
  3784. */
  3785. init: function (cfg) {
  3786. // Apply config defaults
  3787. this.cfg = this.cfg.extend(cfg);
  3788. // Set initial values
  3789. this.reset();
  3790. },
  3791. /**
  3792. * Resets this hasher to its initial state.
  3793. *
  3794. * @example
  3795. *
  3796. * hasher.reset();
  3797. */
  3798. reset: function () {
  3799. // Reset data buffer
  3800. BufferedBlockAlgorithm.reset.call(this);
  3801. // Perform concrete-hasher logic
  3802. this._doReset();
  3803. },
  3804. /**
  3805. * Updates this hasher with a message.
  3806. *
  3807. * @param {WordArray|string} messageUpdate The message to append.
  3808. *
  3809. * @return {Hasher} This hasher.
  3810. *
  3811. * @example
  3812. *
  3813. * hasher.update('message');
  3814. * hasher.update(wordArray);
  3815. */
  3816. update: function (messageUpdate) {
  3817. // Append
  3818. this._append(messageUpdate);
  3819. // Update the hash
  3820. this._process();
  3821. // Chainable
  3822. return this;
  3823. },
  3824. /**
  3825. * Finalizes the hash computation.
  3826. * Note that the finalize operation is effectively a destructive, read-once operation.
  3827. *
  3828. * @param {WordArray|string} messageUpdate (Optional) A final message update.
  3829. *
  3830. * @return {WordArray} The hash.
  3831. *
  3832. * @example
  3833. *
  3834. * var hash = hasher.finalize();
  3835. * var hash = hasher.finalize('message');
  3836. * var hash = hasher.finalize(wordArray);
  3837. */
  3838. finalize: function (messageUpdate) {
  3839. // Final message update
  3840. if (messageUpdate) {
  3841. this._append(messageUpdate);
  3842. }
  3843. // Perform concrete-hasher logic
  3844. var hash = this._doFinalize();
  3845. return hash;
  3846. },
  3847. blockSize: 512/32,
  3848. /**
  3849. * Creates a shortcut function to a hasher's object interface.
  3850. *
  3851. * @param {Hasher} hasher The hasher to create a helper for.
  3852. *
  3853. * @return {Function} The shortcut function.
  3854. *
  3855. * @static
  3856. *
  3857. * @example
  3858. *
  3859. * var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256);
  3860. */
  3861. _createHelper: function (hasher) {
  3862. return function (message, cfg) {
  3863. return new hasher.init(cfg).finalize(message);
  3864. };
  3865. },
  3866. /**
  3867. * Creates a shortcut function to the HMAC's object interface.
  3868. *
  3869. * @param {Hasher} hasher The hasher to use in this HMAC helper.
  3870. *
  3871. * @return {Function} The shortcut function.
  3872. *
  3873. * @static
  3874. *
  3875. * @example
  3876. *
  3877. * var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256);
  3878. */
  3879. _createHmacHelper: function (hasher) {
  3880. return function (message, key) {
  3881. return new C_algo.HMAC.init(hasher, key).finalize(message);
  3882. };
  3883. }
  3884. });
  3885. /**
  3886. * Algorithm namespace.
  3887. */
  3888. var C_algo = C.algo = {};
  3889. return C;
  3890. }(Math));
  3891. define("crypto.core", function(){});
  3892. /*
  3893. CryptoJS v3.1.2
  3894. code.google.com/p/crypto-js
  3895. (c) 2009-2013 by Jeff Mott. All rights reserved.
  3896. code.google.com/p/crypto-js/wiki/License
  3897. */
  3898. (function () {
  3899. // Shortcuts
  3900. var C = CryptoJS;
  3901. var C_lib = C.lib;
  3902. var WordArray = C_lib.WordArray;
  3903. var C_enc = C.enc;
  3904. /**
  3905. * Base64 encoding strategy.
  3906. */
  3907. var Base64 = C_enc.Base64 = {
  3908. /**
  3909. * Converts a word array to a Base64 string.
  3910. *
  3911. * @param {WordArray} wordArray The word array.
  3912. *
  3913. * @return {string} The Base64 string.
  3914. *
  3915. * @static
  3916. *
  3917. * @example
  3918. *
  3919. * var base64String = CryptoJS.enc.Base64.stringify(wordArray);
  3920. */
  3921. stringify: function (wordArray) {
  3922. // Shortcuts
  3923. var words = wordArray.words;
  3924. var sigBytes = wordArray.sigBytes;
  3925. var map = this._map;
  3926. // Clamp excess bits
  3927. wordArray.clamp();
  3928. // Convert
  3929. var base64Chars = [];
  3930. for (var i = 0; i < sigBytes; i += 3) {
  3931. var byte1 = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
  3932. var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff;
  3933. var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff;
  3934. var triplet = (byte1 << 16) | (byte2 << 8) | byte3;
  3935. for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) {
  3936. base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f));
  3937. }
  3938. }
  3939. // Add padding
  3940. var paddingChar = map.charAt(64);
  3941. if (paddingChar) {
  3942. while (base64Chars.length % 4) {
  3943. base64Chars.push(paddingChar);
  3944. }
  3945. }
  3946. return base64Chars.join('');
  3947. },
  3948. /**
  3949. * Converts a Base64 string to a word array.
  3950. *
  3951. * @param {string} base64Str The Base64 string.
  3952. *
  3953. * @return {WordArray} The word array.
  3954. *
  3955. * @static
  3956. *
  3957. * @example
  3958. *
  3959. * var wordArray = CryptoJS.enc.Base64.parse(base64String);
  3960. */
  3961. parse: function (base64Str) {
  3962. // Shortcuts
  3963. var base64StrLength = base64Str.length;
  3964. var map = this._map;
  3965. // Ignore padding
  3966. var paddingChar = map.charAt(64);
  3967. if (paddingChar) {
  3968. var paddingIndex = base64Str.indexOf(paddingChar);
  3969. if (paddingIndex != -1) {
  3970. base64StrLength = paddingIndex;
  3971. }
  3972. }
  3973. // Convert
  3974. var words = [];
  3975. var nBytes = 0;
  3976. for (var i = 0; i < base64StrLength; i++) {
  3977. if (i % 4) {
  3978. var bits1 = map.indexOf(base64Str.charAt(i - 1)) << ((i % 4) * 2);
  3979. var bits2 = map.indexOf(base64Str.charAt(i)) >>> (6 - (i % 4) * 2);
  3980. words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8);
  3981. nBytes++;
  3982. }
  3983. }
  3984. return WordArray.create(words, nBytes);
  3985. },
  3986. _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
  3987. };
  3988. }());
  3989. define("crypto.enc-base64", ["crypto.core"], function(){});
  3990. (function (Math) {
  3991. // Shortcuts
  3992. var C = CryptoJS;
  3993. var C_lib = C.lib;
  3994. var WordArray = C_lib.WordArray;
  3995. var Hasher = C_lib.Hasher;
  3996. var C_algo = C.algo;
  3997. // Constants table
  3998. var T = [];
  3999. // Compute constants
  4000. (function () {
  4001. for (var i = 0; i < 64; i++) {
  4002. T[i] = (Math.abs(Math.sin(i + 1)) * 0x100000000) | 0;
  4003. }
  4004. }());
  4005. /**
  4006. * MD5 hash algorithm.
  4007. */
  4008. var MD5 = C_algo.MD5 = Hasher.extend({
  4009. _doReset: function () {
  4010. this._hash = new WordArray.init([
  4011. 0x67452301, 0xefcdab89,
  4012. 0x98badcfe, 0x10325476
  4013. ]);
  4014. },
  4015. _doProcessBlock: function (M, offset) {
  4016. // Swap endian
  4017. for (var i = 0; i < 16; i++) {
  4018. // Shortcuts
  4019. var offset_i = offset + i;
  4020. var M_offset_i = M[offset_i];
  4021. M[offset_i] = (
  4022. (((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) |
  4023. (((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00)
  4024. );
  4025. }
  4026. // Shortcuts
  4027. var H = this._hash.words;
  4028. var M_offset_0 = M[offset + 0];
  4029. var M_offset_1 = M[offset + 1];
  4030. var M_offset_2 = M[offset + 2];
  4031. var M_offset_3 = M[offset + 3];
  4032. var M_offset_4 = M[offset + 4];
  4033. var M_offset_5 = M[offset + 5];
  4034. var M_offset_6 = M[offset + 6];
  4035. var M_offset_7 = M[offset + 7];
  4036. var M_offset_8 = M[offset + 8];
  4037. var M_offset_9 = M[offset + 9];
  4038. var M_offset_10 = M[offset + 10];
  4039. var M_offset_11 = M[offset + 11];
  4040. var M_offset_12 = M[offset + 12];
  4041. var M_offset_13 = M[offset + 13];
  4042. var M_offset_14 = M[offset + 14];
  4043. var M_offset_15 = M[offset + 15];
  4044. // Working varialbes
  4045. var a = H[0];
  4046. var b = H[1];
  4047. var c = H[2];
  4048. var d = H[3];
  4049. // Computation
  4050. a = FF(a, b, c, d, M_offset_0, 7, T[0]);
  4051. d = FF(d, a, b, c, M_offset_1, 12, T[1]);
  4052. c = FF(c, d, a, b, M_offset_2, 17, T[2]);
  4053. b = FF(b, c, d, a, M_offset_3, 22, T[3]);
  4054. a = FF(a, b, c, d, M_offset_4, 7, T[4]);
  4055. d = FF(d, a, b, c, M_offset_5, 12, T[5]);
  4056. c = FF(c, d, a, b, M_offset_6, 17, T[6]);
  4057. b = FF(b, c, d, a, M_offset_7, 22, T[7]);
  4058. a = FF(a, b, c, d, M_offset_8, 7, T[8]);
  4059. d = FF(d, a, b, c, M_offset_9, 12, T[9]);
  4060. c = FF(c, d, a, b, M_offset_10, 17, T[10]);
  4061. b = FF(b, c, d, a, M_offset_11, 22, T[11]);
  4062. a = FF(a, b, c, d, M_offset_12, 7, T[12]);
  4063. d = FF(d, a, b, c, M_offset_13, 12, T[13]);
  4064. c = FF(c, d, a, b, M_offset_14, 17, T[14]);
  4065. b = FF(b, c, d, a, M_offset_15, 22, T[15]);
  4066. a = GG(a, b, c, d, M_offset_1, 5, T[16]);
  4067. d = GG(d, a, b, c, M_offset_6, 9, T[17]);
  4068. c = GG(c, d, a, b, M_offset_11, 14, T[18]);
  4069. b = GG(b, c, d, a, M_offset_0, 20, T[19]);
  4070. a = GG(a, b, c, d, M_offset_5, 5, T[20]);
  4071. d = GG(d, a, b, c, M_offset_10, 9, T[21]);
  4072. c = GG(c, d, a, b, M_offset_15, 14, T[22]);
  4073. b = GG(b, c, d, a, M_offset_4, 20, T[23]);
  4074. a = GG(a, b, c, d, M_offset_9, 5, T[24]);
  4075. d = GG(d, a, b, c, M_offset_14, 9, T[25]);
  4076. c = GG(c, d, a, b, M_offset_3, 14, T[26]);
  4077. b = GG(b, c, d, a, M_offset_8, 20, T[27]);
  4078. a = GG(a, b, c, d, M_offset_13, 5, T[28]);
  4079. d = GG(d, a, b, c, M_offset_2, 9, T[29]);
  4080. c = GG(c, d, a, b, M_offset_7, 14, T[30]);
  4081. b = GG(b, c, d, a, M_offset_12, 20, T[31]);
  4082. a = HH(a, b, c, d, M_offset_5, 4, T[32]);
  4083. d = HH(d, a, b, c, M_offset_8, 11, T[33]);
  4084. c = HH(c, d, a, b, M_offset_11, 16, T[34]);
  4085. b = HH(b, c, d, a, M_offset_14, 23, T[35]);
  4086. a = HH(a, b, c, d, M_offset_1, 4, T[36]);
  4087. d = HH(d, a, b, c, M_offset_4, 11, T[37]);
  4088. c = HH(c, d, a, b, M_offset_7, 16, T[38]);
  4089. b = HH(b, c, d, a, M_offset_10, 23, T[39]);
  4090. a = HH(a, b, c, d, M_offset_13, 4, T[40]);
  4091. d = HH(d, a, b, c, M_offset_0, 11, T[41]);
  4092. c = HH(c, d, a, b, M_offset_3, 16, T[42]);
  4093. b = HH(b, c, d, a, M_offset_6, 23, T[43]);
  4094. a = HH(a, b, c, d, M_offset_9, 4, T[44]);
  4095. d = HH(d, a, b, c, M_offset_12, 11, T[45]);
  4096. c = HH(c, d, a, b, M_offset_15, 16, T[46]);
  4097. b = HH(b, c, d, a, M_offset_2, 23, T[47]);
  4098. a = II(a, b, c, d, M_offset_0, 6, T[48]);
  4099. d = II(d, a, b, c, M_offset_7, 10, T[49]);
  4100. c = II(c, d, a, b, M_offset_14, 15, T[50]);
  4101. b = II(b, c, d, a, M_offset_5, 21, T[51]);
  4102. a = II(a, b, c, d, M_offset_12, 6, T[52]);
  4103. d = II(d, a, b, c, M_offset_3, 10, T[53]);
  4104. c = II(c, d, a, b, M_offset_10, 15, T[54]);
  4105. b = II(b, c, d, a, M_offset_1, 21, T[55]);
  4106. a = II(a, b, c, d, M_offset_8, 6, T[56]);
  4107. d = II(d, a, b, c, M_offset_15, 10, T[57]);
  4108. c = II(c, d, a, b, M_offset_6, 15, T[58]);
  4109. b = II(b, c, d, a, M_offset_13, 21, T[59]);
  4110. a = II(a, b, c, d, M_offset_4, 6, T[60]);
  4111. d = II(d, a, b, c, M_offset_11, 10, T[61]);
  4112. c = II(c, d, a, b, M_offset_2, 15, T[62]);
  4113. b = II(b, c, d, a, M_offset_9, 21, T[63]);
  4114. // Intermediate hash value
  4115. H[0] = (H[0] + a) | 0;
  4116. H[1] = (H[1] + b) | 0;
  4117. H[2] = (H[2] + c) | 0;
  4118. H[3] = (H[3] + d) | 0;
  4119. },
  4120. _doFinalize: function () {
  4121. // Shortcuts
  4122. var data = this._data;
  4123. var dataWords = data.words;
  4124. var nBitsTotal = this._nDataBytes * 8;
  4125. var nBitsLeft = data.sigBytes * 8;
  4126. // Add padding
  4127. dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
  4128. var nBitsTotalH = Math.floor(nBitsTotal / 0x100000000);
  4129. var nBitsTotalL = nBitsTotal;
  4130. dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = (
  4131. (((nBitsTotalH << 8) | (nBitsTotalH >>> 24)) & 0x00ff00ff) |
  4132. (((nBitsTotalH << 24) | (nBitsTotalH >>> 8)) & 0xff00ff00)
  4133. );
  4134. dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = (
  4135. (((nBitsTotalL << 8) | (nBitsTotalL >>> 24)) & 0x00ff00ff) |
  4136. (((nBitsTotalL << 24) | (nBitsTotalL >>> 8)) & 0xff00ff00)
  4137. );
  4138. data.sigBytes = (dataWords.length + 1) * 4;
  4139. // Hash final blocks
  4140. this._process();
  4141. // Shortcuts
  4142. var hash = this._hash;
  4143. var H = hash.words;
  4144. // Swap endian
  4145. for (var i = 0; i < 4; i++) {
  4146. // Shortcut
  4147. var H_i = H[i];
  4148. H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) |
  4149. (((H_i << 24) | (H_i >>> 8)) & 0xff00ff00);
  4150. }
  4151. // Return final computed hash
  4152. return hash;
  4153. },
  4154. clone: function () {
  4155. var clone = Hasher.clone.call(this);
  4156. clone._hash = this._hash.clone();
  4157. return clone;
  4158. }
  4159. });
  4160. function FF(a, b, c, d, x, s, t) {
  4161. var n = a + ((b & c) | (~b & d)) + x + t;
  4162. return ((n << s) | (n >>> (32 - s))) + b;
  4163. }
  4164. function GG(a, b, c, d, x, s, t) {
  4165. var n = a + ((b & d) | (c & ~d)) + x + t;
  4166. return ((n << s) | (n >>> (32 - s))) + b;
  4167. }
  4168. function HH(a, b, c, d, x, s, t) {
  4169. var n = a + (b ^ c ^ d) + x + t;
  4170. return ((n << s) | (n >>> (32 - s))) + b;
  4171. }
  4172. function II(a, b, c, d, x, s, t) {
  4173. var n = a + (c ^ (b | ~d)) + x + t;
  4174. return ((n << s) | (n >>> (32 - s))) + b;
  4175. }
  4176. /**
  4177. * Shortcut function to the hasher's object interface.
  4178. *
  4179. * @param {WordArray|string} message The message to hash.
  4180. *
  4181. * @return {WordArray} The hash.
  4182. *
  4183. * @static
  4184. *
  4185. * @example
  4186. *
  4187. * var hash = CryptoJS.MD5('message');
  4188. * var hash = CryptoJS.MD5(wordArray);
  4189. */
  4190. C.MD5 = Hasher._createHelper(MD5);
  4191. /**
  4192. * Shortcut function to the HMAC's object interface.
  4193. *
  4194. * @param {WordArray|string} message The message to hash.
  4195. * @param {WordArray|string} key The secret key.
  4196. *
  4197. * @return {WordArray} The HMAC.
  4198. *
  4199. * @static
  4200. *
  4201. * @example
  4202. *
  4203. * var hmac = CryptoJS.HmacMD5(message, key);
  4204. */
  4205. C.HmacMD5 = Hasher._createHmacHelper(MD5);
  4206. }(Math));
  4207. define("crypto.md5", ["crypto.core"], function(){});
  4208. (function () {
  4209. // Shortcuts
  4210. var C = CryptoJS;
  4211. var C_lib = C.lib;
  4212. var Base = C_lib.Base;
  4213. var WordArray = C_lib.WordArray;
  4214. var C_algo = C.algo;
  4215. var MD5 = C_algo.MD5;
  4216. /**
  4217. * This key derivation function is meant to conform with EVP_BytesToKey.
  4218. * www.openssl.org/docs/crypto/EVP_BytesToKey.html
  4219. */
  4220. var EvpKDF = C_algo.EvpKDF = Base.extend({
  4221. /**
  4222. * Configuration options.
  4223. *
  4224. * @property {number} keySize The key size in words to generate. Default: 4 (128 bits)
  4225. * @property {Hasher} hasher The hash algorithm to use. Default: MD5
  4226. * @property {number} iterations The number of iterations to perform. Default: 1
  4227. */
  4228. cfg: Base.extend({
  4229. keySize: 128/32,
  4230. hasher: MD5,
  4231. iterations: 1
  4232. }),
  4233. /**
  4234. * Initializes a newly created key derivation function.
  4235. *
  4236. * @param {Object} cfg (Optional) The configuration options to use for the derivation.
  4237. *
  4238. * @example
  4239. *
  4240. * var kdf = CryptoJS.algo.EvpKDF.create();
  4241. * var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8 });
  4242. * var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8, iterations: 1000 });
  4243. */
  4244. init: function (cfg) {
  4245. this.cfg = this.cfg.extend(cfg);
  4246. },
  4247. /**
  4248. * Derives a key from a password.
  4249. *
  4250. * @param {WordArray|string} password The password.
  4251. * @param {WordArray|string} salt A salt.
  4252. *
  4253. * @return {WordArray} The derived key.
  4254. *
  4255. * @example
  4256. *
  4257. * var key = kdf.compute(password, salt);
  4258. */
  4259. compute: function (password, salt) {
  4260. // Shortcut
  4261. var cfg = this.cfg;
  4262. // Init hasher
  4263. var hasher = cfg.hasher.create();
  4264. // Initial values
  4265. var derivedKey = WordArray.create();
  4266. // Shortcuts
  4267. var derivedKeyWords = derivedKey.words;
  4268. var keySize = cfg.keySize;
  4269. var iterations = cfg.iterations;
  4270. // Generate key
  4271. while (derivedKeyWords.length < keySize) {
  4272. if (block) {
  4273. hasher.update(block);
  4274. }
  4275. var block = hasher.update(password).finalize(salt);
  4276. hasher.reset();
  4277. // Iterations
  4278. for (var i = 1; i < iterations; i++) {
  4279. block = hasher.finalize(block);
  4280. hasher.reset();
  4281. }
  4282. derivedKey.concat(block);
  4283. }
  4284. derivedKey.sigBytes = keySize * 4;
  4285. return derivedKey;
  4286. }
  4287. });
  4288. /**
  4289. * Derives a key from a password.
  4290. *
  4291. * @param {WordArray|string} password The password.
  4292. * @param {WordArray|string} salt A salt.
  4293. * @param {Object} cfg (Optional) The configuration options to use for this computation.
  4294. *
  4295. * @return {WordArray} The derived key.
  4296. *
  4297. * @static
  4298. *
  4299. * @example
  4300. *
  4301. * var key = CryptoJS.EvpKDF(password, salt);
  4302. * var key = CryptoJS.EvpKDF(password, salt, { keySize: 8 });
  4303. * var key = CryptoJS.EvpKDF(password, salt, { keySize: 8, iterations: 1000 });
  4304. */
  4305. C.EvpKDF = function (password, salt, cfg) {
  4306. return EvpKDF.create(cfg).compute(password, salt);
  4307. };
  4308. }());
  4309. define("crypto.evpkdf", ["crypto.md5"], function(){});
  4310. /*
  4311. CryptoJS v3.1.2
  4312. code.google.com/p/crypto-js
  4313. (c) 2009-2013 by Jeff Mott. All rights reserved.
  4314. code.google.com/p/crypto-js/wiki/License
  4315. */
  4316. /**
  4317. * Cipher core components.
  4318. */
  4319. CryptoJS.lib.Cipher || (function (undefined) {
  4320. // Shortcuts
  4321. var C = CryptoJS;
  4322. var C_lib = C.lib;
  4323. var Base = C_lib.Base;
  4324. var WordArray = C_lib.WordArray;
  4325. var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm;
  4326. var C_enc = C.enc;
  4327. var Utf8 = C_enc.Utf8;
  4328. var Base64 = C_enc.Base64;
  4329. var C_algo = C.algo;
  4330. var EvpKDF = C_algo.EvpKDF;
  4331. /**
  4332. * Abstract base cipher template.
  4333. *
  4334. * @property {number} keySize This cipher's key size. Default: 4 (128 bits)
  4335. * @property {number} ivSize This cipher's IV size. Default: 4 (128 bits)
  4336. * @property {number} _ENC_XFORM_MODE A constant representing encryption mode.
  4337. * @property {number} _DEC_XFORM_MODE A constant representing decryption mode.
  4338. */
  4339. var Cipher = C_lib.Cipher = BufferedBlockAlgorithm.extend({
  4340. /**
  4341. * Configuration options.
  4342. *
  4343. * @property {WordArray} iv The IV to use for this operation.
  4344. */
  4345. cfg: Base.extend(),
  4346. /**
  4347. * Creates this cipher in encryption mode.
  4348. *
  4349. * @param {WordArray} key The key.
  4350. * @param {Object} cfg (Optional) The configuration options to use for this operation.
  4351. *
  4352. * @return {Cipher} A cipher instance.
  4353. *
  4354. * @static
  4355. *
  4356. * @example
  4357. *
  4358. * var cipher = CryptoJS.algo.AES.createEncryptor(keyWordArray, { iv: ivWordArray });
  4359. */
  4360. createEncryptor: function (key, cfg) {
  4361. return this.create(this._ENC_XFORM_MODE, key, cfg);
  4362. },
  4363. /**
  4364. * Creates this cipher in decryption mode.
  4365. *
  4366. * @param {WordArray} key The key.
  4367. * @param {Object} cfg (Optional) The configuration options to use for this operation.
  4368. *
  4369. * @return {Cipher} A cipher instance.
  4370. *
  4371. * @static
  4372. *
  4373. * @example
  4374. *
  4375. * var cipher = CryptoJS.algo.AES.createDecryptor(keyWordArray, { iv: ivWordArray });
  4376. */
  4377. createDecryptor: function (key, cfg) {
  4378. return this.create(this._DEC_XFORM_MODE, key, cfg);
  4379. },
  4380. /**
  4381. * Initializes a newly created cipher.
  4382. *
  4383. * @param {number} xformMode Either the encryption or decryption transormation mode constant.
  4384. * @param {WordArray} key The key.
  4385. * @param {Object} cfg (Optional) The configuration options to use for this operation.
  4386. *
  4387. * @example
  4388. *
  4389. * var cipher = CryptoJS.algo.AES.create(CryptoJS.algo.AES._ENC_XFORM_MODE, keyWordArray, { iv: ivWordArray });
  4390. */
  4391. init: function (xformMode, key, cfg) {
  4392. // Apply config defaults
  4393. this.cfg = this.cfg.extend(cfg);
  4394. // Store transform mode and key
  4395. this._xformMode = xformMode;
  4396. this._key = key;
  4397. // Set initial values
  4398. this.reset();
  4399. },
  4400. /**
  4401. * Resets this cipher to its initial state.
  4402. *
  4403. * @example
  4404. *
  4405. * cipher.reset();
  4406. */
  4407. reset: function () {
  4408. // Reset data buffer
  4409. BufferedBlockAlgorithm.reset.call(this);
  4410. // Perform concrete-cipher logic
  4411. this._doReset();
  4412. },
  4413. /**
  4414. * Adds data to be encrypted or decrypted.
  4415. *
  4416. * @param {WordArray|string} dataUpdate The data to encrypt or decrypt.
  4417. *
  4418. * @return {WordArray} The data after processing.
  4419. *
  4420. * @example
  4421. *
  4422. * var encrypted = cipher.process('data');
  4423. * var encrypted = cipher.process(wordArray);
  4424. */
  4425. process: function (dataUpdate) {
  4426. // Append
  4427. this._append(dataUpdate);
  4428. // Process available blocks
  4429. return this._process();
  4430. },
  4431. /**
  4432. * Finalizes the encryption or decryption process.
  4433. * Note that the finalize operation is effectively a destructive, read-once operation.
  4434. *
  4435. * @param {WordArray|string} dataUpdate The final data to encrypt or decrypt.
  4436. *
  4437. * @return {WordArray} The data after final processing.
  4438. *
  4439. * @example
  4440. *
  4441. * var encrypted = cipher.finalize();
  4442. * var encrypted = cipher.finalize('data');
  4443. * var encrypted = cipher.finalize(wordArray);
  4444. */
  4445. finalize: function (dataUpdate) {
  4446. // Final data update
  4447. if (dataUpdate) {
  4448. this._append(dataUpdate);
  4449. }
  4450. // Perform concrete-cipher logic
  4451. var finalProcessedData = this._doFinalize();
  4452. return finalProcessedData;
  4453. },
  4454. keySize: 128/32,
  4455. ivSize: 128/32,
  4456. _ENC_XFORM_MODE: 1,
  4457. _DEC_XFORM_MODE: 2,
  4458. /**
  4459. * Creates shortcut functions to a cipher's object interface.
  4460. *
  4461. * @param {Cipher} cipher The cipher to create a helper for.
  4462. *
  4463. * @return {Object} An object with encrypt and decrypt shortcut functions.
  4464. *
  4465. * @static
  4466. *
  4467. * @example
  4468. *
  4469. * var AES = CryptoJS.lib.Cipher._createHelper(CryptoJS.algo.AES);
  4470. */
  4471. _createHelper: (function () {
  4472. function selectCipherStrategy(key) {
  4473. if (typeof key == 'string') {
  4474. return PasswordBasedCipher;
  4475. } else {
  4476. return SerializableCipher;
  4477. }
  4478. }
  4479. return function (cipher) {
  4480. return {
  4481. encrypt: function (message, key, cfg) {
  4482. return selectCipherStrategy(key).encrypt(cipher, message, key, cfg);
  4483. },
  4484. decrypt: function (ciphertext, key, cfg) {
  4485. return selectCipherStrategy(key).decrypt(cipher, ciphertext, key, cfg);
  4486. }
  4487. };
  4488. };
  4489. }())
  4490. });
  4491. /**
  4492. * Abstract base stream cipher template.
  4493. *
  4494. * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 1 (32 bits)
  4495. */
  4496. var StreamCipher = C_lib.StreamCipher = Cipher.extend({
  4497. _doFinalize: function () {
  4498. // Process partial blocks
  4499. var finalProcessedBlocks = this._process(!!'flush');
  4500. return finalProcessedBlocks;
  4501. },
  4502. blockSize: 1
  4503. });
  4504. /**
  4505. * Mode namespace.
  4506. */
  4507. var C_mode = C.mode = {};
  4508. /**
  4509. * Abstract base block cipher mode template.
  4510. */
  4511. var BlockCipherMode = C_lib.BlockCipherMode = Base.extend({
  4512. /**
  4513. * Creates this mode for encryption.
  4514. *
  4515. * @param {Cipher} cipher A block cipher instance.
  4516. * @param {Array} iv The IV words.
  4517. *
  4518. * @static
  4519. *
  4520. * @example
  4521. *
  4522. * var mode = CryptoJS.mode.CBC.createEncryptor(cipher, iv.words);
  4523. */
  4524. createEncryptor: function (cipher, iv) {
  4525. return this.Encryptor.create(cipher, iv);
  4526. },
  4527. /**
  4528. * Creates this mode for decryption.
  4529. *
  4530. * @param {Cipher} cipher A block cipher instance.
  4531. * @param {Array} iv The IV words.
  4532. *
  4533. * @static
  4534. *
  4535. * @example
  4536. *
  4537. * var mode = CryptoJS.mode.CBC.createDecryptor(cipher, iv.words);
  4538. */
  4539. createDecryptor: function (cipher, iv) {
  4540. return this.Decryptor.create(cipher, iv);
  4541. },
  4542. /**
  4543. * Initializes a newly created mode.
  4544. *
  4545. * @param {Cipher} cipher A block cipher instance.
  4546. * @param {Array} iv The IV words.
  4547. *
  4548. * @example
  4549. *
  4550. * var mode = CryptoJS.mode.CBC.Encryptor.create(cipher, iv.words);
  4551. */
  4552. init: function (cipher, iv) {
  4553. this._cipher = cipher;
  4554. this._iv = iv;
  4555. }
  4556. });
  4557. /**
  4558. * Cipher Block Chaining mode.
  4559. */
  4560. var CBC = C_mode.CBC = (function () {
  4561. /**
  4562. * Abstract base CBC mode.
  4563. */
  4564. var CBC = BlockCipherMode.extend();
  4565. /**
  4566. * CBC encryptor.
  4567. */
  4568. CBC.Encryptor = CBC.extend({
  4569. /**
  4570. * Processes the data block at offset.
  4571. *
  4572. * @param {Array} words The data words to operate on.
  4573. * @param {number} offset The offset where the block starts.
  4574. *
  4575. * @example
  4576. *
  4577. * mode.processBlock(data.words, offset);
  4578. */
  4579. processBlock: function (words, offset) {
  4580. // Shortcuts
  4581. var cipher = this._cipher;
  4582. var blockSize = cipher.blockSize;
  4583. // XOR and encrypt
  4584. xorBlock.call(this, words, offset, blockSize);
  4585. cipher.encryptBlock(words, offset);
  4586. // Remember this block to use with next block
  4587. this._prevBlock = words.slice(offset, offset + blockSize);
  4588. }
  4589. });
  4590. /**
  4591. * CBC decryptor.
  4592. */
  4593. CBC.Decryptor = CBC.extend({
  4594. /**
  4595. * Processes the data block at offset.
  4596. *
  4597. * @param {Array} words The data words to operate on.
  4598. * @param {number} offset The offset where the block starts.
  4599. *
  4600. * @example
  4601. *
  4602. * mode.processBlock(data.words, offset);
  4603. */
  4604. processBlock: function (words, offset) {
  4605. // Shortcuts
  4606. var cipher = this._cipher;
  4607. var blockSize = cipher.blockSize;
  4608. // Remember this block to use with next block
  4609. var thisBlock = words.slice(offset, offset + blockSize);
  4610. // Decrypt and XOR
  4611. cipher.decryptBlock(words, offset);
  4612. xorBlock.call(this, words, offset, blockSize);
  4613. // This block becomes the previous block
  4614. this._prevBlock = thisBlock;
  4615. }
  4616. });
  4617. function xorBlock(words, offset, blockSize) {
  4618. // Shortcut
  4619. var iv = this._iv;
  4620. // Choose mixing block
  4621. if (iv) {
  4622. var block = iv;
  4623. // Remove IV for subsequent blocks
  4624. this._iv = undefined;
  4625. } else {
  4626. var block = this._prevBlock;
  4627. }
  4628. // XOR blocks
  4629. for (var i = 0; i < blockSize; i++) {
  4630. words[offset + i] ^= block[i];
  4631. }
  4632. }
  4633. return CBC;
  4634. }());
  4635. /**
  4636. * Padding namespace.
  4637. */
  4638. var C_pad = C.pad = {};
  4639. /**
  4640. * PKCS #5/7 padding strategy.
  4641. */
  4642. var Pkcs7 = C_pad.Pkcs7 = {
  4643. /**
  4644. * Pads data using the algorithm defined in PKCS #5/7.
  4645. *
  4646. * @param {WordArray} data The data to pad.
  4647. * @param {number} blockSize The multiple that the data should be padded to.
  4648. *
  4649. * @static
  4650. *
  4651. * @example
  4652. *
  4653. * CryptoJS.pad.Pkcs7.pad(wordArray, 4);
  4654. */
  4655. pad: function (data, blockSize) {
  4656. // Shortcut
  4657. var blockSizeBytes = blockSize * 4;
  4658. // Count padding bytes
  4659. var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes;
  4660. // Create padding word
  4661. var paddingWord = (nPaddingBytes << 24) | (nPaddingBytes << 16) | (nPaddingBytes << 8) | nPaddingBytes;
  4662. // Create padding
  4663. var paddingWords = [];
  4664. for (var i = 0; i < nPaddingBytes; i += 4) {
  4665. paddingWords.push(paddingWord);
  4666. }
  4667. var padding = WordArray.create(paddingWords, nPaddingBytes);
  4668. // Add padding
  4669. data.concat(padding);
  4670. },
  4671. /**
  4672. * Unpads data that had been padded using the algorithm defined in PKCS #5/7.
  4673. *
  4674. * @param {WordArray} data The data to unpad.
  4675. *
  4676. * @static
  4677. *
  4678. * @example
  4679. *
  4680. * CryptoJS.pad.Pkcs7.unpad(wordArray);
  4681. */
  4682. unpad: function (data) {
  4683. // Get number of padding bytes from last byte
  4684. var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff;
  4685. // Remove padding
  4686. data.sigBytes -= nPaddingBytes;
  4687. }
  4688. };
  4689. /**
  4690. * Abstract base block cipher template.
  4691. *
  4692. * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 4 (128 bits)
  4693. */
  4694. var BlockCipher = C_lib.BlockCipher = Cipher.extend({
  4695. /**
  4696. * Configuration options.
  4697. *
  4698. * @property {Mode} mode The block mode to use. Default: CBC
  4699. * @property {Padding} padding The padding strategy to use. Default: Pkcs7
  4700. */
  4701. cfg: Cipher.cfg.extend({
  4702. mode: CBC,
  4703. padding: Pkcs7
  4704. }),
  4705. reset: function () {
  4706. // Reset cipher
  4707. Cipher.reset.call(this);
  4708. // Shortcuts
  4709. var cfg = this.cfg;
  4710. var iv = cfg.iv;
  4711. var mode = cfg.mode;
  4712. // Reset block mode
  4713. if (this._xformMode == this._ENC_XFORM_MODE) {
  4714. var modeCreator = mode.createEncryptor;
  4715. } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ {
  4716. var modeCreator = mode.createDecryptor;
  4717. // Keep at least one block in the buffer for unpadding
  4718. this._minBufferSize = 1;
  4719. }
  4720. this._mode = modeCreator.call(mode, this, iv && iv.words);
  4721. },
  4722. _doProcessBlock: function (words, offset) {
  4723. this._mode.processBlock(words, offset);
  4724. },
  4725. _doFinalize: function () {
  4726. // Shortcut
  4727. var padding = this.cfg.padding;
  4728. // Finalize
  4729. if (this._xformMode == this._ENC_XFORM_MODE) {
  4730. // Pad data
  4731. padding.pad(this._data, this.blockSize);
  4732. // Process final blocks
  4733. var finalProcessedBlocks = this._process(!!'flush');
  4734. } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ {
  4735. // Process final blocks
  4736. var finalProcessedBlocks = this._process(!!'flush');
  4737. // Unpad data
  4738. padding.unpad(finalProcessedBlocks);
  4739. }
  4740. return finalProcessedBlocks;
  4741. },
  4742. blockSize: 128/32
  4743. });
  4744. /**
  4745. * A collection of cipher parameters.
  4746. *
  4747. * @property {WordArray} ciphertext The raw ciphertext.
  4748. * @property {WordArray} key The key to this ciphertext.
  4749. * @property {WordArray} iv The IV used in the ciphering operation.
  4750. * @property {WordArray} salt The salt used with a key derivation function.
  4751. * @property {Cipher} algorithm The cipher algorithm.
  4752. * @property {Mode} mode The block mode used in the ciphering operation.
  4753. * @property {Padding} padding The padding scheme used in the ciphering operation.
  4754. * @property {number} blockSize The block size of the cipher.
  4755. * @property {Format} formatter The default formatting strategy to convert this cipher params object to a string.
  4756. */
  4757. var CipherParams = C_lib.CipherParams = Base.extend({
  4758. /**
  4759. * Initializes a newly created cipher params object.
  4760. *
  4761. * @param {Object} cipherParams An object with any of the possible cipher parameters.
  4762. *
  4763. * @example
  4764. *
  4765. * var cipherParams = CryptoJS.lib.CipherParams.create({
  4766. * ciphertext: ciphertextWordArray,
  4767. * key: keyWordArray,
  4768. * iv: ivWordArray,
  4769. * salt: saltWordArray,
  4770. * algorithm: CryptoJS.algo.AES,
  4771. * mode: CryptoJS.mode.CBC,
  4772. * padding: CryptoJS.pad.PKCS7,
  4773. * blockSize: 4,
  4774. * formatter: CryptoJS.format.OpenSSL
  4775. * });
  4776. */
  4777. init: function (cipherParams) {
  4778. this.mixIn(cipherParams);
  4779. },
  4780. /**
  4781. * Converts this cipher params object to a string.
  4782. *
  4783. * @param {Format} formatter (Optional) The formatting strategy to use.
  4784. *
  4785. * @return {string} The stringified cipher params.
  4786. *
  4787. * @throws Error If neither the formatter nor the default formatter is set.
  4788. *
  4789. * @example
  4790. *
  4791. * var string = cipherParams + '';
  4792. * var string = cipherParams.toString();
  4793. * var string = cipherParams.toString(CryptoJS.format.OpenSSL);
  4794. */
  4795. toString: function (formatter) {
  4796. return (formatter || this.formatter).stringify(this);
  4797. }
  4798. });
  4799. /**
  4800. * Format namespace.
  4801. */
  4802. var C_format = C.format = {};
  4803. /**
  4804. * OpenSSL formatting strategy.
  4805. */
  4806. var OpenSSLFormatter = C_format.OpenSSL = {
  4807. /**
  4808. * Converts a cipher params object to an OpenSSL-compatible string.
  4809. *
  4810. * @param {CipherParams} cipherParams The cipher params object.
  4811. *
  4812. * @return {string} The OpenSSL-compatible string.
  4813. *
  4814. * @static
  4815. *
  4816. * @example
  4817. *
  4818. * var openSSLString = CryptoJS.format.OpenSSL.stringify(cipherParams);
  4819. */
  4820. stringify: function (cipherParams) {
  4821. // Shortcuts
  4822. var ciphertext = cipherParams.ciphertext;
  4823. var salt = cipherParams.salt;
  4824. // Format
  4825. if (salt) {
  4826. var wordArray = WordArray.create([0x53616c74, 0x65645f5f]).concat(salt).concat(ciphertext);
  4827. } else {
  4828. var wordArray = ciphertext;
  4829. }
  4830. return wordArray.toString(Base64);
  4831. },
  4832. /**
  4833. * Converts an OpenSSL-compatible string to a cipher params object.
  4834. *
  4835. * @param {string} openSSLStr The OpenSSL-compatible string.
  4836. *
  4837. * @return {CipherParams} The cipher params object.
  4838. *
  4839. * @static
  4840. *
  4841. * @example
  4842. *
  4843. * var cipherParams = CryptoJS.format.OpenSSL.parse(openSSLString);
  4844. */
  4845. parse: function (openSSLStr) {
  4846. // Parse base64
  4847. var ciphertext = Base64.parse(openSSLStr);
  4848. // Shortcut
  4849. var ciphertextWords = ciphertext.words;
  4850. // Test for salt
  4851. if (ciphertextWords[0] == 0x53616c74 && ciphertextWords[1] == 0x65645f5f) {
  4852. // Extract salt
  4853. var salt = WordArray.create(ciphertextWords.slice(2, 4));
  4854. // Remove salt from ciphertext
  4855. ciphertextWords.splice(0, 4);
  4856. ciphertext.sigBytes -= 16;
  4857. }
  4858. return CipherParams.create({ ciphertext: ciphertext, salt: salt });
  4859. }
  4860. };
  4861. /**
  4862. * A cipher wrapper that returns ciphertext as a serializable cipher params object.
  4863. */
  4864. var SerializableCipher = C_lib.SerializableCipher = Base.extend({
  4865. /**
  4866. * Configuration options.
  4867. *
  4868. * @property {Formatter} format The formatting strategy to convert cipher param objects to and from a string. Default: OpenSSL
  4869. */
  4870. cfg: Base.extend({
  4871. format: OpenSSLFormatter
  4872. }),
  4873. /**
  4874. * Encrypts a message.
  4875. *
  4876. * @param {Cipher} cipher The cipher algorithm to use.
  4877. * @param {WordArray|string} message The message to encrypt.
  4878. * @param {WordArray} key The key.
  4879. * @param {Object} cfg (Optional) The configuration options to use for this operation.
  4880. *
  4881. * @return {CipherParams} A cipher params object.
  4882. *
  4883. * @static
  4884. *
  4885. * @example
  4886. *
  4887. * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key);
  4888. * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv });
  4889. * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv, format: CryptoJS.format.OpenSSL });
  4890. */
  4891. encrypt: function (cipher, message, key, cfg) {
  4892. // Apply config defaults
  4893. cfg = this.cfg.extend(cfg);
  4894. // Encrypt
  4895. var encryptor = cipher.createEncryptor(key, cfg);
  4896. var ciphertext = encryptor.finalize(message);
  4897. // Shortcut
  4898. var cipherCfg = encryptor.cfg;
  4899. // Create and return serializable cipher params
  4900. return CipherParams.create({
  4901. ciphertext: ciphertext,
  4902. key: key,
  4903. iv: cipherCfg.iv,
  4904. algorithm: cipher,
  4905. mode: cipherCfg.mode,
  4906. padding: cipherCfg.padding,
  4907. blockSize: cipher.blockSize,
  4908. formatter: cfg.format
  4909. });
  4910. },
  4911. /**
  4912. * Decrypts serialized ciphertext.
  4913. *
  4914. * @param {Cipher} cipher The cipher algorithm to use.
  4915. * @param {CipherParams|string} ciphertext The ciphertext to decrypt.
  4916. * @param {WordArray} key The key.
  4917. * @param {Object} cfg (Optional) The configuration options to use for this operation.
  4918. *
  4919. * @return {WordArray} The plaintext.
  4920. *
  4921. * @static
  4922. *
  4923. * @example
  4924. *
  4925. * var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, key, { iv: iv, format: CryptoJS.format.OpenSSL });
  4926. * var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, key, { iv: iv, format: CryptoJS.format.OpenSSL });
  4927. */
  4928. decrypt: function (cipher, ciphertext, key, cfg) {
  4929. // Apply config defaults
  4930. cfg = this.cfg.extend(cfg);
  4931. // Convert string to CipherParams
  4932. ciphertext = this._parse(ciphertext, cfg.format);
  4933. // Decrypt
  4934. var plaintext = cipher.createDecryptor(key, cfg).finalize(ciphertext.ciphertext);
  4935. return plaintext;
  4936. },
  4937. /**
  4938. * Converts serialized ciphertext to CipherParams,
  4939. * else assumed CipherParams already and returns ciphertext unchanged.
  4940. *
  4941. * @param {CipherParams|string} ciphertext The ciphertext.
  4942. * @param {Formatter} format The formatting strategy to use to parse serialized ciphertext.
  4943. *
  4944. * @return {CipherParams} The unserialized ciphertext.
  4945. *
  4946. * @static
  4947. *
  4948. * @example
  4949. *
  4950. * var ciphertextParams = CryptoJS.lib.SerializableCipher._parse(ciphertextStringOrParams, format);
  4951. */
  4952. _parse: function (ciphertext, format) {
  4953. if (typeof ciphertext == 'string') {
  4954. return format.parse(ciphertext, this);
  4955. } else {
  4956. return ciphertext;
  4957. }
  4958. }
  4959. });
  4960. /**
  4961. * Key derivation function namespace.
  4962. */
  4963. var C_kdf = C.kdf = {};
  4964. /**
  4965. * OpenSSL key derivation function.
  4966. */
  4967. var OpenSSLKdf = C_kdf.OpenSSL = {
  4968. /**
  4969. * Derives a key and IV from a password.
  4970. *
  4971. * @param {string} password The password to derive from.
  4972. * @param {number} keySize The size in words of the key to generate.
  4973. * @param {number} ivSize The size in words of the IV to generate.
  4974. * @param {WordArray|string} salt (Optional) A 64-bit salt to use. If omitted, a salt will be generated randomly.
  4975. *
  4976. * @return {CipherParams} A cipher params object with the key, IV, and salt.
  4977. *
  4978. * @static
  4979. *
  4980. * @example
  4981. *
  4982. * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32);
  4983. * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt');
  4984. */
  4985. execute: function (password, keySize, ivSize, salt) {
  4986. // Generate random salt
  4987. if (!salt) {
  4988. salt = WordArray.random(64/8);
  4989. }
  4990. // Derive key and IV
  4991. var key = EvpKDF.create({ keySize: keySize + ivSize }).compute(password, salt);
  4992. // Separate key and IV
  4993. var iv = WordArray.create(key.words.slice(keySize), ivSize * 4);
  4994. key.sigBytes = keySize * 4;
  4995. // Return params
  4996. return CipherParams.create({ key: key, iv: iv, salt: salt });
  4997. }
  4998. };
  4999. /**
  5000. * A serializable cipher wrapper that derives the key from a password,
  5001. * and returns ciphertext as a serializable cipher params object.
  5002. */
  5003. var PasswordBasedCipher = C_lib.PasswordBasedCipher = SerializableCipher.extend({
  5004. /**
  5005. * Configuration options.
  5006. *
  5007. * @property {KDF} kdf The key derivation function to use to generate a key and IV from a password. Default: OpenSSL
  5008. */
  5009. cfg: SerializableCipher.cfg.extend({
  5010. kdf: OpenSSLKdf
  5011. }),
  5012. /**
  5013. * Encrypts a message using a password.
  5014. *
  5015. * @param {Cipher} cipher The cipher algorithm to use.
  5016. * @param {WordArray|string} message The message to encrypt.
  5017. * @param {string} password The password.
  5018. * @param {Object} cfg (Optional) The configuration options to use for this operation.
  5019. *
  5020. * @return {CipherParams} A cipher params object.
  5021. *
  5022. * @static
  5023. *
  5024. * @example
  5025. *
  5026. * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password');
  5027. * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password', { format: CryptoJS.format.OpenSSL });
  5028. */
  5029. encrypt: function (cipher, message, password, cfg) {
  5030. // Apply config defaults
  5031. cfg = this.cfg.extend(cfg);
  5032. // Derive key and other params
  5033. var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize);
  5034. // Add IV to config
  5035. cfg.iv = derivedParams.iv;
  5036. // Encrypt
  5037. var ciphertext = SerializableCipher.encrypt.call(this, cipher, message, derivedParams.key, cfg);
  5038. // Mix in derived params
  5039. ciphertext.mixIn(derivedParams);
  5040. return ciphertext;
  5041. },
  5042. /**
  5043. * Decrypts serialized ciphertext using a password.
  5044. *
  5045. * @param {Cipher} cipher The cipher algorithm to use.
  5046. * @param {CipherParams|string} ciphertext The ciphertext to decrypt.
  5047. * @param {string} password The password.
  5048. * @param {Object} cfg (Optional) The configuration options to use for this operation.
  5049. *
  5050. * @return {WordArray} The plaintext.
  5051. *
  5052. * @static
  5053. *
  5054. * @example
  5055. *
  5056. * var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, 'password', { format: CryptoJS.format.OpenSSL });
  5057. * var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, 'password', { format: CryptoJS.format.OpenSSL });
  5058. */
  5059. decrypt: function (cipher, ciphertext, password, cfg) {
  5060. // Apply config defaults
  5061. cfg = this.cfg.extend(cfg);
  5062. // Convert string to CipherParams
  5063. ciphertext = this._parse(ciphertext, cfg.format);
  5064. // Derive key and other params
  5065. var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize, ciphertext.salt);
  5066. // Add IV to config
  5067. cfg.iv = derivedParams.iv;
  5068. // Decrypt
  5069. var plaintext = SerializableCipher.decrypt.call(this, cipher, ciphertext, derivedParams.key, cfg);
  5070. return plaintext;
  5071. }
  5072. });
  5073. }());
  5074. define("crypto.cipher-core", ["crypto.enc-base64","crypto.evpkdf"], function(){});
  5075. /*
  5076. CryptoJS v3.1.2
  5077. code.google.com/p/crypto-js
  5078. (c) 2009-2013 by Jeff Mott. All rights reserved.
  5079. code.google.com/p/crypto-js/wiki/License
  5080. */
  5081. (function () {
  5082. // Shortcuts
  5083. var C = CryptoJS;
  5084. var C_lib = C.lib;
  5085. var BlockCipher = C_lib.BlockCipher;
  5086. var C_algo = C.algo;
  5087. // Lookup tables
  5088. var SBOX = [];
  5089. var INV_SBOX = [];
  5090. var SUB_MIX_0 = [];
  5091. var SUB_MIX_1 = [];
  5092. var SUB_MIX_2 = [];
  5093. var SUB_MIX_3 = [];
  5094. var INV_SUB_MIX_0 = [];
  5095. var INV_SUB_MIX_1 = [];
  5096. var INV_SUB_MIX_2 = [];
  5097. var INV_SUB_MIX_3 = [];
  5098. // Compute lookup tables
  5099. (function () {
  5100. // Compute double table
  5101. var d = [];
  5102. for (var i = 0; i < 256; i++) {
  5103. if (i < 128) {
  5104. d[i] = i << 1;
  5105. } else {
  5106. d[i] = (i << 1) ^ 0x11b;
  5107. }
  5108. }
  5109. // Walk GF(2^8)
  5110. var x = 0;
  5111. var xi = 0;
  5112. for (var i = 0; i < 256; i++) {
  5113. // Compute sbox
  5114. var sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4);
  5115. sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63;
  5116. SBOX[x] = sx;
  5117. INV_SBOX[sx] = x;
  5118. // Compute multiplication
  5119. var x2 = d[x];
  5120. var x4 = d[x2];
  5121. var x8 = d[x4];
  5122. // Compute sub bytes, mix columns tables
  5123. var t = (d[sx] * 0x101) ^ (sx * 0x1010100);
  5124. SUB_MIX_0[x] = (t << 24) | (t >>> 8);
  5125. SUB_MIX_1[x] = (t << 16) | (t >>> 16);
  5126. SUB_MIX_2[x] = (t << 8) | (t >>> 24);
  5127. SUB_MIX_3[x] = t;
  5128. // Compute inv sub bytes, inv mix columns tables
  5129. var t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100);
  5130. INV_SUB_MIX_0[sx] = (t << 24) | (t >>> 8);
  5131. INV_SUB_MIX_1[sx] = (t << 16) | (t >>> 16);
  5132. INV_SUB_MIX_2[sx] = (t << 8) | (t >>> 24);
  5133. INV_SUB_MIX_3[sx] = t;
  5134. // Compute next counter
  5135. if (!x) {
  5136. x = xi = 1;
  5137. } else {
  5138. x = x2 ^ d[d[d[x8 ^ x2]]];
  5139. xi ^= d[d[xi]];
  5140. }
  5141. }
  5142. }());
  5143. // Precomputed Rcon lookup
  5144. var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];
  5145. /**
  5146. * AES block cipher algorithm.
  5147. */
  5148. var AES = C_algo.AES = BlockCipher.extend({
  5149. _doReset: function () {
  5150. // Shortcuts
  5151. var key = this._key;
  5152. var keyWords = key.words;
  5153. var keySize = key.sigBytes / 4;
  5154. // Compute number of rounds
  5155. var nRounds = this._nRounds = keySize + 6
  5156. // Compute number of key schedule rows
  5157. var ksRows = (nRounds + 1) * 4;
  5158. // Compute key schedule
  5159. var keySchedule = this._keySchedule = [];
  5160. for (var ksRow = 0; ksRow < ksRows; ksRow++) {
  5161. if (ksRow < keySize) {
  5162. keySchedule[ksRow] = keyWords[ksRow];
  5163. } else {
  5164. var t = keySchedule[ksRow - 1];
  5165. if (!(ksRow % keySize)) {
  5166. // Rot word
  5167. t = (t << 8) | (t >>> 24);
  5168. // Sub word
  5169. t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff];
  5170. // Mix Rcon
  5171. t ^= RCON[(ksRow / keySize) | 0] << 24;
  5172. } else if (keySize > 6 && ksRow % keySize == 4) {
  5173. // Sub word
  5174. t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff];
  5175. }
  5176. keySchedule[ksRow] = keySchedule[ksRow - keySize] ^ t;
  5177. }
  5178. }
  5179. // Compute inv key schedule
  5180. var invKeySchedule = this._invKeySchedule = [];
  5181. for (var invKsRow = 0; invKsRow < ksRows; invKsRow++) {
  5182. var ksRow = ksRows - invKsRow;
  5183. if (invKsRow % 4) {
  5184. var t = keySchedule[ksRow];
  5185. } else {
  5186. var t = keySchedule[ksRow - 4];
  5187. }
  5188. if (invKsRow < 4 || ksRow <= 4) {
  5189. invKeySchedule[invKsRow] = t;
  5190. } else {
  5191. invKeySchedule[invKsRow] = INV_SUB_MIX_0[SBOX[t >>> 24]] ^ INV_SUB_MIX_1[SBOX[(t >>> 16) & 0xff]] ^
  5192. INV_SUB_MIX_2[SBOX[(t >>> 8) & 0xff]] ^ INV_SUB_MIX_3[SBOX[t & 0xff]];
  5193. }
  5194. }
  5195. },
  5196. encryptBlock: function (M, offset) {
  5197. this._doCryptBlock(M, offset, this._keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX);
  5198. },
  5199. decryptBlock: function (M, offset) {
  5200. // Swap 2nd and 4th rows
  5201. var t = M[offset + 1];
  5202. M[offset + 1] = M[offset + 3];
  5203. M[offset + 3] = t;
  5204. this._doCryptBlock(M, offset, this._invKeySchedule, INV_SUB_MIX_0, INV_SUB_MIX_1, INV_SUB_MIX_2, INV_SUB_MIX_3, INV_SBOX);
  5205. // Inv swap 2nd and 4th rows
  5206. var t = M[offset + 1];
  5207. M[offset + 1] = M[offset + 3];
  5208. M[offset + 3] = t;
  5209. },
  5210. _doCryptBlock: function (M, offset, keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX) {
  5211. // Shortcut
  5212. var nRounds = this._nRounds;
  5213. // Get input, add round key
  5214. var s0 = M[offset] ^ keySchedule[0];
  5215. var s1 = M[offset + 1] ^ keySchedule[1];
  5216. var s2 = M[offset + 2] ^ keySchedule[2];
  5217. var s3 = M[offset + 3] ^ keySchedule[3];
  5218. // Key schedule row counter
  5219. var ksRow = 4;
  5220. // Rounds
  5221. for (var round = 1; round < nRounds; round++) {
  5222. // Shift rows, sub bytes, mix columns, add round key
  5223. var t0 = SUB_MIX_0[s0 >>> 24] ^ SUB_MIX_1[(s1 >>> 16) & 0xff] ^ SUB_MIX_2[(s2 >>> 8) & 0xff] ^ SUB_MIX_3[s3 & 0xff] ^ keySchedule[ksRow++];
  5224. var t1 = SUB_MIX_0[s1 >>> 24] ^ SUB_MIX_1[(s2 >>> 16) & 0xff] ^ SUB_MIX_2[(s3 >>> 8) & 0xff] ^ SUB_MIX_3[s0 & 0xff] ^ keySchedule[ksRow++];
  5225. var t2 = SUB_MIX_0[s2 >>> 24] ^ SUB_MIX_1[(s3 >>> 16) & 0xff] ^ SUB_MIX_2[(s0 >>> 8) & 0xff] ^ SUB_MIX_3[s1 & 0xff] ^ keySchedule[ksRow++];
  5226. var t3 = SUB_MIX_0[s3 >>> 24] ^ SUB_MIX_1[(s0 >>> 16) & 0xff] ^ SUB_MIX_2[(s1 >>> 8) & 0xff] ^ SUB_MIX_3[s2 & 0xff] ^ keySchedule[ksRow++];
  5227. // Update state
  5228. s0 = t0;
  5229. s1 = t1;
  5230. s2 = t2;
  5231. s3 = t3;
  5232. }
  5233. // Shift rows, sub bytes, add round key
  5234. var t0 = ((SBOX[s0 >>> 24] << 24) | (SBOX[(s1 >>> 16) & 0xff] << 16) | (SBOX[(s2 >>> 8) & 0xff] << 8) | SBOX[s3 & 0xff]) ^ keySchedule[ksRow++];
  5235. var t1 = ((SBOX[s1 >>> 24] << 24) | (SBOX[(s2 >>> 16) & 0xff] << 16) | (SBOX[(s3 >>> 8) & 0xff] << 8) | SBOX[s0 & 0xff]) ^ keySchedule[ksRow++];
  5236. var t2 = ((SBOX[s2 >>> 24] << 24) | (SBOX[(s3 >>> 16) & 0xff] << 16) | (SBOX[(s0 >>> 8) & 0xff] << 8) | SBOX[s1 & 0xff]) ^ keySchedule[ksRow++];
  5237. var t3 = ((SBOX[s3 >>> 24] << 24) | (SBOX[(s0 >>> 16) & 0xff] << 16) | (SBOX[(s1 >>> 8) & 0xff] << 8) | SBOX[s2 & 0xff]) ^ keySchedule[ksRow++];
  5238. // Set output
  5239. M[offset] = t0;
  5240. M[offset + 1] = t1;
  5241. M[offset + 2] = t2;
  5242. M[offset + 3] = t3;
  5243. },
  5244. keySize: 256/32
  5245. });
  5246. /**
  5247. * Shortcut functions to the cipher's object interface.
  5248. *
  5249. * @example
  5250. *
  5251. * var ciphertext = CryptoJS.AES.encrypt(message, key, cfg);
  5252. * var plaintext = CryptoJS.AES.decrypt(ciphertext, key, cfg);
  5253. */
  5254. C.AES = BlockCipher._createHelper(AES);
  5255. }());
  5256. define("crypto.aes", ["crypto.cipher-core"], function(){});
  5257. /*
  5258. CryptoJS v3.1.2
  5259. code.google.com/p/crypto-js
  5260. (c) 2009-2013 by Jeff Mott. All rights reserved.
  5261. code.google.com/p/crypto-js/wiki/License
  5262. */
  5263. (function () {
  5264. // Shortcuts
  5265. var C = CryptoJS;
  5266. var C_lib = C.lib;
  5267. var WordArray = C_lib.WordArray;
  5268. var Hasher = C_lib.Hasher;
  5269. var C_algo = C.algo;
  5270. // Reusable object
  5271. var W = [];
  5272. /**
  5273. * SHA-1 hash algorithm.
  5274. */
  5275. var SHA1 = C_algo.SHA1 = Hasher.extend({
  5276. _doReset: function () {
  5277. this._hash = new WordArray.init([
  5278. 0x67452301, 0xefcdab89,
  5279. 0x98badcfe, 0x10325476,
  5280. 0xc3d2e1f0
  5281. ]);
  5282. },
  5283. _doProcessBlock: function (M, offset) {
  5284. // Shortcut
  5285. var H = this._hash.words;
  5286. // Working variables
  5287. var a = H[0];
  5288. var b = H[1];
  5289. var c = H[2];
  5290. var d = H[3];
  5291. var e = H[4];
  5292. // Computation
  5293. for (var i = 0; i < 80; i++) {
  5294. if (i < 16) {
  5295. W[i] = M[offset + i] | 0;
  5296. } else {
  5297. var n = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];
  5298. W[i] = (n << 1) | (n >>> 31);
  5299. }
  5300. var t = ((a << 5) | (a >>> 27)) + e + W[i];
  5301. if (i < 20) {
  5302. t += ((b & c) | (~b & d)) + 0x5a827999;
  5303. } else if (i < 40) {
  5304. t += (b ^ c ^ d) + 0x6ed9eba1;
  5305. } else if (i < 60) {
  5306. t += ((b & c) | (b & d) | (c & d)) - 0x70e44324;
  5307. } else /* if (i < 80) */ {
  5308. t += (b ^ c ^ d) - 0x359d3e2a;
  5309. }
  5310. e = d;
  5311. d = c;
  5312. c = (b << 30) | (b >>> 2);
  5313. b = a;
  5314. a = t;
  5315. }
  5316. // Intermediate hash value
  5317. H[0] = (H[0] + a) | 0;
  5318. H[1] = (H[1] + b) | 0;
  5319. H[2] = (H[2] + c) | 0;
  5320. H[3] = (H[3] + d) | 0;
  5321. H[4] = (H[4] + e) | 0;
  5322. },
  5323. _doFinalize: function () {
  5324. // Shortcuts
  5325. var data = this._data;
  5326. var dataWords = data.words;
  5327. var nBitsTotal = this._nDataBytes * 8;
  5328. var nBitsLeft = data.sigBytes * 8;
  5329. // Add padding
  5330. dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
  5331. dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);
  5332. dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal;
  5333. data.sigBytes = dataWords.length * 4;
  5334. // Hash final blocks
  5335. this._process();
  5336. // Return final computed hash
  5337. return this._hash;
  5338. },
  5339. clone: function () {
  5340. var clone = Hasher.clone.call(this);
  5341. clone._hash = this._hash.clone();
  5342. return clone;
  5343. }
  5344. });
  5345. /**
  5346. * Shortcut function to the hasher's object interface.
  5347. *
  5348. * @param {WordArray|string} message The message to hash.
  5349. *
  5350. * @return {WordArray} The hash.
  5351. *
  5352. * @static
  5353. *
  5354. * @example
  5355. *
  5356. * var hash = CryptoJS.SHA1('message');
  5357. * var hash = CryptoJS.SHA1(wordArray);
  5358. */
  5359. C.SHA1 = Hasher._createHelper(SHA1);
  5360. /**
  5361. * Shortcut function to the HMAC's object interface.
  5362. *
  5363. * @param {WordArray|string} message The message to hash.
  5364. * @param {WordArray|string} key The secret key.
  5365. *
  5366. * @return {WordArray} The HMAC.
  5367. *
  5368. * @static
  5369. *
  5370. * @example
  5371. *
  5372. * var hmac = CryptoJS.HmacSHA1(message, key);
  5373. */
  5374. C.HmacSHA1 = Hasher._createHmacHelper(SHA1);
  5375. }());
  5376. define("crypto.sha1", ["crypto.core"], function(){});
  5377. /*
  5378. CryptoJS v3.1.2
  5379. code.google.com/p/crypto-js
  5380. (c) 2009-2013 by Jeff Mott. All rights reserved.
  5381. code.google.com/p/crypto-js/wiki/License
  5382. */
  5383. (function (Math) {
  5384. // Shortcuts
  5385. var C = CryptoJS;
  5386. var C_lib = C.lib;
  5387. var WordArray = C_lib.WordArray;
  5388. var Hasher = C_lib.Hasher;
  5389. var C_algo = C.algo;
  5390. // Initialization and round constants tables
  5391. var H = [];
  5392. var K = [];
  5393. // Compute constants
  5394. (function () {
  5395. function isPrime(n) {
  5396. var sqrtN = Math.sqrt(n);
  5397. for (var factor = 2; factor <= sqrtN; factor++) {
  5398. if (!(n % factor)) {
  5399. return false;
  5400. }
  5401. }
  5402. return true;
  5403. }
  5404. function getFractionalBits(n) {
  5405. return ((n - (n | 0)) * 0x100000000) | 0;
  5406. }
  5407. var n = 2;
  5408. var nPrime = 0;
  5409. while (nPrime < 64) {
  5410. if (isPrime(n)) {
  5411. if (nPrime < 8) {
  5412. H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2));
  5413. }
  5414. K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3));
  5415. nPrime++;
  5416. }
  5417. n++;
  5418. }
  5419. }());
  5420. // Reusable object
  5421. var W = [];
  5422. /**
  5423. * SHA-256 hash algorithm.
  5424. */
  5425. var SHA256 = C_algo.SHA256 = Hasher.extend({
  5426. _doReset: function () {
  5427. this._hash = new WordArray.init(H.slice(0));
  5428. },
  5429. _doProcessBlock: function (M, offset) {
  5430. // Shortcut
  5431. var H = this._hash.words;
  5432. // Working variables
  5433. var a = H[0];
  5434. var b = H[1];
  5435. var c = H[2];
  5436. var d = H[3];
  5437. var e = H[4];
  5438. var f = H[5];
  5439. var g = H[6];
  5440. var h = H[7];
  5441. // Computation
  5442. for (var i = 0; i < 64; i++) {
  5443. if (i < 16) {
  5444. W[i] = M[offset + i] | 0;
  5445. } else {
  5446. var gamma0x = W[i - 15];
  5447. var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^
  5448. ((gamma0x << 14) | (gamma0x >>> 18)) ^
  5449. (gamma0x >>> 3);
  5450. var gamma1x = W[i - 2];
  5451. var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^
  5452. ((gamma1x << 13) | (gamma1x >>> 19)) ^
  5453. (gamma1x >>> 10);
  5454. W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16];
  5455. }
  5456. var ch = (e & f) ^ (~e & g);
  5457. var maj = (a & b) ^ (a & c) ^ (b & c);
  5458. var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22));
  5459. var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25));
  5460. var t1 = h + sigma1 + ch + K[i] + W[i];
  5461. var t2 = sigma0 + maj;
  5462. h = g;
  5463. g = f;
  5464. f = e;
  5465. e = (d + t1) | 0;
  5466. d = c;
  5467. c = b;
  5468. b = a;
  5469. a = (t1 + t2) | 0;
  5470. }
  5471. // Intermediate hash value
  5472. H[0] = (H[0] + a) | 0;
  5473. H[1] = (H[1] + b) | 0;
  5474. H[2] = (H[2] + c) | 0;
  5475. H[3] = (H[3] + d) | 0;
  5476. H[4] = (H[4] + e) | 0;
  5477. H[5] = (H[5] + f) | 0;
  5478. H[6] = (H[6] + g) | 0;
  5479. H[7] = (H[7] + h) | 0;
  5480. },
  5481. _doFinalize: function () {
  5482. // Shortcuts
  5483. var data = this._data;
  5484. var dataWords = data.words;
  5485. var nBitsTotal = this._nDataBytes * 8;
  5486. var nBitsLeft = data.sigBytes * 8;
  5487. // Add padding
  5488. dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
  5489. dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);
  5490. dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal;
  5491. data.sigBytes = dataWords.length * 4;
  5492. // Hash final blocks
  5493. this._process();
  5494. // Return final computed hash
  5495. return this._hash;
  5496. },
  5497. clone: function () {
  5498. var clone = Hasher.clone.call(this);
  5499. clone._hash = this._hash.clone();
  5500. return clone;
  5501. }
  5502. });
  5503. /**
  5504. * Shortcut function to the hasher's object interface.
  5505. *
  5506. * @param {WordArray|string} message The message to hash.
  5507. *
  5508. * @return {WordArray} The hash.
  5509. *
  5510. * @static
  5511. *
  5512. * @example
  5513. *
  5514. * var hash = CryptoJS.SHA256('message');
  5515. * var hash = CryptoJS.SHA256(wordArray);
  5516. */
  5517. C.SHA256 = Hasher._createHelper(SHA256);
  5518. /**
  5519. * Shortcut function to the HMAC's object interface.
  5520. *
  5521. * @param {WordArray|string} message The message to hash.
  5522. * @param {WordArray|string} key The secret key.
  5523. *
  5524. * @return {WordArray} The HMAC.
  5525. *
  5526. * @static
  5527. *
  5528. * @example
  5529. *
  5530. * var hmac = CryptoJS.HmacSHA256(message, key);
  5531. */
  5532. C.HmacSHA256 = Hasher._createHmacHelper(SHA256);
  5533. }(Math));
  5534. define("crypto.sha256", ["crypto.core"], function(){});
  5535. /*
  5536. CryptoJS v3.1.2
  5537. code.google.com/p/crypto-js
  5538. (c) 2009-2013 by Jeff Mott. All rights reserved.
  5539. code.google.com/p/crypto-js/wiki/License
  5540. */
  5541. (function () {
  5542. // Shortcuts
  5543. var C = CryptoJS;
  5544. var C_lib = C.lib;
  5545. var Base = C_lib.Base;
  5546. var C_enc = C.enc;
  5547. var Utf8 = C_enc.Utf8;
  5548. var C_algo = C.algo;
  5549. /**
  5550. * HMAC algorithm.
  5551. */
  5552. var HMAC = C_algo.HMAC = Base.extend({
  5553. /**
  5554. * Initializes a newly created HMAC.
  5555. *
  5556. * @param {Hasher} hasher The hash algorithm to use.
  5557. * @param {WordArray|string} key The secret key.
  5558. *
  5559. * @example
  5560. *
  5561. * var hmacHasher = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);
  5562. */
  5563. init: function (hasher, key) {
  5564. // Init hasher
  5565. hasher = this._hasher = new hasher.init();
  5566. // Convert string to WordArray, else assume WordArray already
  5567. if (typeof key == 'string') {
  5568. key = Utf8.parse(key);
  5569. }
  5570. // Shortcuts
  5571. var hasherBlockSize = hasher.blockSize;
  5572. var hasherBlockSizeBytes = hasherBlockSize * 4;
  5573. // Allow arbitrary length keys
  5574. if (key.sigBytes > hasherBlockSizeBytes) {
  5575. key = hasher.finalize(key);
  5576. }
  5577. // Clamp excess bits
  5578. key.clamp();
  5579. // Clone key for inner and outer pads
  5580. var oKey = this._oKey = key.clone();
  5581. var iKey = this._iKey = key.clone();
  5582. // Shortcuts
  5583. var oKeyWords = oKey.words;
  5584. var iKeyWords = iKey.words;
  5585. // XOR keys with pad constants
  5586. for (var i = 0; i < hasherBlockSize; i++) {
  5587. oKeyWords[i] ^= 0x5c5c5c5c;
  5588. iKeyWords[i] ^= 0x36363636;
  5589. }
  5590. oKey.sigBytes = iKey.sigBytes = hasherBlockSizeBytes;
  5591. // Set initial values
  5592. this.reset();
  5593. },
  5594. /**
  5595. * Resets this HMAC to its initial state.
  5596. *
  5597. * @example
  5598. *
  5599. * hmacHasher.reset();
  5600. */
  5601. reset: function () {
  5602. // Shortcut
  5603. var hasher = this._hasher;
  5604. // Reset
  5605. hasher.reset();
  5606. hasher.update(this._iKey);
  5607. },
  5608. /**
  5609. * Updates this HMAC with a message.
  5610. *
  5611. * @param {WordArray|string} messageUpdate The message to append.
  5612. *
  5613. * @return {HMAC} This HMAC instance.
  5614. *
  5615. * @example
  5616. *
  5617. * hmacHasher.update('message');
  5618. * hmacHasher.update(wordArray);
  5619. */
  5620. update: function (messageUpdate) {
  5621. this._hasher.update(messageUpdate);
  5622. // Chainable
  5623. return this;
  5624. },
  5625. /**
  5626. * Finalizes the HMAC computation.
  5627. * Note that the finalize operation is effectively a destructive, read-once operation.
  5628. *
  5629. * @param {WordArray|string} messageUpdate (Optional) A final message update.
  5630. *
  5631. * @return {WordArray} The HMAC.
  5632. *
  5633. * @example
  5634. *
  5635. * var hmac = hmacHasher.finalize();
  5636. * var hmac = hmacHasher.finalize('message');
  5637. * var hmac = hmacHasher.finalize(wordArray);
  5638. */
  5639. finalize: function (messageUpdate) {
  5640. // Shortcut
  5641. var hasher = this._hasher;
  5642. // Compute HMAC
  5643. var innerHash = hasher.finalize(messageUpdate);
  5644. hasher.reset();
  5645. var hmac = hasher.finalize(this._oKey.clone().concat(innerHash));
  5646. return hmac;
  5647. }
  5648. });
  5649. }());
  5650. define("crypto.hmac", ["crypto.core"], function(){});
  5651. /*
  5652. CryptoJS v3.1.2
  5653. code.google.com/p/crypto-js
  5654. (c) 2009-2013 by Jeff Mott. All rights reserved.
  5655. code.google.com/p/crypto-js/wiki/License
  5656. */
  5657. /**
  5658. * A noop padding strategy.
  5659. */
  5660. CryptoJS.pad.NoPadding = {
  5661. pad: function () {
  5662. },
  5663. unpad: function () {
  5664. }
  5665. };
  5666. define("crypto.pad-nopadding", ["crypto.cipher-core"], function(){});
  5667. /*
  5668. CryptoJS v3.1.2
  5669. code.google.com/p/crypto-js
  5670. (c) 2009-2013 by Jeff Mott. All rights reserved.
  5671. code.google.com/p/crypto-js/wiki/License
  5672. */
  5673. /**
  5674. * Counter block mode.
  5675. */
  5676. CryptoJS.mode.CTR = (function () {
  5677. var CTR = CryptoJS.lib.BlockCipherMode.extend();
  5678. var Encryptor = CTR.Encryptor = CTR.extend({
  5679. processBlock: function (words, offset) {
  5680. // Shortcuts
  5681. var cipher = this._cipher
  5682. var blockSize = cipher.blockSize;
  5683. var iv = this._iv;
  5684. var counter = this._counter;
  5685. // Generate keystream
  5686. if (iv) {
  5687. counter = this._counter = iv.slice(0);
  5688. // Remove IV for subsequent blocks
  5689. this._iv = undefined;
  5690. }
  5691. var keystream = counter.slice(0);
  5692. cipher.encryptBlock(keystream, 0);
  5693. // Increment counter
  5694. counter[blockSize - 1] = (counter[blockSize - 1] + 1) | 0
  5695. // Encrypt
  5696. for (var i = 0; i < blockSize; i++) {
  5697. words[offset + i] ^= keystream[i];
  5698. }
  5699. }
  5700. });
  5701. CTR.Decryptor = Encryptor;
  5702. return CTR;
  5703. }());
  5704. define("crypto.mode-ctr", ["crypto.cipher-core"], function(){});
  5705. ;(function (root, factory) {
  5706. if (typeof define === "function" && define.amd) {
  5707. define('crypto',[
  5708. "crypto.core",
  5709. "crypto.enc-base64",
  5710. "crypto.md5",
  5711. "crypto.evpkdf",
  5712. "crypto.cipher-core",
  5713. "crypto.aes",
  5714. "crypto.sha1",
  5715. "crypto.sha256",
  5716. "crypto.hmac",
  5717. "crypto.pad-nopadding",
  5718. "crypto.mode-ctr"
  5719. ], function() {
  5720. return CryptoJS;
  5721. }
  5722. );
  5723. } else {
  5724. root.CryptoJS = factory();
  5725. }
  5726. }(this));
  5727. ;(function (root, factory) {
  5728. if (typeof define === 'function' && define.amd) {
  5729. define('bigint',[], factory.bind(root, root.crypto || root.msCrypto))
  5730. } else if (typeof module !== 'undefined' && module.exports) {
  5731. module.exports = factory(require('crypto'))
  5732. } else {
  5733. root.BigInt = factory(root.crypto || root.msCrypto)
  5734. }
  5735. }(this, function (crypto) {
  5736. ////////////////////////////////////////////////////////////////////////////////////////
  5737. // Big Integer Library v. 5.5
  5738. // Created 2000, last modified 2013
  5739. // Leemon Baird
  5740. // www.leemon.com
  5741. //
  5742. // Version history:
  5743. // v 5.5 17 Mar 2013
  5744. // - two lines of a form like "if (x<0) x+=n" had the "if" changed to "while" to
  5745. // handle the case when x<-n. (Thanks to James Ansell for finding that bug)
  5746. // v 5.4 3 Oct 2009
  5747. // - added "var i" to greaterShift() so i is not global. (Thanks to Péter Szabó for finding that bug)
  5748. //
  5749. // v 5.3 21 Sep 2009
  5750. // - added randProbPrime(k) for probable primes
  5751. // - unrolled loop in mont_ (slightly faster)
  5752. // - millerRabin now takes a bigInt parameter rather than an int
  5753. //
  5754. // v 5.2 15 Sep 2009
  5755. // - fixed capitalization in call to int2bigInt in randBigInt
  5756. // (thanks to Emili Evripidou, Reinhold Behringer, and Samuel Macaleese for finding that bug)
  5757. //
  5758. // v 5.1 8 Oct 2007
  5759. // - renamed inverseModInt_ to inverseModInt since it doesn't change its parameters
  5760. // - added functions GCD and randBigInt, which call GCD_ and randBigInt_
  5761. // - fixed a bug found by Rob Visser (see comment with his name below)
  5762. // - improved comments
  5763. //
  5764. // This file is public domain. You can use it for any purpose without restriction.
  5765. // I do not guarantee that it is correct, so use it at your own risk. If you use
  5766. // it for something interesting, I'd appreciate hearing about it. If you find
  5767. // any bugs or make any improvements, I'd appreciate hearing about those too.
  5768. // It would also be nice if my name and URL were left in the comments. But none
  5769. // of that is required.
  5770. //
  5771. // This code defines a bigInt library for arbitrary-precision integers.
  5772. // A bigInt is an array of integers storing the value in chunks of bpe bits,
  5773. // little endian (buff[0] is the least significant word).
  5774. // Negative bigInts are stored two's complement. Almost all the functions treat
  5775. // bigInts as nonnegative. The few that view them as two's complement say so
  5776. // in their comments. Some functions assume their parameters have at least one
  5777. // leading zero element. Functions with an underscore at the end of the name put
  5778. // their answer into one of the arrays passed in, and have unpredictable behavior
  5779. // in case of overflow, so the caller must make sure the arrays are big enough to
  5780. // hold the answer. But the average user should never have to call any of the
  5781. // underscored functions. Each important underscored function has a wrapper function
  5782. // of the same name without the underscore that takes care of the details for you.
  5783. // For each underscored function where a parameter is modified, that same variable
  5784. // must not be used as another argument too. So, you cannot square x by doing
  5785. // multMod_(x,x,n). You must use squareMod_(x,n) instead, or do y=dup(x); multMod_(x,y,n).
  5786. // Or simply use the multMod(x,x,n) function without the underscore, where
  5787. // such issues never arise, because non-underscored functions never change
  5788. // their parameters; they always allocate new memory for the answer that is returned.
  5789. //
  5790. // These functions are designed to avoid frequent dynamic memory allocation in the inner loop.
  5791. // For most functions, if it needs a BigInt as a local variable it will actually use
  5792. // a global, and will only allocate to it only when it's not the right size. This ensures
  5793. // that when a function is called repeatedly with same-sized parameters, it only allocates
  5794. // memory on the first call.
  5795. //
  5796. // Note that for cryptographic purposes, the calls to Math.random() must
  5797. // be replaced with calls to a better pseudorandom number generator.
  5798. //
  5799. // In the following, "bigInt" means a bigInt with at least one leading zero element,
  5800. // and "integer" means a nonnegative integer less than radix. In some cases, integer
  5801. // can be negative. Negative bigInts are 2s complement.
  5802. //
  5803. // The following functions do not modify their inputs.
  5804. // Those returning a bigInt, string, or Array will dynamically allocate memory for that value.
  5805. // Those returning a boolean will return the integer 0 (false) or 1 (true).
  5806. // Those returning boolean or int will not allocate memory except possibly on the first
  5807. // time they're called with a given parameter size.
  5808. //
  5809. // bigInt add(x,y) //return (x+y) for bigInts x and y.
  5810. // bigInt addInt(x,n) //return (x+n) where x is a bigInt and n is an integer.
  5811. // string bigInt2str(x,base) //return a string form of bigInt x in a given base, with 2 <= base <= 95
  5812. // int bitSize(x) //return how many bits long the bigInt x is, not counting leading zeros
  5813. // bigInt dup(x) //return a copy of bigInt x
  5814. // boolean equals(x,y) //is the bigInt x equal to the bigint y?
  5815. // boolean equalsInt(x,y) //is bigint x equal to integer y?
  5816. // bigInt expand(x,n) //return a copy of x with at least n elements, adding leading zeros if needed
  5817. // Array findPrimes(n) //return array of all primes less than integer n
  5818. // bigInt GCD(x,y) //return greatest common divisor of bigInts x and y (each with same number of elements).
  5819. // boolean greater(x,y) //is x>y? (x and y are nonnegative bigInts)
  5820. // boolean greaterShift(x,y,shift)//is (x <<(shift*bpe)) > y?
  5821. // bigInt int2bigInt(t,n,m) //return a bigInt equal to integer t, with at least n bits and m array elements
  5822. // bigInt inverseMod(x,n) //return (x**(-1) mod n) for bigInts x and n. If no inverse exists, it returns null
  5823. // int inverseModInt(x,n) //return x**(-1) mod n, for integers x and n. Return 0 if there is no inverse
  5824. // boolean isZero(x) //is the bigInt x equal to zero?
  5825. // boolean millerRabin(x,b) //does one round of Miller-Rabin base integer b say that bigInt x is possibly prime? (b is bigInt, 1<b<x)
  5826. // boolean millerRabinInt(x,b) //does one round of Miller-Rabin base integer b say that bigInt x is possibly prime? (b is int, 1<b<x)
  5827. // bigInt mod(x,n) //return a new bigInt equal to (x mod n) for bigInts x and n.
  5828. // int modInt(x,n) //return x mod n for bigInt x and integer n.
  5829. // bigInt mult(x,y) //return x*y for bigInts x and y. This is faster when y<x.
  5830. // bigInt multMod(x,y,n) //return (x*y mod n) for bigInts x,y,n. For greater speed, let y<x.
  5831. // boolean negative(x) //is bigInt x negative?
  5832. // bigInt powMod(x,y,n) //return (x**y mod n) where x,y,n are bigInts and ** is exponentiation. 0**0=1. Faster for odd n.
  5833. // bigInt randBigInt(n,s) //return an n-bit random BigInt (n>=1). If s=1, then the most significant of those n bits is set to 1.
  5834. // bigInt randTruePrime(k) //return a new, random, k-bit, true prime bigInt using Maurer's algorithm.
  5835. // bigInt randProbPrime(k) //return a new, random, k-bit, probable prime bigInt (probability it's composite less than 2^-80).
  5836. // bigInt str2bigInt(s,b,n,m) //return a bigInt for number represented in string s in base b with at least n bits and m array elements
  5837. // bigInt sub(x,y) //return (x-y) for bigInts x and y. Negative answers will be 2s complement
  5838. // bigInt trim(x,k) //return a copy of x with exactly k leading zero elements
  5839. //
  5840. //
  5841. // The following functions each have a non-underscored version, which most users should call instead.
  5842. // These functions each write to a single parameter, and the caller is responsible for ensuring the array
  5843. // passed in is large enough to hold the result.
  5844. //
  5845. // void addInt_(x,n) //do x=x+n where x is a bigInt and n is an integer
  5846. // void add_(x,y) //do x=x+y for bigInts x and y
  5847. // void copy_(x,y) //do x=y on bigInts x and y
  5848. // void copyInt_(x,n) //do x=n on bigInt x and integer n
  5849. // void GCD_(x,y) //set x to the greatest common divisor of bigInts x and y, (y is destroyed). (This never overflows its array).
  5850. // boolean inverseMod_(x,n) //do x=x**(-1) mod n, for bigInts x and n. Returns 1 (0) if inverse does (doesn't) exist
  5851. // void mod_(x,n) //do x=x mod n for bigInts x and n. (This never overflows its array).
  5852. // void mult_(x,y) //do x=x*y for bigInts x and y.
  5853. // void multMod_(x,y,n) //do x=x*y mod n for bigInts x,y,n.
  5854. // void powMod_(x,y,n) //do x=x**y mod n, where x,y,n are bigInts (n is odd) and ** is exponentiation. 0**0=1.
  5855. // void randBigInt_(b,n,s) //do b = an n-bit random BigInt. if s=1, then nth bit (most significant bit) is set to 1. n>=1.
  5856. // void randTruePrime_(ans,k) //do ans = a random k-bit true random prime (not just probable prime) with 1 in the msb.
  5857. // void sub_(x,y) //do x=x-y for bigInts x and y. Negative answers will be 2s complement.
  5858. //
  5859. // The following functions do NOT have a non-underscored version.
  5860. // They each write a bigInt result to one or more parameters. The caller is responsible for
  5861. // ensuring the arrays passed in are large enough to hold the results.
  5862. //
  5863. // void addShift_(x,y,ys) //do x=x+(y<<(ys*bpe))
  5864. // void carry_(x) //do carries and borrows so each element of the bigInt x fits in bpe bits.
  5865. // void divide_(x,y,q,r) //divide x by y giving quotient q and remainder r
  5866. // int divInt_(x,n) //do x=floor(x/n) for bigInt x and integer n, and return the remainder. (This never overflows its array).
  5867. // int eGCD_(x,y,d,a,b) //sets a,b,d to positive bigInts such that d = GCD_(x,y) = a*x-b*y
  5868. // void halve_(x) //do x=floor(|x|/2)*sgn(x) for bigInt x in 2's complement. (This never overflows its array).
  5869. // void leftShift_(x,n) //left shift bigInt x by n bits. n<bpe.
  5870. // void linComb_(x,y,a,b) //do x=a*x+b*y for bigInts x and y and integers a and b
  5871. // void linCombShift_(x,y,b,ys) //do x=x+b*(y<<(ys*bpe)) for bigInts x and y, and integers b and ys
  5872. // void mont_(x,y,n,np) //Montgomery multiplication (see comments where the function is defined)
  5873. // void multInt_(x,n) //do x=x*n where x is a bigInt and n is an integer.
  5874. // void rightShift_(x,n) //right shift bigInt x by n bits. (This never overflows its array).
  5875. // void squareMod_(x,n) //do x=x*x mod n for bigInts x,n
  5876. // void subShift_(x,y,ys) //do x=x-(y<<(ys*bpe)). Negative answers will be 2s complement.
  5877. //
  5878. // The following functions are based on algorithms from the _Handbook of Applied Cryptography_
  5879. // powMod_() = algorithm 14.94, Montgomery exponentiation
  5880. // eGCD_,inverseMod_() = algorithm 14.61, Binary extended GCD_
  5881. // GCD_() = algorothm 14.57, Lehmer's algorithm
  5882. // mont_() = algorithm 14.36, Montgomery multiplication
  5883. // divide_() = algorithm 14.20 Multiple-precision division
  5884. // squareMod_() = algorithm 14.16 Multiple-precision squaring
  5885. // randTruePrime_() = algorithm 4.62, Maurer's algorithm
  5886. // millerRabin() = algorithm 4.24, Miller-Rabin algorithm
  5887. //
  5888. // Profiling shows:
  5889. // randTruePrime_() spends:
  5890. // 10% of its time in calls to powMod_()
  5891. // 85% of its time in calls to millerRabin()
  5892. // millerRabin() spends:
  5893. // 99% of its time in calls to powMod_() (always with a base of 2)
  5894. // powMod_() spends:
  5895. // 94% of its time in calls to mont_() (almost always with x==y)
  5896. //
  5897. // This suggests there are several ways to speed up this library slightly:
  5898. // - convert powMod_ to use a Montgomery form of k-ary window (or maybe a Montgomery form of sliding window)
  5899. // -- this should especially focus on being fast when raising 2 to a power mod n
  5900. // - convert randTruePrime_() to use a minimum r of 1/3 instead of 1/2 with the appropriate change to the test
  5901. // - tune the parameters in randTruePrime_(), including c, m, and recLimit
  5902. // - speed up the single loop in mont_() that takes 95% of the runtime, perhaps by reducing checking
  5903. // within the loop when all the parameters are the same length.
  5904. //
  5905. // There are several ideas that look like they wouldn't help much at all:
  5906. // - replacing trial division in randTruePrime_() with a sieve (that speeds up something taking almost no time anyway)
  5907. // - increase bpe from 15 to 30 (that would help if we had a 32*32->64 multiplier, but not with JavaScript's 32*32->32)
  5908. // - speeding up mont_(x,y,n,np) when x==y by doing a non-modular, non-Montgomery square
  5909. // followed by a Montgomery reduction. The intermediate answer will be twice as long as x, so that
  5910. // method would be slower. This is unfortunate because the code currently spends almost all of its time
  5911. // doing mont_(x,x,...), both for randTruePrime_() and powMod_(). A faster method for Montgomery squaring
  5912. // would have a large impact on the speed of randTruePrime_() and powMod_(). HAC has a couple of poorly-worded
  5913. // sentences that seem to imply it's faster to do a non-modular square followed by a single
  5914. // Montgomery reduction, but that's obviously wrong.
  5915. ////////////////////////////////////////////////////////////////////////////////////////
  5916. //globals
  5917. // The number of significant bits in the fraction of a JavaScript
  5918. // floating-point number is 52, independent of platform.
  5919. // See: https://github.com/arlolra/otr/issues/41
  5920. var bpe = 26; // bits stored per array element
  5921. var radix = 1 << bpe; // equals 2^bpe
  5922. var mask = radix - 1; // AND this with an array element to chop it down to bpe bits
  5923. //the digits for converting to different bases
  5924. var digitsStr='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_=!@#$%^&*()[]{}|;:,.<>/?`~ \\\'\"+-';
  5925. var one=int2bigInt(1,1,1); //constant used in powMod_()
  5926. //the following global variables are scratchpad memory to
  5927. //reduce dynamic memory allocation in the inner loop
  5928. var t=new Array(0);
  5929. var ss=t; //used in mult_()
  5930. var s0=t; //used in multMod_(), squareMod_()
  5931. var s1=t; //used in powMod_(), multMod_(), squareMod_()
  5932. var s2=t; //used in powMod_(), multMod_()
  5933. var s3=t; //used in powMod_()
  5934. var s4=t, s5=t; //used in mod_()
  5935. var s6=t; //used in bigInt2str()
  5936. var s7=t; //used in powMod_()
  5937. var T=t; //used in GCD_()
  5938. var sa=t; //used in mont_()
  5939. var mr_x1=t, mr_r=t, mr_a=t; //used in millerRabin()
  5940. var eg_v=t, eg_u=t, eg_A=t, eg_B=t, eg_C=t, eg_D=t; //used in eGCD_(), inverseMod_()
  5941. var md_q1=t, md_q2=t, md_q3=t, md_r=t, md_r1=t, md_r2=t, md_tt=t; //used in mod_()
  5942. var primes=t, pows=t, s_i=t, s_i2=t, s_R=t, s_rm=t, s_q=t, s_n1=t;
  5943. var s_a=t, s_r2=t, s_n=t, s_b=t, s_d=t, s_x1=t, s_x2=t, s_aa=t; //used in randTruePrime_()
  5944. var rpprb=t; //used in randProbPrimeRounds() (which also uses "primes")
  5945. ////////////////////////////////////////////////////////////////////////////////////////
  5946. //return array of all primes less than integer n
  5947. function findPrimes(n) {
  5948. var i,s,p,ans;
  5949. s=new Array(n);
  5950. for (i=0;i<n;i++)
  5951. s[i]=0;
  5952. s[0]=2;
  5953. p=0; //first p elements of s are primes, the rest are a sieve
  5954. for(;s[p]<n;) { //s[p] is the pth prime
  5955. for(i=s[p]*s[p]; i<n; i+=s[p]) //mark multiples of s[p]
  5956. s[i]=1;
  5957. p++;
  5958. s[p]=s[p-1]+1;
  5959. for(; s[p]<n && s[s[p]]; s[p]++); //find next prime (where s[p]==0)
  5960. }
  5961. ans=new Array(p);
  5962. for(i=0;i<p;i++)
  5963. ans[i]=s[i];
  5964. return ans;
  5965. }
  5966. //does a single round of Miller-Rabin base b consider x to be a possible prime?
  5967. //x is a bigInt, and b is an integer, with b<x
  5968. function millerRabinInt(x,b) {
  5969. if (mr_x1.length!=x.length) {
  5970. mr_x1=dup(x);
  5971. mr_r=dup(x);
  5972. mr_a=dup(x);
  5973. }
  5974. copyInt_(mr_a,b);
  5975. return millerRabin(x,mr_a);
  5976. }
  5977. //does a single round of Miller-Rabin base b consider x to be a possible prime?
  5978. //x and b are bigInts with b<x
  5979. function millerRabin(x,b) {
  5980. var i,j,k,s;
  5981. if (mr_x1.length!=x.length) {
  5982. mr_x1=dup(x);
  5983. mr_r=dup(x);
  5984. mr_a=dup(x);
  5985. }
  5986. copy_(mr_a,b);
  5987. copy_(mr_r,x);
  5988. copy_(mr_x1,x);
  5989. addInt_(mr_r,-1);
  5990. addInt_(mr_x1,-1);
  5991. //s=the highest power of two that divides mr_r
  5992. /*
  5993. k=0;
  5994. for (i=0;i<mr_r.length;i++)
  5995. for (j=1;j<mask;j<<=1)
  5996. if (x[i] & j) {
  5997. s=(k<mr_r.length+bpe ? k : 0);
  5998. i=mr_r.length;
  5999. j=mask;
  6000. } else
  6001. k++;
  6002. */
  6003. /* http://www.javascripter.net/math/primes/millerrabinbug-bigint54.htm */
  6004. if (isZero(mr_r)) return 0;
  6005. for (k=0; mr_r[k]==0; k++);
  6006. for (i=1,j=2; mr_r[k]%j==0; j*=2,i++ );
  6007. s = k*bpe + i - 1;
  6008. /* end */
  6009. if (s)
  6010. rightShift_(mr_r,s);
  6011. powMod_(mr_a,mr_r,x);
  6012. if (!equalsInt(mr_a,1) && !equals(mr_a,mr_x1)) {
  6013. j=1;
  6014. while (j<=s-1 && !equals(mr_a,mr_x1)) {
  6015. squareMod_(mr_a,x);
  6016. if (equalsInt(mr_a,1)) {
  6017. return 0;
  6018. }
  6019. j++;
  6020. }
  6021. if (!equals(mr_a,mr_x1)) {
  6022. return 0;
  6023. }
  6024. }
  6025. return 1;
  6026. }
  6027. //returns how many bits long the bigInt is, not counting leading zeros.
  6028. function bitSize(x) {
  6029. var j,z,w;
  6030. for (j=x.length-1; (x[j]==0) && (j>0); j--);
  6031. for (z=0,w=x[j]; w; (w>>=1),z++);
  6032. z+=bpe*j;
  6033. return z;
  6034. }
  6035. //return a copy of x with at least n elements, adding leading zeros if needed
  6036. function expand(x,n) {
  6037. var ans=int2bigInt(0,(x.length>n ? x.length : n)*bpe,0);
  6038. copy_(ans,x);
  6039. return ans;
  6040. }
  6041. //return a k-bit true random prime using Maurer's algorithm.
  6042. function randTruePrime(k) {
  6043. var ans=int2bigInt(0,k,0);
  6044. randTruePrime_(ans,k);
  6045. return trim(ans,1);
  6046. }
  6047. //return a k-bit random probable prime with probability of error < 2^-80
  6048. function randProbPrime(k) {
  6049. if (k>=600) return randProbPrimeRounds(k,2); //numbers from HAC table 4.3
  6050. if (k>=550) return randProbPrimeRounds(k,4);
  6051. if (k>=500) return randProbPrimeRounds(k,5);
  6052. if (k>=400) return randProbPrimeRounds(k,6);
  6053. if (k>=350) return randProbPrimeRounds(k,7);
  6054. if (k>=300) return randProbPrimeRounds(k,9);
  6055. if (k>=250) return randProbPrimeRounds(k,12); //numbers from HAC table 4.4
  6056. if (k>=200) return randProbPrimeRounds(k,15);
  6057. if (k>=150) return randProbPrimeRounds(k,18);
  6058. if (k>=100) return randProbPrimeRounds(k,27);
  6059. return randProbPrimeRounds(k,40); //number from HAC remark 4.26 (only an estimate)
  6060. }
  6061. //return a k-bit probable random prime using n rounds of Miller Rabin (after trial division with small primes)
  6062. function randProbPrimeRounds(k,n) {
  6063. var ans, i, divisible, B;
  6064. B=30000; //B is largest prime to use in trial division
  6065. ans=int2bigInt(0,k,0);
  6066. //optimization: try larger and smaller B to find the best limit.
  6067. if (primes.length==0)
  6068. primes=findPrimes(30000); //check for divisibility by primes <=30000
  6069. if (rpprb.length!=ans.length)
  6070. rpprb=dup(ans);
  6071. for (;;) { //keep trying random values for ans until one appears to be prime
  6072. //optimization: pick a random number times L=2*3*5*...*p, plus a
  6073. // random element of the list of all numbers in [0,L) not divisible by any prime up to p.
  6074. // This can reduce the amount of random number generation.
  6075. randBigInt_(ans,k,0); //ans = a random odd number to check
  6076. ans[0] |= 1;
  6077. divisible=0;
  6078. //check ans for divisibility by small primes up to B
  6079. for (i=0; (i<primes.length) && (primes[i]<=B); i++)
  6080. if (modInt(ans,primes[i])==0 && !equalsInt(ans,primes[i])) {
  6081. divisible=1;
  6082. break;
  6083. }
  6084. //optimization: change millerRabin so the base can be bigger than the number being checked, then eliminate the while here.
  6085. //do n rounds of Miller Rabin, with random bases less than ans
  6086. for (i=0; i<n && !divisible; i++) {
  6087. randBigInt_(rpprb,k,0);
  6088. while(!greater(ans,rpprb)) //pick a random rpprb that's < ans
  6089. randBigInt_(rpprb,k,0);
  6090. if (!millerRabin(ans,rpprb))
  6091. divisible=1;
  6092. }
  6093. if(!divisible)
  6094. return ans;
  6095. }
  6096. }
  6097. //return a new bigInt equal to (x mod n) for bigInts x and n.
  6098. function mod(x,n) {
  6099. var ans=dup(x);
  6100. mod_(ans,n);
  6101. return trim(ans,1);
  6102. }
  6103. //return (x+n) where x is a bigInt and n is an integer.
  6104. function addInt(x,n) {
  6105. var ans=expand(x,x.length+1);
  6106. addInt_(ans,n);
  6107. return trim(ans,1);
  6108. }
  6109. //return x*y for bigInts x and y. This is faster when y<x.
  6110. function mult(x,y) {
  6111. var ans=expand(x,x.length+y.length);
  6112. mult_(ans,y);
  6113. return trim(ans,1);
  6114. }
  6115. //return (x**y mod n) where x,y,n are bigInts and ** is exponentiation. 0**0=1. Faster for odd n.
  6116. function powMod(x,y,n) {
  6117. var ans=expand(x,n.length);
  6118. powMod_(ans,trim(y,2),trim(n,2),0); //this should work without the trim, but doesn't
  6119. return trim(ans,1);
  6120. }
  6121. //return (x-y) for bigInts x and y. Negative answers will be 2s complement
  6122. function sub(x,y) {
  6123. var ans=expand(x,(x.length>y.length ? x.length+1 : y.length+1));
  6124. sub_(ans,y);
  6125. return trim(ans,1);
  6126. }
  6127. //return (x+y) for bigInts x and y.
  6128. function add(x,y) {
  6129. var ans=expand(x,(x.length>y.length ? x.length+1 : y.length+1));
  6130. add_(ans,y);
  6131. return trim(ans,1);
  6132. }
  6133. //return (x**(-1) mod n) for bigInts x and n. If no inverse exists, it returns null
  6134. function inverseMod(x,n) {
  6135. var ans=expand(x,n.length);
  6136. var s;
  6137. s=inverseMod_(ans,n);
  6138. return s ? trim(ans,1) : null;
  6139. }
  6140. //return (x*y mod n) for bigInts x,y,n. For greater speed, let y<x.
  6141. function multMod(x,y,n) {
  6142. var ans=expand(x,n.length);
  6143. multMod_(ans,y,n);
  6144. return trim(ans,1);
  6145. }
  6146. //generate a k-bit true random prime using Maurer's algorithm,
  6147. //and put it into ans. The bigInt ans must be large enough to hold it.
  6148. function randTruePrime_(ans,k) {
  6149. var c,w,m,pm,dd,j,r,B,divisible,z,zz,recSize,recLimit;
  6150. if (primes.length==0)
  6151. primes=findPrimes(30000); //check for divisibility by primes <=30000
  6152. if (pows.length==0) {
  6153. pows=new Array(512);
  6154. for (j=0;j<512;j++) {
  6155. pows[j]=Math.pow(2,j/511.0-1.0);
  6156. }
  6157. }
  6158. //c and m should be tuned for a particular machine and value of k, to maximize speed
  6159. c=0.1; //c=0.1 in HAC
  6160. m=20; //generate this k-bit number by first recursively generating a number that has between k/2 and k-m bits
  6161. recLimit=20; //stop recursion when k <=recLimit. Must have recLimit >= 2
  6162. if (s_i2.length!=ans.length) {
  6163. s_i2=dup(ans);
  6164. s_R =dup(ans);
  6165. s_n1=dup(ans);
  6166. s_r2=dup(ans);
  6167. s_d =dup(ans);
  6168. s_x1=dup(ans);
  6169. s_x2=dup(ans);
  6170. s_b =dup(ans);
  6171. s_n =dup(ans);
  6172. s_i =dup(ans);
  6173. s_rm=dup(ans);
  6174. s_q =dup(ans);
  6175. s_a =dup(ans);
  6176. s_aa=dup(ans);
  6177. }
  6178. if (k <= recLimit) { //generate small random primes by trial division up to its square root
  6179. pm=(1<<((k+2)>>1))-1; //pm is binary number with all ones, just over sqrt(2^k)
  6180. copyInt_(ans,0);
  6181. for (dd=1;dd;) {
  6182. dd=0;
  6183. ans[0]= 1 | (1<<(k-1)) | randomBitInt(k); //random, k-bit, odd integer, with msb 1
  6184. for (j=1;(j<primes.length) && ((primes[j]&pm)==primes[j]);j++) { //trial division by all primes 3...sqrt(2^k)
  6185. if (0==(ans[0]%primes[j])) {
  6186. dd=1;
  6187. break;
  6188. }
  6189. }
  6190. }
  6191. carry_(ans);
  6192. return;
  6193. }
  6194. B=c*k*k; //try small primes up to B (or all the primes[] array if the largest is less than B).
  6195. if (k>2*m) //generate this k-bit number by first recursively generating a number that has between k/2 and k-m bits
  6196. for (r=1; k-k*r<=m; )
  6197. r=pows[randomBitInt(9)]; //r=Math.pow(2,Math.random()-1);
  6198. else
  6199. r=0.5;
  6200. //simulation suggests the more complex algorithm using r=.333 is only slightly faster.
  6201. recSize=Math.floor(r*k)+1;
  6202. randTruePrime_(s_q,recSize);
  6203. copyInt_(s_i2,0);
  6204. s_i2[Math.floor((k-2)/bpe)] |= (1<<((k-2)%bpe)); //s_i2=2^(k-2)
  6205. divide_(s_i2,s_q,s_i,s_rm); //s_i=floor((2^(k-1))/(2q))
  6206. z=bitSize(s_i);
  6207. for (;;) {
  6208. for (;;) { //generate z-bit numbers until one falls in the range [0,s_i-1]
  6209. randBigInt_(s_R,z,0);
  6210. if (greater(s_i,s_R))
  6211. break;
  6212. } //now s_R is in the range [0,s_i-1]
  6213. addInt_(s_R,1); //now s_R is in the range [1,s_i]
  6214. add_(s_R,s_i); //now s_R is in the range [s_i+1,2*s_i]
  6215. copy_(s_n,s_q);
  6216. mult_(s_n,s_R);
  6217. multInt_(s_n,2);
  6218. addInt_(s_n,1); //s_n=2*s_R*s_q+1
  6219. copy_(s_r2,s_R);
  6220. multInt_(s_r2,2); //s_r2=2*s_R
  6221. //check s_n for divisibility by small primes up to B
  6222. for (divisible=0,j=0; (j<primes.length) && (primes[j]<B); j++)
  6223. if (modInt(s_n,primes[j])==0 && !equalsInt(s_n,primes[j])) {
  6224. divisible=1;
  6225. break;
  6226. }
  6227. if (!divisible) //if it passes small primes check, then try a single Miller-Rabin base 2
  6228. if (!millerRabinInt(s_n,2)) //this line represents 75% of the total runtime for randTruePrime_
  6229. divisible=1;
  6230. if (!divisible) { //if it passes that test, continue checking s_n
  6231. addInt_(s_n,-3);
  6232. for (j=s_n.length-1;(s_n[j]==0) && (j>0); j--); //strip leading zeros
  6233. for (zz=0,w=s_n[j]; w; (w>>=1),zz++);
  6234. zz+=bpe*j; //zz=number of bits in s_n, ignoring leading zeros
  6235. for (;;) { //generate z-bit numbers until one falls in the range [0,s_n-1]
  6236. randBigInt_(s_a,zz,0);
  6237. if (greater(s_n,s_a))
  6238. break;
  6239. } //now s_a is in the range [0,s_n-1]
  6240. addInt_(s_n,3); //now s_a is in the range [0,s_n-4]
  6241. addInt_(s_a,2); //now s_a is in the range [2,s_n-2]
  6242. copy_(s_b,s_a);
  6243. copy_(s_n1,s_n);
  6244. addInt_(s_n1,-1);
  6245. powMod_(s_b,s_n1,s_n); //s_b=s_a^(s_n-1) modulo s_n
  6246. addInt_(s_b,-1);
  6247. if (isZero(s_b)) {
  6248. copy_(s_b,s_a);
  6249. powMod_(s_b,s_r2,s_n);
  6250. addInt_(s_b,-1);
  6251. copy_(s_aa,s_n);
  6252. copy_(s_d,s_b);
  6253. GCD_(s_d,s_n); //if s_b and s_n are relatively prime, then s_n is a prime
  6254. if (equalsInt(s_d,1)) {
  6255. copy_(ans,s_aa);
  6256. return; //if we've made it this far, then s_n is absolutely guaranteed to be prime
  6257. }
  6258. }
  6259. }
  6260. }
  6261. }
  6262. //Return an n-bit random BigInt (n>=1). If s=1, then the most significant of those n bits is set to 1.
  6263. function randBigInt(n,s) {
  6264. var a,b;
  6265. a=Math.floor((n-1)/bpe)+2; //# array elements to hold the BigInt with a leading 0 element
  6266. b=int2bigInt(0,0,a);
  6267. randBigInt_(b,n,s);
  6268. return b;
  6269. }
  6270. //Set b to an n-bit random BigInt. If s=1, then the most significant of those n bits is set to 1.
  6271. //Array b must be big enough to hold the result. Must have n>=1
  6272. function randBigInt_(b,n,s) {
  6273. var i,a;
  6274. for (i=0;i<b.length;i++)
  6275. b[i]=0;
  6276. a=Math.floor((n-1)/bpe)+1; //# array elements to hold the BigInt
  6277. for (i=0;i<a;i++) {
  6278. b[i]=randomBitInt(bpe);
  6279. }
  6280. b[a-1] &= (2<<((n-1)%bpe))-1;
  6281. if (s==1)
  6282. b[a-1] |= (1<<((n-1)%bpe));
  6283. }
  6284. //Return the greatest common divisor of bigInts x and y (each with same number of elements).
  6285. function GCD(x,y) {
  6286. var xc,yc;
  6287. xc=dup(x);
  6288. yc=dup(y);
  6289. GCD_(xc,yc);
  6290. return xc;
  6291. }
  6292. //set x to the greatest common divisor of bigInts x and y (each with same number of elements).
  6293. //y is destroyed.
  6294. function GCD_(x,y) {
  6295. var i,xp,yp,A,B,C,D,q,sing,qp;
  6296. if (T.length!=x.length)
  6297. T=dup(x);
  6298. sing=1;
  6299. while (sing) { //while y has nonzero elements other than y[0]
  6300. sing=0;
  6301. for (i=1;i<y.length;i++) //check if y has nonzero elements other than 0
  6302. if (y[i]) {
  6303. sing=1;
  6304. break;
  6305. }
  6306. if (!sing) break; //quit when y all zero elements except possibly y[0]
  6307. for (i=x.length;!x[i] && i>=0;i--); //find most significant element of x
  6308. xp=x[i];
  6309. yp=y[i];
  6310. A=1; B=0; C=0; D=1;
  6311. while ((yp+C) && (yp+D)) {
  6312. q =Math.floor((xp+A)/(yp+C));
  6313. qp=Math.floor((xp+B)/(yp+D));
  6314. if (q!=qp)
  6315. break;
  6316. t= A-q*C; A=C; C=t; // do (A,B,xp, C,D,yp) = (C,D,yp, A,B,xp) - q*(0,0,0, C,D,yp)
  6317. t= B-q*D; B=D; D=t;
  6318. t=xp-q*yp; xp=yp; yp=t;
  6319. }
  6320. if (B) {
  6321. copy_(T,x);
  6322. linComb_(x,y,A,B); //x=A*x+B*y
  6323. linComb_(y,T,D,C); //y=D*y+C*T
  6324. } else {
  6325. mod_(x,y);
  6326. copy_(T,x);
  6327. copy_(x,y);
  6328. copy_(y,T);
  6329. }
  6330. }
  6331. if (y[0]==0)
  6332. return;
  6333. t=modInt(x,y[0]);
  6334. copyInt_(x,y[0]);
  6335. y[0]=t;
  6336. while (y[0]) {
  6337. x[0]%=y[0];
  6338. t=x[0]; x[0]=y[0]; y[0]=t;
  6339. }
  6340. }
  6341. //do x=x**(-1) mod n, for bigInts x and n.
  6342. //If no inverse exists, it sets x to zero and returns 0, else it returns 1.
  6343. //The x array must be at least as large as the n array.
  6344. function inverseMod_(x,n) {
  6345. var k=1+2*Math.max(x.length,n.length);
  6346. if(!(x[0]&1) && !(n[0]&1)) { //if both inputs are even, then inverse doesn't exist
  6347. copyInt_(x,0);
  6348. return 0;
  6349. }
  6350. if (eg_u.length!=k) {
  6351. eg_u=new Array(k);
  6352. eg_v=new Array(k);
  6353. eg_A=new Array(k);
  6354. eg_B=new Array(k);
  6355. eg_C=new Array(k);
  6356. eg_D=new Array(k);
  6357. }
  6358. copy_(eg_u,x);
  6359. copy_(eg_v,n);
  6360. copyInt_(eg_A,1);
  6361. copyInt_(eg_B,0);
  6362. copyInt_(eg_C,0);
  6363. copyInt_(eg_D,1);
  6364. for (;;) {
  6365. while(!(eg_u[0]&1)) { //while eg_u is even
  6366. halve_(eg_u);
  6367. if (!(eg_A[0]&1) && !(eg_B[0]&1)) { //if eg_A==eg_B==0 mod 2
  6368. halve_(eg_A);
  6369. halve_(eg_B);
  6370. } else {
  6371. add_(eg_A,n); halve_(eg_A);
  6372. sub_(eg_B,x); halve_(eg_B);
  6373. }
  6374. }
  6375. while (!(eg_v[0]&1)) { //while eg_v is even
  6376. halve_(eg_v);
  6377. if (!(eg_C[0]&1) && !(eg_D[0]&1)) { //if eg_C==eg_D==0 mod 2
  6378. halve_(eg_C);
  6379. halve_(eg_D);
  6380. } else {
  6381. add_(eg_C,n); halve_(eg_C);
  6382. sub_(eg_D,x); halve_(eg_D);
  6383. }
  6384. }
  6385. if (!greater(eg_v,eg_u)) { //eg_v <= eg_u
  6386. sub_(eg_u,eg_v);
  6387. sub_(eg_A,eg_C);
  6388. sub_(eg_B,eg_D);
  6389. } else { //eg_v > eg_u
  6390. sub_(eg_v,eg_u);
  6391. sub_(eg_C,eg_A);
  6392. sub_(eg_D,eg_B);
  6393. }
  6394. if (equalsInt(eg_u,0)) {
  6395. while (negative(eg_C)) //make sure answer is nonnegative
  6396. add_(eg_C,n);
  6397. copy_(x,eg_C);
  6398. if (!equalsInt(eg_v,1)) { //if GCD_(x,n)!=1, then there is no inverse
  6399. copyInt_(x,0);
  6400. return 0;
  6401. }
  6402. return 1;
  6403. }
  6404. }
  6405. }
  6406. //return x**(-1) mod n, for integers x and n. Return 0 if there is no inverse
  6407. function inverseModInt(x,n) {
  6408. var a=1,b=0,t;
  6409. for (;;) {
  6410. if (x==1) return a;
  6411. if (x==0) return 0;
  6412. b-=a*Math.floor(n/x);
  6413. n%=x;
  6414. if (n==1) return b; //to avoid negatives, change this b to n-b, and each -= to +=
  6415. if (n==0) return 0;
  6416. a-=b*Math.floor(x/n);
  6417. x%=n;
  6418. }
  6419. }
  6420. //this deprecated function is for backward compatibility only.
  6421. function inverseModInt_(x,n) {
  6422. return inverseModInt(x,n);
  6423. }
  6424. //Given positive bigInts x and y, change the bigints v, a, and b to positive bigInts such that:
  6425. // v = GCD_(x,y) = a*x-b*y
  6426. //The bigInts v, a, b, must have exactly as many elements as the larger of x and y.
  6427. function eGCD_(x,y,v,a,b) {
  6428. var g=0;
  6429. var k=Math.max(x.length,y.length);
  6430. if (eg_u.length!=k) {
  6431. eg_u=new Array(k);
  6432. eg_A=new Array(k);
  6433. eg_B=new Array(k);
  6434. eg_C=new Array(k);
  6435. eg_D=new Array(k);
  6436. }
  6437. while(!(x[0]&1) && !(y[0]&1)) { //while x and y both even
  6438. halve_(x);
  6439. halve_(y);
  6440. g++;
  6441. }
  6442. copy_(eg_u,x);
  6443. copy_(v,y);
  6444. copyInt_(eg_A,1);
  6445. copyInt_(eg_B,0);
  6446. copyInt_(eg_C,0);
  6447. copyInt_(eg_D,1);
  6448. for (;;) {
  6449. while(!(eg_u[0]&1)) { //while u is even
  6450. halve_(eg_u);
  6451. if (!(eg_A[0]&1) && !(eg_B[0]&1)) { //if A==B==0 mod 2
  6452. halve_(eg_A);
  6453. halve_(eg_B);
  6454. } else {
  6455. add_(eg_A,y); halve_(eg_A);
  6456. sub_(eg_B,x); halve_(eg_B);
  6457. }
  6458. }
  6459. while (!(v[0]&1)) { //while v is even
  6460. halve_(v);
  6461. if (!(eg_C[0]&1) && !(eg_D[0]&1)) { //if C==D==0 mod 2
  6462. halve_(eg_C);
  6463. halve_(eg_D);
  6464. } else {
  6465. add_(eg_C,y); halve_(eg_C);
  6466. sub_(eg_D,x); halve_(eg_D);
  6467. }
  6468. }
  6469. if (!greater(v,eg_u)) { //v<=u
  6470. sub_(eg_u,v);
  6471. sub_(eg_A,eg_C);
  6472. sub_(eg_B,eg_D);
  6473. } else { //v>u
  6474. sub_(v,eg_u);
  6475. sub_(eg_C,eg_A);
  6476. sub_(eg_D,eg_B);
  6477. }
  6478. if (equalsInt(eg_u,0)) {
  6479. while (negative(eg_C)) { //make sure a (C) is nonnegative
  6480. add_(eg_C,y);
  6481. sub_(eg_D,x);
  6482. }
  6483. multInt_(eg_D,-1); ///make sure b (D) is nonnegative
  6484. copy_(a,eg_C);
  6485. copy_(b,eg_D);
  6486. leftShift_(v,g);
  6487. return;
  6488. }
  6489. }
  6490. }
  6491. //is bigInt x negative?
  6492. function negative(x) {
  6493. return ((x[x.length-1]>>(bpe-1))&1);
  6494. }
  6495. //is (x << (shift*bpe)) > y?
  6496. //x and y are nonnegative bigInts
  6497. //shift is a nonnegative integer
  6498. function greaterShift(x,y,shift) {
  6499. var i, kx=x.length, ky=y.length;
  6500. var k=((kx+shift)<ky) ? (kx+shift) : ky;
  6501. for (i=ky-1-shift; i<kx && i>=0; i++)
  6502. if (x[i]>0)
  6503. return 1; //if there are nonzeros in x to the left of the first column of y, then x is bigger
  6504. for (i=kx-1+shift; i<ky; i++)
  6505. if (y[i]>0)
  6506. return 0; //if there are nonzeros in y to the left of the first column of x, then x is not bigger
  6507. for (i=k-1; i>=shift; i--)
  6508. if (x[i-shift]>y[i]) return 1;
  6509. else if (x[i-shift]<y[i]) return 0;
  6510. return 0;
  6511. }
  6512. //is x > y? (x and y both nonnegative)
  6513. function greater(x,y) {
  6514. var i;
  6515. var k=(x.length<y.length) ? x.length : y.length;
  6516. for (i=x.length;i<y.length;i++)
  6517. if (y[i])
  6518. return 0; //y has more digits
  6519. for (i=y.length;i<x.length;i++)
  6520. if (x[i])
  6521. return 1; //x has more digits
  6522. for (i=k-1;i>=0;i--)
  6523. if (x[i]>y[i])
  6524. return 1;
  6525. else if (x[i]<y[i])
  6526. return 0;
  6527. return 0;
  6528. }
  6529. //divide x by y giving quotient q and remainder r. (q=floor(x/y), r=x mod y). All 4 are bigints.
  6530. //x must have at least one leading zero element.
  6531. //y must be nonzero.
  6532. //q and r must be arrays that are exactly the same length as x. (Or q can have more).
  6533. //Must have x.length >= y.length >= 2.
  6534. function divide_(x,y,q,r) {
  6535. var kx, ky;
  6536. var i,j,y1,y2,c,a,b;
  6537. copy_(r,x);
  6538. for (ky=y.length;y[ky-1]==0;ky--); //ky is number of elements in y, not including leading zeros
  6539. //normalize: ensure the most significant element of y has its highest bit set
  6540. b=y[ky-1];
  6541. for (a=0; b; a++)
  6542. b>>=1;
  6543. a=bpe-a; //a is how many bits to shift so that the high order bit of y is leftmost in its array element
  6544. leftShift_(y,a); //multiply both by 1<<a now, then divide both by that at the end
  6545. leftShift_(r,a);
  6546. //Rob Visser discovered a bug: the following line was originally just before the normalization.
  6547. for (kx=r.length;r[kx-1]==0 && kx>ky;kx--); //kx is number of elements in normalized x, not including leading zeros
  6548. copyInt_(q,0); // q=0
  6549. while (!greaterShift(y,r,kx-ky)) { // while (leftShift_(y,kx-ky) <= r) {
  6550. subShift_(r,y,kx-ky); // r=r-leftShift_(y,kx-ky)
  6551. q[kx-ky]++; // q[kx-ky]++;
  6552. } // }
  6553. for (i=kx-1; i>=ky; i--) {
  6554. if (r[i]==y[ky-1])
  6555. q[i-ky]=mask;
  6556. else
  6557. q[i-ky]=Math.floor((r[i]*radix+r[i-1])/y[ky-1]);
  6558. //The following for(;;) loop is equivalent to the commented while loop,
  6559. //except that the uncommented version avoids overflow.
  6560. //The commented loop comes from HAC, which assumes r[-1]==y[-1]==0
  6561. // while (q[i-ky]*(y[ky-1]*radix+y[ky-2]) > r[i]*radix*radix+r[i-1]*radix+r[i-2])
  6562. // q[i-ky]--;
  6563. for (;;) {
  6564. y2=(ky>1 ? y[ky-2] : 0)*q[i-ky];
  6565. c=y2;
  6566. y2=y2 & mask;
  6567. c = (c - y2) / radix;
  6568. y1=c+q[i-ky]*y[ky-1];
  6569. c=y1;
  6570. y1=y1 & mask;
  6571. c = (c - y1) / radix;
  6572. if (c==r[i] ? y1==r[i-1] ? y2>(i>1 ? r[i-2] : 0) : y1>r[i-1] : c>r[i])
  6573. q[i-ky]--;
  6574. else
  6575. break;
  6576. }
  6577. linCombShift_(r,y,-q[i-ky],i-ky); //r=r-q[i-ky]*leftShift_(y,i-ky)
  6578. if (negative(r)) {
  6579. addShift_(r,y,i-ky); //r=r+leftShift_(y,i-ky)
  6580. q[i-ky]--;
  6581. }
  6582. }
  6583. rightShift_(y,a); //undo the normalization step
  6584. rightShift_(r,a); //undo the normalization step
  6585. }
  6586. //do carries and borrows so each element of the bigInt x fits in bpe bits.
  6587. function carry_(x) {
  6588. var i,k,c,b;
  6589. k=x.length;
  6590. c=0;
  6591. for (i=0;i<k;i++) {
  6592. c+=x[i];
  6593. b=0;
  6594. if (c<0) {
  6595. b = c & mask;
  6596. b = -((c - b) / radix);
  6597. c+=b*radix;
  6598. }
  6599. x[i]=c & mask;
  6600. c = ((c - x[i]) / radix) - b;
  6601. }
  6602. }
  6603. //return x mod n for bigInt x and integer n.
  6604. function modInt(x,n) {
  6605. var i,c=0;
  6606. for (i=x.length-1; i>=0; i--)
  6607. c=(c*radix+x[i])%n;
  6608. return c;
  6609. }
  6610. //convert the integer t into a bigInt with at least the given number of bits.
  6611. //the returned array stores the bigInt in bpe-bit chunks, little endian (buff[0] is least significant word)
  6612. //Pad the array with leading zeros so that it has at least minSize elements.
  6613. //There will always be at least one leading 0 element.
  6614. function int2bigInt(t,bits,minSize) {
  6615. var i,k, buff;
  6616. k=Math.ceil(bits/bpe)+1;
  6617. k=minSize>k ? minSize : k;
  6618. buff=new Array(k);
  6619. copyInt_(buff,t);
  6620. return buff;
  6621. }
  6622. //return the bigInt given a string representation in a given base.
  6623. //Pad the array with leading zeros so that it has at least minSize elements.
  6624. //If base=-1, then it reads in a space-separated list of array elements in decimal.
  6625. //The array will always have at least one leading zero, unless base=-1.
  6626. function str2bigInt(s,base,minSize) {
  6627. var d, i, j, x, y, kk;
  6628. var k=s.length;
  6629. if (base==-1) { //comma-separated list of array elements in decimal
  6630. x=new Array(0);
  6631. for (;;) {
  6632. y=new Array(x.length+1);
  6633. for (i=0;i<x.length;i++)
  6634. y[i+1]=x[i];
  6635. y[0]=parseInt(s,10);
  6636. x=y;
  6637. d=s.indexOf(',',0);
  6638. if (d<1)
  6639. break;
  6640. s=s.substring(d+1);
  6641. if (s.length==0)
  6642. break;
  6643. }
  6644. if (x.length<minSize) {
  6645. y=new Array(minSize);
  6646. copy_(y,x);
  6647. return y;
  6648. }
  6649. return x;
  6650. }
  6651. // log2(base)*k
  6652. var bb = base, p = 0;
  6653. var b = base == 1 ? k : 0;
  6654. while (bb > 1) {
  6655. if (bb & 1) p = 1;
  6656. b += k;
  6657. bb >>= 1;
  6658. }
  6659. b += p*k;
  6660. x=int2bigInt(0,b,0);
  6661. for (i=0;i<k;i++) {
  6662. d=digitsStr.indexOf(s.substring(i,i+1),0);
  6663. if (base<=36 && d>=36) //convert lowercase to uppercase if base<=36
  6664. d-=26;
  6665. if (d>=base || d<0) { //stop at first illegal character
  6666. break;
  6667. }
  6668. multInt_(x,base);
  6669. addInt_(x,d);
  6670. }
  6671. for (k=x.length;k>0 && !x[k-1];k--); //strip off leading zeros
  6672. k=minSize>k+1 ? minSize : k+1;
  6673. y=new Array(k);
  6674. kk=k<x.length ? k : x.length;
  6675. for (i=0;i<kk;i++)
  6676. y[i]=x[i];
  6677. for (;i<k;i++)
  6678. y[i]=0;
  6679. return y;
  6680. }
  6681. //is bigint x equal to integer y?
  6682. //y must have less than bpe bits
  6683. function equalsInt(x,y) {
  6684. var i;
  6685. if (x[0]!=y)
  6686. return 0;
  6687. for (i=1;i<x.length;i++)
  6688. if (x[i])
  6689. return 0;
  6690. return 1;
  6691. }
  6692. //are bigints x and y equal?
  6693. //this works even if x and y are different lengths and have arbitrarily many leading zeros
  6694. function equals(x,y) {
  6695. var i;
  6696. var k=x.length<y.length ? x.length : y.length;
  6697. for (i=0;i<k;i++)
  6698. if (x[i]!=y[i])
  6699. return 0;
  6700. if (x.length>y.length) {
  6701. for (;i<x.length;i++)
  6702. if (x[i])
  6703. return 0;
  6704. } else {
  6705. for (;i<y.length;i++)
  6706. if (y[i])
  6707. return 0;
  6708. }
  6709. return 1;
  6710. }
  6711. //is the bigInt x equal to zero?
  6712. function isZero(x) {
  6713. var i;
  6714. for (i=0;i<x.length;i++)
  6715. if (x[i])
  6716. return 0;
  6717. return 1;
  6718. }
  6719. //convert a bigInt into a string in a given base, from base 2 up to base 95.
  6720. //Base -1 prints the contents of the array representing the number.
  6721. function bigInt2str(x,base) {
  6722. var i,t,s="";
  6723. if (s6.length!=x.length)
  6724. s6=dup(x);
  6725. else
  6726. copy_(s6,x);
  6727. if (base==-1) { //return the list of array contents
  6728. for (i=x.length-1;i>0;i--)
  6729. s+=x[i]+',';
  6730. s+=x[0];
  6731. }
  6732. else { //return it in the given base
  6733. while (!isZero(s6)) {
  6734. t=divInt_(s6,base); //t=s6 % base; s6=floor(s6/base);
  6735. s=digitsStr.substring(t,t+1)+s;
  6736. }
  6737. }
  6738. if (s.length==0)
  6739. s="0";
  6740. return s;
  6741. }
  6742. //returns a duplicate of bigInt x
  6743. function dup(x) {
  6744. var i, buff;
  6745. buff=new Array(x.length);
  6746. copy_(buff,x);
  6747. return buff;
  6748. }
  6749. //do x=y on bigInts x and y. x must be an array at least as big as y (not counting the leading zeros in y).
  6750. function copy_(x,y) {
  6751. var i;
  6752. var k=x.length<y.length ? x.length : y.length;
  6753. for (i=0;i<k;i++)
  6754. x[i]=y[i];
  6755. for (i=k;i<x.length;i++)
  6756. x[i]=0;
  6757. }
  6758. //do x=y on bigInt x and integer y.
  6759. function copyInt_(x,n) {
  6760. var i,c;
  6761. for (c=n,i=0;i<x.length;i++) {
  6762. x[i]=c & mask;
  6763. c>>=bpe;
  6764. }
  6765. }
  6766. //do x=x+n where x is a bigInt and n is an integer.
  6767. //x must be large enough to hold the result.
  6768. function addInt_(x,n) {
  6769. var i,k,c,b;
  6770. x[0]+=n;
  6771. k=x.length;
  6772. c=0;
  6773. for (i=0;i<k;i++) {
  6774. c+=x[i];
  6775. b=0;
  6776. if (c<0) {
  6777. b = c & mask;
  6778. b = -((c - b) / radix);
  6779. c+=b*radix;
  6780. }
  6781. x[i]=c & mask;
  6782. c = ((c - x[i]) / radix) - b;
  6783. if (!c) return; //stop carrying as soon as the carry is zero
  6784. }
  6785. }
  6786. //right shift bigInt x by n bits.
  6787. function rightShift_(x,n) {
  6788. var i;
  6789. var k=Math.floor(n/bpe);
  6790. if (k) {
  6791. for (i=0;i<x.length-k;i++) //right shift x by k elements
  6792. x[i]=x[i+k];
  6793. for (;i<x.length;i++)
  6794. x[i]=0;
  6795. n%=bpe;
  6796. }
  6797. for (i=0;i<x.length-1;i++) {
  6798. x[i]=mask & ((x[i+1]<<(bpe-n)) | (x[i]>>n));
  6799. }
  6800. x[i]>>=n;
  6801. }
  6802. //do x=floor(|x|/2)*sgn(x) for bigInt x in 2's complement
  6803. function halve_(x) {
  6804. var i;
  6805. for (i=0;i<x.length-1;i++) {
  6806. x[i]=mask & ((x[i+1]<<(bpe-1)) | (x[i]>>1));
  6807. }
  6808. x[i]=(x[i]>>1) | (x[i] & (radix>>1)); //most significant bit stays the same
  6809. }
  6810. //left shift bigInt x by n bits.
  6811. function leftShift_(x,n) {
  6812. var i;
  6813. var k=Math.floor(n/bpe);
  6814. if (k) {
  6815. for (i=x.length; i>=k; i--) //left shift x by k elements
  6816. x[i]=x[i-k];
  6817. for (;i>=0;i--)
  6818. x[i]=0;
  6819. n%=bpe;
  6820. }
  6821. if (!n)
  6822. return;
  6823. for (i=x.length-1;i>0;i--) {
  6824. x[i]=mask & ((x[i]<<n) | (x[i-1]>>(bpe-n)));
  6825. }
  6826. x[i]=mask & (x[i]<<n);
  6827. }
  6828. //do x=x*n where x is a bigInt and n is an integer.
  6829. //x must be large enough to hold the result.
  6830. function multInt_(x,n) {
  6831. var i,k,c,b;
  6832. if (!n)
  6833. return;
  6834. k=x.length;
  6835. c=0;
  6836. for (i=0;i<k;i++) {
  6837. c+=x[i]*n;
  6838. b=0;
  6839. if (c<0) {
  6840. b = c & mask;
  6841. b = -((c - b) / radix);
  6842. c+=b*radix;
  6843. }
  6844. x[i]=c & mask;
  6845. c = ((c - x[i]) / radix) - b;
  6846. }
  6847. }
  6848. //do x=floor(x/n) for bigInt x and integer n, and return the remainder
  6849. function divInt_(x,n) {
  6850. var i,r=0,s;
  6851. for (i=x.length-1;i>=0;i--) {
  6852. s=r*radix+x[i];
  6853. x[i]=Math.floor(s/n);
  6854. r=s%n;
  6855. }
  6856. return r;
  6857. }
  6858. //do the linear combination x=a*x+b*y for bigInts x and y, and integers a and b.
  6859. //x must be large enough to hold the answer.
  6860. function linComb_(x,y,a,b) {
  6861. var i,c,k,kk;
  6862. k=x.length<y.length ? x.length : y.length;
  6863. kk=x.length;
  6864. for (c=0,i=0;i<k;i++) {
  6865. c+=a*x[i]+b*y[i];
  6866. x[i]=c & mask;
  6867. c = (c - x[i]) / radix;
  6868. }
  6869. for (i=k;i<kk;i++) {
  6870. c+=a*x[i];
  6871. x[i]=c & mask;
  6872. c = (c - x[i]) / radix;
  6873. }
  6874. }
  6875. //do the linear combination x=a*x+b*(y<<(ys*bpe)) for bigInts x and y, and integers a, b and ys.
  6876. //x must be large enough to hold the answer.
  6877. function linCombShift_(x,y,b,ys) {
  6878. var i,c,k,kk;
  6879. k=x.length<ys+y.length ? x.length : ys+y.length;
  6880. kk=x.length;
  6881. for (c=0,i=ys;i<k;i++) {
  6882. c+=x[i]+b*y[i-ys];
  6883. x[i]=c & mask;
  6884. c = (c - x[i]) / radix;
  6885. }
  6886. for (i=k;c && i<kk;i++) {
  6887. c+=x[i];
  6888. x[i]=c & mask;
  6889. c = (c - x[i]) / radix;
  6890. }
  6891. }
  6892. //do x=x+(y<<(ys*bpe)) for bigInts x and y, and integers a,b and ys.
  6893. //x must be large enough to hold the answer.
  6894. function addShift_(x,y,ys) {
  6895. var i,c,k,kk;
  6896. k=x.length<ys+y.length ? x.length : ys+y.length;
  6897. kk=x.length;
  6898. for (c=0,i=ys;i<k;i++) {
  6899. c+=x[i]+y[i-ys];
  6900. x[i]=c & mask;
  6901. c = (c - x[i]) / radix;
  6902. }
  6903. for (i=k;c && i<kk;i++) {
  6904. c+=x[i];
  6905. x[i]=c & mask;
  6906. c = (c - x[i]) / radix;
  6907. }
  6908. }
  6909. //do x=x-(y<<(ys*bpe)) for bigInts x and y, and integers a,b and ys.
  6910. //x must be large enough to hold the answer.
  6911. function subShift_(x,y,ys) {
  6912. var i,c,k,kk;
  6913. k=x.length<ys+y.length ? x.length : ys+y.length;
  6914. kk=x.length;
  6915. for (c=0,i=ys;i<k;i++) {
  6916. c+=x[i]-y[i-ys];
  6917. x[i]=c & mask;
  6918. c = (c - x[i]) / radix;
  6919. }
  6920. for (i=k;c && i<kk;i++) {
  6921. c+=x[i];
  6922. x[i]=c & mask;
  6923. c = (c - x[i]) / radix;
  6924. }
  6925. }
  6926. //do x=x-y for bigInts x and y.
  6927. //x must be large enough to hold the answer.
  6928. //negative answers will be 2s complement
  6929. function sub_(x,y) {
  6930. var i,c,k,kk;
  6931. k=x.length<y.length ? x.length : y.length;
  6932. for (c=0,i=0;i<k;i++) {
  6933. c+=x[i]-y[i];
  6934. x[i]=c & mask;
  6935. c = (c - x[i]) / radix;
  6936. }
  6937. for (i=k;c && i<x.length;i++) {
  6938. c+=x[i];
  6939. x[i]=c & mask;
  6940. c = (c - x[i]) / radix;
  6941. }
  6942. }
  6943. //do x=x+y for bigInts x and y.
  6944. //x must be large enough to hold the answer.
  6945. function add_(x,y) {
  6946. var i,c,k,kk;
  6947. k=x.length<y.length ? x.length : y.length;
  6948. for (c=0,i=0;i<k;i++) {
  6949. c+=x[i]+y[i];
  6950. x[i]=c & mask;
  6951. c = (c - x[i]) / radix;
  6952. }
  6953. for (i=k;c && i<x.length;i++) {
  6954. c+=x[i];
  6955. x[i]=c & mask;
  6956. c = (c - x[i]) / radix;
  6957. }
  6958. }
  6959. //do x=x*y for bigInts x and y. This is faster when y<x.
  6960. function mult_(x,y) {
  6961. var i;
  6962. if (ss.length!=2*x.length)
  6963. ss=new Array(2*x.length);
  6964. copyInt_(ss,0);
  6965. for (i=0;i<y.length;i++)
  6966. if (y[i])
  6967. linCombShift_(ss,x,y[i],i); //ss=1*ss+y[i]*(x<<(i*bpe))
  6968. copy_(x,ss);
  6969. }
  6970. //do x=x mod n for bigInts x and n.
  6971. function mod_(x,n) {
  6972. if (s4.length!=x.length)
  6973. s4=dup(x);
  6974. else
  6975. copy_(s4,x);
  6976. if (s5.length!=x.length)
  6977. s5=dup(x);
  6978. divide_(s4,n,s5,x); //x = remainder of s4 / n
  6979. }
  6980. //do x=x*y mod n for bigInts x,y,n.
  6981. //for greater speed, let y<x.
  6982. function multMod_(x,y,n) {
  6983. var i;
  6984. if (s0.length!=2*x.length)
  6985. s0=new Array(2*x.length);
  6986. copyInt_(s0,0);
  6987. for (i=0;i<y.length;i++)
  6988. if (y[i])
  6989. linCombShift_(s0,x,y[i],i); //s0=1*s0+y[i]*(x<<(i*bpe))
  6990. mod_(s0,n);
  6991. copy_(x,s0);
  6992. }
  6993. //do x=x*x mod n for bigInts x,n.
  6994. function squareMod_(x,n) {
  6995. var i,j,d,c,kx,kn,k;
  6996. for (kx=x.length; kx>0 && !x[kx-1]; kx--); //ignore leading zeros in x
  6997. k=kx>n.length ? 2*kx : 2*n.length; //k=# elements in the product, which is twice the elements in the larger of x and n
  6998. if (s0.length!=k)
  6999. s0=new Array(k);
  7000. copyInt_(s0,0);
  7001. for (i=0;i<kx;i++) {
  7002. c=s0[2*i]+x[i]*x[i];
  7003. s0[2*i]=c & mask;
  7004. c = (c - s0[2*i]) / radix;
  7005. for (j=i+1;j<kx;j++) {
  7006. c=s0[i+j]+2*x[i]*x[j]+c;
  7007. s0[i+j]=(c & mask);
  7008. c = (c - s0[i+j]) / radix;
  7009. }
  7010. s0[i+kx]=c;
  7011. }
  7012. mod_(s0,n);
  7013. copy_(x,s0);
  7014. }
  7015. //return x with exactly k leading zero elements
  7016. function trim(x,k) {
  7017. var i,y;
  7018. for (i=x.length; i>0 && !x[i-1]; i--);
  7019. y=new Array(i+k);
  7020. copy_(y,x);
  7021. return y;
  7022. }
  7023. //do x=x**y mod n, where x,y,n are bigInts and ** is exponentiation. 0**0=1.
  7024. //this is faster when n is odd. x usually needs to have as many elements as n.
  7025. function powMod_(x,y,n) {
  7026. var k1,k2,kn,np;
  7027. if(s7.length!=n.length)
  7028. s7=dup(n);
  7029. //for even modulus, use a simple square-and-multiply algorithm,
  7030. //rather than using the more complex Montgomery algorithm.
  7031. if ((n[0]&1)==0) {
  7032. copy_(s7,x);
  7033. copyInt_(x,1);
  7034. while(!equalsInt(y,0)) {
  7035. if (y[0]&1)
  7036. multMod_(x,s7,n);
  7037. divInt_(y,2);
  7038. squareMod_(s7,n);
  7039. }
  7040. return;
  7041. }
  7042. //calculate np from n for the Montgomery multiplications
  7043. copyInt_(s7,0);
  7044. for (kn=n.length;kn>0 && !n[kn-1];kn--);
  7045. np=radix-inverseModInt(modInt(n,radix),radix);
  7046. s7[kn]=1;
  7047. multMod_(x ,s7,n); // x = x * 2**(kn*bp) mod n
  7048. if (s3.length!=x.length)
  7049. s3=dup(x);
  7050. else
  7051. copy_(s3,x);
  7052. for (k1=y.length-1;k1>0 & !y[k1]; k1--); //k1=first nonzero element of y
  7053. if (y[k1]==0) { //anything to the 0th power is 1
  7054. copyInt_(x,1);
  7055. return;
  7056. }
  7057. for (k2=1<<(bpe-1);k2 && !(y[k1] & k2); k2>>=1); //k2=position of first 1 bit in y[k1]
  7058. for (;;) {
  7059. if (!(k2>>=1)) { //look at next bit of y
  7060. k1--;
  7061. if (k1<0) {
  7062. mont_(x,one,n,np);
  7063. return;
  7064. }
  7065. k2=1<<(bpe-1);
  7066. }
  7067. mont_(x,x,n,np);
  7068. if (k2 & y[k1]) //if next bit is a 1
  7069. mont_(x,s3,n,np);
  7070. }
  7071. }
  7072. //do x=x*y*Ri mod n for bigInts x,y,n,
  7073. // where Ri = 2**(-kn*bpe) mod n, and kn is the
  7074. // number of elements in the n array, not
  7075. // counting leading zeros.
  7076. //x array must have at least as many elemnts as the n array
  7077. //It's OK if x and y are the same variable.
  7078. //must have:
  7079. // x,y < n
  7080. // n is odd
  7081. // np = -(n^(-1)) mod radix
  7082. function mont_(x,y,n,np) {
  7083. var i,j,c,ui,t,t2,ks;
  7084. var kn=n.length;
  7085. var ky=y.length;
  7086. if (sa.length!=kn)
  7087. sa=new Array(kn);
  7088. copyInt_(sa,0);
  7089. for (;kn>0 && n[kn-1]==0;kn--); //ignore leading zeros of n
  7090. for (;ky>0 && y[ky-1]==0;ky--); //ignore leading zeros of y
  7091. ks=sa.length-1; //sa will never have more than this many nonzero elements.
  7092. //the following loop consumes 95% of the runtime for randTruePrime_() and powMod_() for large numbers
  7093. for (i=0; i<kn; i++) {
  7094. t=sa[0]+x[i]*y[0];
  7095. ui=((t & mask) * np) & mask; //the inner "& mask" was needed on Safari (but not MSIE) at one time
  7096. c=(t+ui*n[0]);
  7097. c = (c - (c & mask)) / radix;
  7098. t=x[i];
  7099. //do sa=(sa+x[i]*y+ui*n)/b where b=2**bpe. Loop is unrolled 5-fold for speed
  7100. j=1;
  7101. for (;j<ky-4;) {
  7102. c+=sa[j]+ui*n[j]+t*y[j]; t2=sa[j-1]=c & mask; c=(c-t2)/radix; j++;
  7103. c+=sa[j]+ui*n[j]+t*y[j]; t2=sa[j-1]=c & mask; c=(c-t2)/radix; j++;
  7104. c+=sa[j]+ui*n[j]+t*y[j]; t2=sa[j-1]=c & mask; c=(c-t2)/radix; j++;
  7105. c+=sa[j]+ui*n[j]+t*y[j]; t2=sa[j-1]=c & mask; c=(c-t2)/radix; j++;
  7106. c+=sa[j]+ui*n[j]+t*y[j]; t2=sa[j-1]=c & mask; c=(c-t2)/radix; j++;
  7107. }
  7108. for (;j<ky;) {
  7109. c+=sa[j]+ui*n[j]+t*y[j]; t2=sa[j-1]=c & mask; c=(c-t2)/radix; j++;
  7110. }
  7111. for (;j<kn-4;) {
  7112. c+=sa[j]+ui*n[j]; t2=sa[j-1]=c & mask; c=(c-t2)/radix; j++;
  7113. c+=sa[j]+ui*n[j]; t2=sa[j-1]=c & mask; c=(c-t2)/radix; j++;
  7114. c+=sa[j]+ui*n[j]; t2=sa[j-1]=c & mask; c=(c-t2)/radix; j++;
  7115. c+=sa[j]+ui*n[j]; t2=sa[j-1]=c & mask; c=(c-t2)/radix; j++;
  7116. c+=sa[j]+ui*n[j]; t2=sa[j-1]=c & mask; c=(c-t2)/radix; j++;
  7117. }
  7118. for (;j<kn;) {
  7119. c+=sa[j]+ui*n[j]; t2=sa[j-1]=c & mask; c=(c-t2)/radix; j++;
  7120. }
  7121. for (;j<ks;) {
  7122. c+=sa[j]; t2=sa[j-1]=c & mask; c=(c-t2)/radix; j++;
  7123. }
  7124. sa[j-1]=c & mask;
  7125. }
  7126. if (!greater(n,sa))
  7127. sub_(sa,n);
  7128. copy_(x,sa);
  7129. }
  7130. // otr.js additions
  7131. // computes num / den mod n
  7132. function divMod(num, den, n) {
  7133. return multMod(num, inverseMod(den, n), n)
  7134. }
  7135. // computes one - two mod n
  7136. function subMod(one, two, n) {
  7137. one = mod(one, n)
  7138. two = mod(two, n)
  7139. if (greater(two, one)) one = add(one, n)
  7140. return sub(one, two)
  7141. }
  7142. // computes 2^m as a bigInt
  7143. function twoToThe(m) {
  7144. var b = Math.floor(m / bpe) + 2
  7145. var t = new Array(b)
  7146. for (var i = 0; i < b; i++) t[i] = 0
  7147. t[b - 2] = 1 << (m % bpe)
  7148. return t
  7149. }
  7150. // cache these results for faster lookup
  7151. var _num2bin = (function () {
  7152. var i = 0, _num2bin= {}
  7153. for (; i < 0x100; ++i) {
  7154. _num2bin[i] = String.fromCharCode(i) // 0 -> "\00"
  7155. }
  7156. return _num2bin
  7157. }())
  7158. // serialize a bigInt to an ascii string
  7159. // padded up to pad length
  7160. function bigInt2bits(bi, pad) {
  7161. pad || (pad = 0)
  7162. bi = dup(bi)
  7163. var ba = ''
  7164. while (!isZero(bi)) {
  7165. ba = _num2bin[bi[0] & 0xff] + ba
  7166. rightShift_(bi, 8)
  7167. }
  7168. while (ba.length < pad) {
  7169. ba = '\x00' + ba
  7170. }
  7171. return ba
  7172. }
  7173. // converts a byte array to a bigInt
  7174. function ba2bigInt(data) {
  7175. var mpi = str2bigInt('0', 10, data.length)
  7176. data.forEach(function (d, i) {
  7177. if (i) leftShift_(mpi, 8)
  7178. mpi[0] |= d
  7179. })
  7180. return mpi
  7181. }
  7182. // returns a function that returns an array of n bytes
  7183. var randomBytes = (function () {
  7184. // in node
  7185. if ( typeof crypto !== 'undefined' &&
  7186. typeof crypto.randomBytes === 'function' ) {
  7187. return function (n) {
  7188. try {
  7189. var buf = crypto.randomBytes(n)
  7190. } catch (e) { throw e }
  7191. return Array.prototype.slice.call(buf, 0)
  7192. }
  7193. }
  7194. // in browser
  7195. else if ( typeof crypto !== 'undefined' &&
  7196. typeof crypto.getRandomValues === 'function' ) {
  7197. return function (n) {
  7198. var buf = new Uint8Array(n)
  7199. crypto.getRandomValues(buf)
  7200. return Array.prototype.slice.call(buf, 0)
  7201. }
  7202. }
  7203. // err
  7204. else {
  7205. console.log('Keys should not be generated without CSPRNG.');
  7206. return;
  7207. // throw new Error('Keys should not be generated without CSPRNG.')
  7208. }
  7209. }())
  7210. // Salsa 20 in webworker needs a 40 byte seed
  7211. function getSeed() {
  7212. return randomBytes(40)
  7213. }
  7214. // returns a single random byte
  7215. function randomByte() {
  7216. return randomBytes(1)[0]
  7217. }
  7218. // returns a k-bit random integer
  7219. function randomBitInt(k) {
  7220. if (k > 31) throw new Error("Too many bits.")
  7221. var i = 0, r = 0
  7222. var b = Math.floor(k / 8)
  7223. var mask = (1 << (k % 8)) - 1
  7224. if (mask) r = randomByte() & mask
  7225. for (; i < b; i++)
  7226. r = (256 * r) + randomByte()
  7227. return r
  7228. }
  7229. return {
  7230. str2bigInt : str2bigInt
  7231. , bigInt2str : bigInt2str
  7232. , int2bigInt : int2bigInt
  7233. , multMod : multMod
  7234. , powMod : powMod
  7235. , inverseMod : inverseMod
  7236. , randBigInt : randBigInt
  7237. , randBigInt_ : randBigInt_
  7238. , equals : equals
  7239. , equalsInt : equalsInt
  7240. , sub : sub
  7241. , mod : mod
  7242. , modInt : modInt
  7243. , mult : mult
  7244. , divInt_ : divInt_
  7245. , rightShift_ : rightShift_
  7246. , dup : dup
  7247. , greater : greater
  7248. , add : add
  7249. , isZero : isZero
  7250. , bitSize : bitSize
  7251. , millerRabin : millerRabin
  7252. , divide_ : divide_
  7253. , trim : trim
  7254. , primes : primes
  7255. , findPrimes : findPrimes
  7256. , getSeed : getSeed
  7257. , divMod : divMod
  7258. , subMod : subMod
  7259. , twoToThe : twoToThe
  7260. , bigInt2bits : bigInt2bits
  7261. , ba2bigInt : ba2bigInt
  7262. }
  7263. }))
  7264. ;
  7265. /*!
  7266. * EventEmitter v4.2.3 - git.io/ee
  7267. * Oliver Caldwell
  7268. * MIT license
  7269. * @preserve
  7270. */
  7271. (function () {
  7272. /**
  7273. * Class for managing events.
  7274. * Can be extended to provide event functionality in other classes.
  7275. *
  7276. * @class EventEmitter Manages event registering and emitting.
  7277. */
  7278. function EventEmitter() {}
  7279. // Shortcuts to improve speed and size
  7280. // Easy access to the prototype
  7281. var proto = EventEmitter.prototype;
  7282. /**
  7283. * Finds the index of the listener for the event in it's storage array.
  7284. *
  7285. * @param {Function[]} listeners Array of listeners to search through.
  7286. * @param {Function} listener Method to look for.
  7287. * @return {Number} Index of the specified listener, -1 if not found
  7288. * @api private
  7289. */
  7290. function indexOfListener(listeners, listener) {
  7291. var i = listeners.length;
  7292. while (i--) {
  7293. if (listeners[i].listener === listener) {
  7294. return i;
  7295. }
  7296. }
  7297. return -1;
  7298. }
  7299. /**
  7300. * Alias a method while keeping the context correct, to allow for overwriting of target method.
  7301. *
  7302. * @param {String} name The name of the target method.
  7303. * @return {Function} The aliased method
  7304. * @api private
  7305. */
  7306. function alias(name) {
  7307. return function aliasClosure() {
  7308. return this[name].apply(this, arguments);
  7309. };
  7310. }
  7311. /**
  7312. * Returns the listener array for the specified event.
  7313. * Will initialise the event object and listener arrays if required.
  7314. * Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object containing bar and baz. But only if you have either defined them with defineEvent or added some listeners to them.
  7315. * Each property in the object response is an array of listener functions.
  7316. *
  7317. * @param {String|RegExp} evt Name of the event to return the listeners from.
  7318. * @return {Function[]|Object} All listener functions for the event.
  7319. */
  7320. proto.getListeners = function getListeners(evt) {
  7321. var events = this._getEvents();
  7322. var response;
  7323. var key;
  7324. // Return a concatenated array of all matching events if
  7325. // the selector is a regular expression.
  7326. if (typeof evt === 'object') {
  7327. response = {};
  7328. for (key in events) {
  7329. if (events.hasOwnProperty(key) && evt.test(key)) {
  7330. response[key] = events[key];
  7331. }
  7332. }
  7333. }
  7334. else {
  7335. response = events[evt] || (events[evt] = []);
  7336. }
  7337. return response;
  7338. };
  7339. /**
  7340. * Takes a list of listener objects and flattens it into a list of listener functions.
  7341. *
  7342. * @param {Object[]} listeners Raw listener objects.
  7343. * @return {Function[]} Just the listener functions.
  7344. */
  7345. proto.flattenListeners = function flattenListeners(listeners) {
  7346. var flatListeners = [];
  7347. var i;
  7348. for (i = 0; i < listeners.length; i += 1) {
  7349. flatListeners.push(listeners[i].listener);
  7350. }
  7351. return flatListeners;
  7352. };
  7353. /**
  7354. * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful.
  7355. *
  7356. * @param {String|RegExp} evt Name of the event to return the listeners from.
  7357. * @return {Object} All listener functions for an event in an object.
  7358. */
  7359. proto.getListenersAsObject = function getListenersAsObject(evt) {
  7360. var listeners = this.getListeners(evt);
  7361. var response;
  7362. if (listeners instanceof Array) {
  7363. response = {};
  7364. response[evt] = listeners;
  7365. }
  7366. return response || listeners;
  7367. };
  7368. /**
  7369. * Adds a listener function to the specified event.
  7370. * The listener will not be added if it is a duplicate.
  7371. * If the listener returns true then it will be removed after it is called.
  7372. * If you pass a regular expression as the event name then the listener will be added to all events that match it.
  7373. *
  7374. * @param {String|RegExp} evt Name of the event to attach the listener to.
  7375. * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
  7376. * @return {Object} Current instance of EventEmitter for chaining.
  7377. */
  7378. proto.addListener = function addListener(evt, listener) {
  7379. var listeners = this.getListenersAsObject(evt);
  7380. var listenerIsWrapped = typeof listener === 'object';
  7381. var key;
  7382. for (key in listeners) {
  7383. if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {
  7384. listeners[key].push(listenerIsWrapped ? listener : {
  7385. listener: listener,
  7386. once: false
  7387. });
  7388. }
  7389. }
  7390. return this;
  7391. };
  7392. /**
  7393. * Alias of addListener
  7394. */
  7395. proto.on = alias('addListener');
  7396. /**
  7397. * Semi-alias of addListener. It will add a listener that will be
  7398. * automatically removed after it's first execution.
  7399. *
  7400. * @param {String|RegExp} evt Name of the event to attach the listener to.
  7401. * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
  7402. * @return {Object} Current instance of EventEmitter for chaining.
  7403. */
  7404. proto.addOnceListener = function addOnceListener(evt, listener) {
  7405. return this.addListener(evt, {
  7406. listener: listener,
  7407. once: true
  7408. });
  7409. };
  7410. /**
  7411. * Alias of addOnceListener.
  7412. */
  7413. proto.once = alias('addOnceListener');
  7414. /**
  7415. * Defines an event name. This is required if you want to use a regex to add a listener to multiple events at once. If you don't do this then how do you expect it to know what event to add to? Should it just add to every possible match for a regex? No. That is scary and bad.
  7416. * You need to tell it what event names should be matched by a regex.
  7417. *
  7418. * @param {String} evt Name of the event to create.
  7419. * @return {Object} Current instance of EventEmitter for chaining.
  7420. */
  7421. proto.defineEvent = function defineEvent(evt) {
  7422. this.getListeners(evt);
  7423. return this;
  7424. };
  7425. /**
  7426. * Uses defineEvent to define multiple events.
  7427. *
  7428. * @param {String[]} evts An array of event names to define.
  7429. * @return {Object} Current instance of EventEmitter for chaining.
  7430. */
  7431. proto.defineEvents = function defineEvents(evts) {
  7432. for (var i = 0; i < evts.length; i += 1) {
  7433. this.defineEvent(evts[i]);
  7434. }
  7435. return this;
  7436. };
  7437. /**
  7438. * Removes a listener function from the specified event.
  7439. * When passed a regular expression as the event name, it will remove the listener from all events that match it.
  7440. *
  7441. * @param {String|RegExp} evt Name of the event to remove the listener from.
  7442. * @param {Function} listener Method to remove from the event.
  7443. * @return {Object} Current instance of EventEmitter for chaining.
  7444. */
  7445. proto.removeListener = function removeListener(evt, listener) {
  7446. var listeners = this.getListenersAsObject(evt);
  7447. var index;
  7448. var key;
  7449. for (key in listeners) {
  7450. if (listeners.hasOwnProperty(key)) {
  7451. index = indexOfListener(listeners[key], listener);
  7452. if (index !== -1) {
  7453. listeners[key].splice(index, 1);
  7454. }
  7455. }
  7456. }
  7457. return this;
  7458. };
  7459. /**
  7460. * Alias of removeListener
  7461. */
  7462. proto.off = alias('removeListener');
  7463. /**
  7464. * Adds listeners in bulk using the manipulateListeners method.
  7465. * If you pass an object as the second argument you can add to multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. You can also pass it an event name and an array of listeners to be added.
  7466. * You can also pass it a regular expression to add the array of listeners to all events that match it.
  7467. * Yeah, this function does quite a bit. That's probably a bad thing.
  7468. *
  7469. * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add to multiple events at once.
  7470. * @param {Function[]} [listeners] An optional array of listener functions to add.
  7471. * @return {Object} Current instance of EventEmitter for chaining.
  7472. */
  7473. proto.addListeners = function addListeners(evt, listeners) {
  7474. // Pass through to manipulateListeners
  7475. return this.manipulateListeners(false, evt, listeners);
  7476. };
  7477. /**
  7478. * Removes listeners in bulk using the manipulateListeners method.
  7479. * If you pass an object as the second argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
  7480. * You can also pass it an event name and an array of listeners to be removed.
  7481. * You can also pass it a regular expression to remove the listeners from all events that match it.
  7482. *
  7483. * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to remove from multiple events at once.
  7484. * @param {Function[]} [listeners] An optional array of listener functions to remove.
  7485. * @return {Object} Current instance of EventEmitter for chaining.
  7486. */
  7487. proto.removeListeners = function removeListeners(evt, listeners) {
  7488. // Pass through to manipulateListeners
  7489. return this.manipulateListeners(true, evt, listeners);
  7490. };
  7491. /**
  7492. * Edits listeners in bulk. The addListeners and removeListeners methods both use this to do their job. You should really use those instead, this is a little lower level.
  7493. * The first argument will determine if the listeners are removed (true) or added (false).
  7494. * If you pass an object as the second argument you can add/remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
  7495. * You can also pass it an event name and an array of listeners to be added/removed.
  7496. * You can also pass it a regular expression to manipulate the listeners of all events that match it.
  7497. *
  7498. * @param {Boolean} remove True if you want to remove listeners, false if you want to add.
  7499. * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add/remove from multiple events at once.
  7500. * @param {Function[]} [listeners] An optional array of listener functions to add/remove.
  7501. * @return {Object} Current instance of EventEmitter for chaining.
  7502. */
  7503. proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) {
  7504. var i;
  7505. var value;
  7506. var single = remove ? this.removeListener : this.addListener;
  7507. var multiple = remove ? this.removeListeners : this.addListeners;
  7508. // If evt is an object then pass each of it's properties to this method
  7509. if (typeof evt === 'object' && !(evt instanceof RegExp)) {
  7510. for (i in evt) {
  7511. if (evt.hasOwnProperty(i) && (value = evt[i])) {
  7512. // Pass the single listener straight through to the singular method
  7513. if (typeof value === 'function') {
  7514. single.call(this, i, value);
  7515. }
  7516. else {
  7517. // Otherwise pass back to the multiple function
  7518. multiple.call(this, i, value);
  7519. }
  7520. }
  7521. }
  7522. }
  7523. else {
  7524. // So evt must be a string
  7525. // And listeners must be an array of listeners
  7526. // Loop over it and pass each one to the multiple method
  7527. i = listeners.length;
  7528. while (i--) {
  7529. single.call(this, evt, listeners[i]);
  7530. }
  7531. }
  7532. return this;
  7533. };
  7534. /**
  7535. * Removes all listeners from a specified event.
  7536. * If you do not specify an event then all listeners will be removed.
  7537. * That means every event will be emptied.
  7538. * You can also pass a regex to remove all events that match it.
  7539. *
  7540. * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed.
  7541. * @return {Object} Current instance of EventEmitter for chaining.
  7542. */
  7543. proto.removeEvent = function removeEvent(evt) {
  7544. var type = typeof evt;
  7545. var events = this._getEvents();
  7546. var key;
  7547. // Remove different things depending on the state of evt
  7548. if (type === 'string') {
  7549. // Remove all listeners for the specified event
  7550. delete events[evt];
  7551. }
  7552. else if (type === 'object') {
  7553. // Remove all events matching the regex.
  7554. for (key in events) {
  7555. if (events.hasOwnProperty(key) && evt.test(key)) {
  7556. delete events[key];
  7557. }
  7558. }
  7559. }
  7560. else {
  7561. // Remove all listeners in all events
  7562. delete this._events;
  7563. }
  7564. return this;
  7565. };
  7566. /**
  7567. * Emits an event of your choice.
  7568. * When emitted, every listener attached to that event will be executed.
  7569. * If you pass the optional argument array then those arguments will be passed to every listener upon execution.
  7570. * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
  7571. * So they will not arrive within the array on the other side, they will be separate.
  7572. * You can also pass a regular expression to emit to all events that match it.
  7573. *
  7574. * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
  7575. * @param {Array} [args] Optional array of arguments to be passed to each listener.
  7576. * @return {Object} Current instance of EventEmitter for chaining.
  7577. */
  7578. proto.emitEvent = function emitEvent(evt, args) {
  7579. var listeners = this.getListenersAsObject(evt);
  7580. var listener;
  7581. var i;
  7582. var key;
  7583. var response;
  7584. for (key in listeners) {
  7585. if (listeners.hasOwnProperty(key)) {
  7586. i = listeners[key].length;
  7587. while (i--) {
  7588. // If the listener returns true then it shall be removed from the event
  7589. // The function is executed either with a basic call or an apply if there is an args array
  7590. listener = listeners[key][i];
  7591. if (listener.once === true) {
  7592. this.removeListener(evt, listener.listener);
  7593. }
  7594. response = listener.listener.apply(this, args || []);
  7595. if (response === this._getOnceReturnValue()) {
  7596. this.removeListener(evt, listener.listener);
  7597. }
  7598. }
  7599. }
  7600. }
  7601. return this;
  7602. };
  7603. /**
  7604. * Alias of emitEvent
  7605. */
  7606. proto.trigger = alias('emitEvent');
  7607. /**
  7608. * Subtly different from emitEvent in that it will pass its arguments on to the listeners, as opposed to taking a single array of arguments to pass on.
  7609. * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it.
  7610. *
  7611. * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
  7612. * @param {...*} Optional additional arguments to be passed to each listener.
  7613. * @return {Object} Current instance of EventEmitter for chaining.
  7614. */
  7615. proto.emit = function emit(evt) {
  7616. var args = Array.prototype.slice.call(arguments, 1);
  7617. return this.emitEvent(evt, args);
  7618. };
  7619. /**
  7620. * Sets the current value to check against when executing listeners. If a
  7621. * listeners return value matches the one set here then it will be removed
  7622. * after execution. This value defaults to true.
  7623. *
  7624. * @param {*} value The new value to check for when executing listeners.
  7625. * @return {Object} Current instance of EventEmitter for chaining.
  7626. */
  7627. proto.setOnceReturnValue = function setOnceReturnValue(value) {
  7628. this._onceReturnValue = value;
  7629. return this;
  7630. };
  7631. /**
  7632. * Fetches the current value to check against when executing listeners. If
  7633. * the listeners return value matches this one then it should be removed
  7634. * automatically. It will return true by default.
  7635. *
  7636. * @return {*|Boolean} The current value to check for or the default, true.
  7637. * @api private
  7638. */
  7639. proto._getOnceReturnValue = function _getOnceReturnValue() {
  7640. if (this.hasOwnProperty('_onceReturnValue')) {
  7641. return this._onceReturnValue;
  7642. }
  7643. else {
  7644. return true;
  7645. }
  7646. };
  7647. /**
  7648. * Fetches the events object and creates one if required.
  7649. *
  7650. * @return {Object} The events storage object.
  7651. * @api private
  7652. */
  7653. proto._getEvents = function _getEvents() {
  7654. return this._events || (this._events = {});
  7655. };
  7656. // Expose the class either via AMD, CommonJS or the global object
  7657. if (typeof define === 'function' && define.amd) {
  7658. define('eventemitter',[],function () {
  7659. return EventEmitter;
  7660. });
  7661. }
  7662. else if (typeof module === 'object' && module.exports){
  7663. module.exports = EventEmitter;
  7664. }
  7665. else {
  7666. this.EventEmitter = EventEmitter;
  7667. }
  7668. }.call(this));
  7669. /*!
  7670. otr.js v0.2.12 - 2014-04-15
  7671. (c) 2014 - Arlo Breault <arlolra@gmail.com>
  7672. Freely distributed under the MPL v2.0 license.
  7673. This file is concatenated for the browser.
  7674. Please see: https://github.com/arlolra/otr
  7675. */
  7676. ;(function (root, factory) {
  7677. if (typeof define === 'function' && define.amd) {
  7678. define('otr',[
  7679. "jquery",
  7680. "jquery.browser",
  7681. "bigint",
  7682. "crypto",
  7683. "eventemitter"
  7684. ], function ($, dummy, BigInt, CryptoJS, EventEmitter) {
  7685. if ($.browser.msie) {
  7686. return undefined;
  7687. }
  7688. var root = {
  7689. BigInt: BigInt
  7690. , CryptoJS: CryptoJS
  7691. , EventEmitter: EventEmitter
  7692. , OTR: {}
  7693. , DSA: {}
  7694. }
  7695. return factory.call(root)
  7696. })
  7697. } else {
  7698. root.OTR = {}
  7699. root.DSA = {}
  7700. factory.call(root)
  7701. }
  7702. }(this, function () {
  7703. ;(function () {
  7704. var root = this
  7705. var CONST = {
  7706. // diffie-heilman
  7707. N : 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF'
  7708. , G : '2'
  7709. // otr message states
  7710. , MSGSTATE_PLAINTEXT : 0
  7711. , MSGSTATE_ENCRYPTED : 1
  7712. , MSGSTATE_FINISHED : 2
  7713. // otr auth states
  7714. , AUTHSTATE_NONE : 0
  7715. , AUTHSTATE_AWAITING_DHKEY : 1
  7716. , AUTHSTATE_AWAITING_REVEALSIG : 2
  7717. , AUTHSTATE_AWAITING_SIG : 3
  7718. // whitespace tags
  7719. , WHITESPACE_TAG : '\x20\x09\x20\x20\x09\x09\x09\x09\x20\x09\x20\x09\x20\x09\x20\x20'
  7720. , WHITESPACE_TAG_V2 : '\x20\x20\x09\x09\x20\x20\x09\x20'
  7721. , WHITESPACE_TAG_V3 : '\x20\x20\x09\x09\x20\x20\x09\x09'
  7722. // otr tags
  7723. , OTR_TAG : '?OTR'
  7724. , OTR_VERSION_1 : '\x00\x01'
  7725. , OTR_VERSION_2 : '\x00\x02'
  7726. , OTR_VERSION_3 : '\x00\x03'
  7727. // smp machine states
  7728. , SMPSTATE_EXPECT0 : 0
  7729. , SMPSTATE_EXPECT1 : 1
  7730. , SMPSTATE_EXPECT2 : 2
  7731. , SMPSTATE_EXPECT3 : 3
  7732. , SMPSTATE_EXPECT4 : 4
  7733. // unstandard status codes
  7734. , STATUS_SEND_QUERY : 0
  7735. , STATUS_AKE_INIT : 1
  7736. , STATUS_AKE_SUCCESS : 2
  7737. , STATUS_END_OTR : 3
  7738. }
  7739. if (typeof module !== 'undefined' && module.exports) {
  7740. module.exports = CONST
  7741. } else {
  7742. root.OTR.CONST = CONST
  7743. }
  7744. }).call(this)
  7745. ;(function () {
  7746. var root = this
  7747. var HLP = {}, CryptoJS, BigInt
  7748. if (typeof module !== 'undefined' && module.exports) {
  7749. module.exports = HLP = {}
  7750. CryptoJS = require('../vendor/crypto.js')
  7751. BigInt = require('../vendor/bigint.js')
  7752. } else {
  7753. if (root.OTR) root.OTR.HLP = HLP
  7754. if (root.DSA) root.DSA.HLP = HLP
  7755. CryptoJS = root.CryptoJS
  7756. BigInt = root.BigInt
  7757. }
  7758. // data types (byte lengths)
  7759. var DTS = {
  7760. BYTE : 1
  7761. , SHORT : 2
  7762. , INT : 4
  7763. , CTR : 8
  7764. , MAC : 20
  7765. , SIG : 40
  7766. }
  7767. // otr message wrapper begin and end
  7768. var WRAPPER_BEGIN = "?OTR"
  7769. , WRAPPER_END = "."
  7770. var TWO = BigInt.str2bigInt('2', 10)
  7771. HLP.debug = function (msg) {
  7772. // used as HLP.debug.call(ctx, msg)
  7773. if ( this.debug &&
  7774. typeof this.debug !== 'function' &&
  7775. typeof console !== 'undefined'
  7776. ) console.log(msg)
  7777. }
  7778. HLP.extend = function (child, parent) {
  7779. for (var key in parent) {
  7780. if (Object.hasOwnProperty.call(parent, key))
  7781. child[key] = parent[key]
  7782. }
  7783. function Ctor() { this.constructor = child }
  7784. Ctor.prototype = parent.prototype
  7785. child.prototype = new Ctor()
  7786. child.__super__ = parent.prototype
  7787. }
  7788. // constant-time string comparison
  7789. HLP.compare = function (str1, str2) {
  7790. if (str1.length !== str2.length)
  7791. return false
  7792. var i = 0, result = 0
  7793. for (; i < str1.length; i++)
  7794. result |= str1[i].charCodeAt(0) ^ str2[i].charCodeAt(0)
  7795. return result === 0
  7796. }
  7797. HLP.randomExponent = function () {
  7798. return BigInt.randBigInt(1536)
  7799. }
  7800. HLP.smpHash = function (version, fmpi, smpi) {
  7801. var sha256 = CryptoJS.algo.SHA256.create()
  7802. sha256.update(CryptoJS.enc.Latin1.parse(HLP.packBytes(version, DTS.BYTE)))
  7803. sha256.update(CryptoJS.enc.Latin1.parse(HLP.packMPI(fmpi)))
  7804. if (smpi) sha256.update(CryptoJS.enc.Latin1.parse(HLP.packMPI(smpi)))
  7805. var hash = sha256.finalize()
  7806. return HLP.bits2bigInt(hash.toString(CryptoJS.enc.Latin1))
  7807. }
  7808. HLP.makeMac = function (aesctr, m) {
  7809. var pass = CryptoJS.enc.Latin1.parse(m)
  7810. var mac = CryptoJS.HmacSHA256(CryptoJS.enc.Latin1.parse(aesctr), pass)
  7811. return HLP.mask(mac.toString(CryptoJS.enc.Latin1), 0, 160)
  7812. }
  7813. HLP.make1Mac = function (aesctr, m) {
  7814. var pass = CryptoJS.enc.Latin1.parse(m)
  7815. var mac = CryptoJS.HmacSHA1(CryptoJS.enc.Latin1.parse(aesctr), pass)
  7816. return mac.toString(CryptoJS.enc.Latin1)
  7817. }
  7818. HLP.encryptAes = function (msg, c, iv) {
  7819. var opts = {
  7820. mode: CryptoJS.mode.CTR
  7821. , iv: CryptoJS.enc.Latin1.parse(iv)
  7822. , padding: CryptoJS.pad.NoPadding
  7823. }
  7824. var aesctr = CryptoJS.AES.encrypt(
  7825. msg
  7826. , CryptoJS.enc.Latin1.parse(c)
  7827. , opts
  7828. )
  7829. var aesctr_decoded = CryptoJS.enc.Base64.parse(aesctr.toString())
  7830. return CryptoJS.enc.Latin1.stringify(aesctr_decoded)
  7831. }
  7832. HLP.decryptAes = function (msg, c, iv) {
  7833. msg = CryptoJS.enc.Latin1.parse(msg)
  7834. var opts = {
  7835. mode: CryptoJS.mode.CTR
  7836. , iv: CryptoJS.enc.Latin1.parse(iv)
  7837. , padding: CryptoJS.pad.NoPadding
  7838. }
  7839. return CryptoJS.AES.decrypt(
  7840. CryptoJS.enc.Base64.stringify(msg)
  7841. , CryptoJS.enc.Latin1.parse(c)
  7842. , opts
  7843. )
  7844. }
  7845. HLP.multPowMod = function (a, b, c, d, e) {
  7846. return BigInt.multMod(BigInt.powMod(a, b, e), BigInt.powMod(c, d, e), e)
  7847. }
  7848. HLP.ZKP = function (v, c, d, e) {
  7849. return BigInt.equals(c, HLP.smpHash(v, d, e))
  7850. }
  7851. // greater than, or equal
  7852. HLP.GTOE = function (a, b) {
  7853. return (BigInt.equals(a, b) || BigInt.greater(a, b))
  7854. }
  7855. HLP.between = function (x, a, b) {
  7856. return (BigInt.greater(x, a) && BigInt.greater(b, x))
  7857. }
  7858. HLP.checkGroup = function (g, N_MINUS_2) {
  7859. return HLP.GTOE(g, TWO) && HLP.GTOE(N_MINUS_2, g)
  7860. }
  7861. HLP.h1 = function (b, secbytes) {
  7862. var sha1 = CryptoJS.algo.SHA1.create()
  7863. sha1.update(CryptoJS.enc.Latin1.parse(b))
  7864. sha1.update(CryptoJS.enc.Latin1.parse(secbytes))
  7865. return (sha1.finalize()).toString(CryptoJS.enc.Latin1)
  7866. }
  7867. HLP.h2 = function (b, secbytes) {
  7868. var sha256 = CryptoJS.algo.SHA256.create()
  7869. sha256.update(CryptoJS.enc.Latin1.parse(b))
  7870. sha256.update(CryptoJS.enc.Latin1.parse(secbytes))
  7871. return (sha256.finalize()).toString(CryptoJS.enc.Latin1)
  7872. }
  7873. HLP.mask = function (bytes, start, n) {
  7874. return bytes.substr(start / 8, n / 8)
  7875. }
  7876. var _toString = String.fromCharCode;
  7877. HLP.packBytes = function (val, bytes) {
  7878. val = val.toString(16)
  7879. var nex, res = '' // big-endian, unsigned long
  7880. for (; bytes > 0; bytes--) {
  7881. nex = val.length ? val.substr(-2, 2) : '0'
  7882. val = val.substr(0, val.length - 2)
  7883. res = _toString(parseInt(nex, 16)) + res
  7884. }
  7885. return res
  7886. }
  7887. HLP.packINT = function (d) {
  7888. return HLP.packBytes(d, DTS.INT)
  7889. }
  7890. HLP.packCtr = function (d) {
  7891. return HLP.padCtr(HLP.packBytes(d, DTS.CTR))
  7892. }
  7893. HLP.padCtr = function (ctr) {
  7894. return ctr + '\x00\x00\x00\x00\x00\x00\x00\x00'
  7895. }
  7896. HLP.unpackCtr = function (d) {
  7897. d = HLP.toByteArray(d.substring(0, 8))
  7898. return HLP.unpack(d)
  7899. }
  7900. HLP.unpack = function (arr) {
  7901. var val = 0, i = 0, len = arr.length
  7902. for (; i < len; i++) {
  7903. val = (val * 256) + arr[i]
  7904. }
  7905. return val
  7906. }
  7907. HLP.packData = function (d) {
  7908. return HLP.packINT(d.length) + d
  7909. }
  7910. HLP.bits2bigInt = function (bits) {
  7911. bits = HLP.toByteArray(bits)
  7912. return BigInt.ba2bigInt(bits)
  7913. }
  7914. HLP.packMPI = function (mpi) {
  7915. return HLP.packData(BigInt.bigInt2bits(BigInt.trim(mpi, 0)))
  7916. }
  7917. HLP.packSHORT = function (short) {
  7918. return HLP.packBytes(short, DTS.SHORT)
  7919. }
  7920. HLP.unpackSHORT = function (short) {
  7921. short = HLP.toByteArray(short)
  7922. return HLP.unpack(short)
  7923. }
  7924. HLP.packTLV = function (type, value) {
  7925. return HLP.packSHORT(type) + HLP.packSHORT(value.length) + value
  7926. }
  7927. HLP.readLen = function (msg) {
  7928. msg = HLP.toByteArray(msg.substring(0, 4))
  7929. return HLP.unpack(msg)
  7930. }
  7931. HLP.readData = function (data) {
  7932. var n = HLP.unpack(data.splice(0, 4))
  7933. return [n, data]
  7934. }
  7935. HLP.readMPI = function (data) {
  7936. data = HLP.toByteArray(data)
  7937. data = HLP.readData(data)
  7938. return BigInt.ba2bigInt(data[1])
  7939. }
  7940. HLP.packMPIs = function (arr) {
  7941. return arr.reduce(function (prv, cur) {
  7942. return prv + HLP.packMPI(cur)
  7943. }, '')
  7944. }
  7945. HLP.unpackMPIs = function (num, mpis) {
  7946. var i = 0, arr = []
  7947. for (; i < num; i++) arr.push('MPI')
  7948. return (HLP.splitype(arr, mpis)).map(function (m) {
  7949. return HLP.readMPI(m)
  7950. })
  7951. }
  7952. HLP.wrapMsg = function (msg, fs, v3, our_it, their_it) {
  7953. msg = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Latin1.parse(msg))
  7954. msg = WRAPPER_BEGIN + ":" + msg + WRAPPER_END
  7955. var its
  7956. if (v3) {
  7957. its = '|'
  7958. its += (HLP.readLen(our_it)).toString(16)
  7959. its += '|'
  7960. its += (HLP.readLen(their_it)).toString(16)
  7961. }
  7962. if (!fs) return [null, msg]
  7963. var n = Math.ceil(msg.length / fs)
  7964. if (n > 65535) return ['Too many fragments']
  7965. if (n == 1) return [null, msg]
  7966. var k, bi, ei, frag, mf, mfs = []
  7967. for (k = 1; k <= n; k++) {
  7968. bi = (k - 1) * fs
  7969. ei = k * fs
  7970. frag = msg.slice(bi, ei)
  7971. mf = WRAPPER_BEGIN
  7972. if (v3) mf += its
  7973. mf += ',' + k + ','
  7974. mf += n + ','
  7975. mf += frag + ','
  7976. mfs.push(mf)
  7977. }
  7978. return [null, mfs]
  7979. }
  7980. HLP.splitype = function splitype(arr, msg) {
  7981. var data = []
  7982. arr.forEach(function (a) {
  7983. var str
  7984. switch (a) {
  7985. case 'PUBKEY':
  7986. str = splitype(['SHORT', 'MPI', 'MPI', 'MPI', 'MPI'], msg).join('')
  7987. break
  7988. case 'DATA': // falls through
  7989. case 'MPI':
  7990. str = msg.substring(0, HLP.readLen(msg) + 4)
  7991. break
  7992. default:
  7993. str = msg.substring(0, DTS[a])
  7994. }
  7995. data.push(str)
  7996. msg = msg.substring(str.length)
  7997. })
  7998. return data
  7999. }
  8000. // https://github.com/msgpack/msgpack-javascript/blob/master/msgpack.js
  8001. var _bin2num = (function () {
  8002. var i = 0, _bin2num = {}
  8003. for (; i < 0x100; ++i) {
  8004. _bin2num[String.fromCharCode(i)] = i // "\00" -> 0x00
  8005. }
  8006. for (i = 0x80; i < 0x100; ++i) { // [Webkit][Gecko]
  8007. _bin2num[String.fromCharCode(0xf700 + i)] = i // "\f780" -> 0x80
  8008. }
  8009. return _bin2num
  8010. }())
  8011. HLP.toByteArray = function (data) {
  8012. var rv = []
  8013. , ary = data.split("")
  8014. , i = -1
  8015. , iz = ary.length
  8016. , remain = iz % 8
  8017. while (remain--) {
  8018. ++i
  8019. rv[i] = _bin2num[ary[i]]
  8020. }
  8021. remain = iz >> 3
  8022. while (remain--) {
  8023. rv.push(_bin2num[ary[++i]], _bin2num[ary[++i]],
  8024. _bin2num[ary[++i]], _bin2num[ary[++i]],
  8025. _bin2num[ary[++i]], _bin2num[ary[++i]],
  8026. _bin2num[ary[++i]], _bin2num[ary[++i]])
  8027. }
  8028. return rv
  8029. }
  8030. }).call(this)
  8031. ;(function () {
  8032. var root = this
  8033. var CryptoJS, BigInt, Worker, WWPath, HLP
  8034. if (typeof module !== 'undefined' && module.exports) {
  8035. module.exports = DSA
  8036. CryptoJS = require('../vendor/crypto.js')
  8037. BigInt = require('../vendor/bigint.js')
  8038. WWPath = require('path').join(__dirname, '/dsa-webworker.js')
  8039. HLP = require('./helpers.js')
  8040. } else {
  8041. // copy over and expose internals
  8042. Object.keys(root.DSA).forEach(function (k) {
  8043. DSA[k] = root.DSA[k]
  8044. })
  8045. root.DSA = DSA
  8046. CryptoJS = root.CryptoJS
  8047. BigInt = root.BigInt
  8048. Worker = root.Worker
  8049. WWPath = 'dsa-webworker.js'
  8050. HLP = DSA.HLP
  8051. }
  8052. var ZERO = BigInt.str2bigInt('0', 10)
  8053. , ONE = BigInt.str2bigInt('1', 10)
  8054. , TWO = BigInt.str2bigInt('2', 10)
  8055. , KEY_TYPE = '\x00\x00'
  8056. var DEBUG = false
  8057. function timer() {
  8058. var start = (new Date()).getTime()
  8059. return function (s) {
  8060. if (!DEBUG || typeof console === 'undefined') return
  8061. var t = (new Date()).getTime()
  8062. console.log(s + ': ' + (t - start))
  8063. start = t
  8064. }
  8065. }
  8066. function makeRandom(min, max) {
  8067. var c = BigInt.randBigInt(BigInt.bitSize(max))
  8068. if (!HLP.between(c, min, max)) return makeRandom(min, max)
  8069. return c
  8070. }
  8071. // altered BigInt.randProbPrime()
  8072. // n rounds of Miller Rabin (after trial division with small primes)
  8073. var rpprb = []
  8074. function isProbPrime(k, n) {
  8075. var i, B = 30000, l = BigInt.bitSize(k)
  8076. var primes = BigInt.primes
  8077. if (primes.length === 0)
  8078. primes = BigInt.findPrimes(B)
  8079. if (rpprb.length != k.length)
  8080. rpprb = BigInt.dup(k)
  8081. // check ans for divisibility by small primes up to B
  8082. for (i = 0; (i < primes.length) && (primes[i] <= B); i++)
  8083. if (BigInt.modInt(k, primes[i]) === 0 && !BigInt.equalsInt(k, primes[i]))
  8084. return 0
  8085. // do n rounds of Miller Rabin, with random bases less than k
  8086. for (i = 0; i < n; i++) {
  8087. BigInt.randBigInt_(rpprb, l, 0)
  8088. while(!BigInt.greater(k, rpprb)) // pick a random rpprb that's < k
  8089. BigInt.randBigInt_(rpprb, l, 0)
  8090. if (!BigInt.millerRabin(k, rpprb))
  8091. return 0
  8092. }
  8093. return 1
  8094. }
  8095. var bit_lengths = {
  8096. '1024': { N: 160, repeat: 40 } // 40x should give 2^-80 confidence
  8097. , '2048': { N: 224, repeat: 56 }
  8098. }
  8099. var primes = {}
  8100. // follows go lang http://golang.org/src/pkg/crypto/dsa/dsa.go
  8101. // fips version was removed in 0c99af0df3e7
  8102. function generatePrimes(bit_length) {
  8103. var t = timer() // for debugging
  8104. // number of MR tests to perform
  8105. var repeat = bit_lengths[bit_length].repeat
  8106. var N = bit_lengths[bit_length].N
  8107. var LM1 = BigInt.twoToThe(bit_length - 1)
  8108. var bl4 = 4 * bit_length
  8109. var brk = false
  8110. var q, p, rem, counter
  8111. for (;;) {
  8112. q = BigInt.randBigInt(N, 1)
  8113. q[0] |= 1
  8114. if (!isProbPrime(q, repeat)) continue
  8115. t('q')
  8116. for (counter = 0; counter < bl4; counter++) {
  8117. p = BigInt.randBigInt(bit_length, 1)
  8118. p[0] |= 1
  8119. rem = BigInt.mod(p, q)
  8120. rem = BigInt.sub(rem, ONE)
  8121. p = BigInt.sub(p, rem)
  8122. if (BigInt.greater(LM1, p)) continue
  8123. if (!isProbPrime(p, repeat)) continue
  8124. t('p')
  8125. primes[bit_length] = { p: p, q: q }
  8126. brk = true
  8127. break
  8128. }
  8129. if (brk) break
  8130. }
  8131. var h = BigInt.dup(TWO)
  8132. var pm1 = BigInt.sub(p, ONE)
  8133. var e = BigInt.multMod(pm1, BigInt.inverseMod(q, p), p)
  8134. var g
  8135. for (;;) {
  8136. g = BigInt.powMod(h, e, p)
  8137. if (BigInt.equals(g, ONE)) {
  8138. h = BigInt.add(h, ONE)
  8139. continue
  8140. }
  8141. primes[bit_length].g = g
  8142. t('g')
  8143. return
  8144. }
  8145. throw new Error('Unreachable!')
  8146. }
  8147. function DSA(obj, opts) {
  8148. if (!(this instanceof DSA)) return new DSA(obj, opts)
  8149. // options
  8150. opts = opts || {}
  8151. // inherit
  8152. if (obj) {
  8153. var self = this
  8154. ;['p', 'q', 'g', 'y', 'x'].forEach(function (prop) {
  8155. self[prop] = obj[prop]
  8156. })
  8157. this.type = obj.type || KEY_TYPE
  8158. return
  8159. }
  8160. // default to 1024
  8161. var bit_length = parseInt(opts.bit_length ? opts.bit_length : 1024, 10)
  8162. if (!bit_lengths[bit_length])
  8163. throw new Error('Unsupported bit length.')
  8164. // set primes
  8165. if (!primes[bit_length])
  8166. generatePrimes(bit_length)
  8167. this.p = primes[bit_length].p
  8168. this.q = primes[bit_length].q
  8169. this.g = primes[bit_length].g
  8170. // key type
  8171. this.type = KEY_TYPE
  8172. // private key
  8173. this.x = makeRandom(ZERO, this.q)
  8174. // public keys (p, q, g, y)
  8175. this.y = BigInt.powMod(this.g, this.x, this.p)
  8176. // nocache?
  8177. if (opts.nocache) primes[bit_length] = null
  8178. }
  8179. DSA.prototype = {
  8180. constructor: DSA,
  8181. packPublic: function () {
  8182. var str = this.type
  8183. str += HLP.packMPI(this.p)
  8184. str += HLP.packMPI(this.q)
  8185. str += HLP.packMPI(this.g)
  8186. str += HLP.packMPI(this.y)
  8187. return str
  8188. },
  8189. packPrivate: function () {
  8190. var str = this.packPublic() + HLP.packMPI(this.x)
  8191. str = CryptoJS.enc.Latin1.parse(str)
  8192. return str.toString(CryptoJS.enc.Base64)
  8193. },
  8194. // http://www.imperialviolet.org/2013/06/15/suddendeathentropy.html
  8195. generateNonce: function (m) {
  8196. var priv = BigInt.bigInt2bits(BigInt.trim(this.x, 0))
  8197. var rand = BigInt.bigInt2bits(BigInt.randBigInt(256))
  8198. var sha256 = CryptoJS.algo.SHA256.create()
  8199. sha256.update(CryptoJS.enc.Latin1.parse(priv))
  8200. sha256.update(m)
  8201. sha256.update(CryptoJS.enc.Latin1.parse(rand))
  8202. var hash = sha256.finalize()
  8203. hash = HLP.bits2bigInt(hash.toString(CryptoJS.enc.Latin1))
  8204. BigInt.rightShift_(hash, 256 - BigInt.bitSize(this.q))
  8205. return HLP.between(hash, ZERO, this.q) ? hash : this.generateNonce(m)
  8206. },
  8207. sign: function (m) {
  8208. m = CryptoJS.enc.Latin1.parse(m)
  8209. var b = BigInt.str2bigInt(m.toString(CryptoJS.enc.Hex), 16)
  8210. var k, r = ZERO, s = ZERO
  8211. while (BigInt.isZero(s) || BigInt.isZero(r)) {
  8212. k = this.generateNonce(m)
  8213. r = BigInt.mod(BigInt.powMod(this.g, k, this.p), this.q)
  8214. if (BigInt.isZero(r)) continue
  8215. s = BigInt.inverseMod(k, this.q)
  8216. s = BigInt.mult(s, BigInt.add(b, BigInt.mult(this.x, r)))
  8217. s = BigInt.mod(s, this.q)
  8218. }
  8219. return [r, s]
  8220. },
  8221. fingerprint: function () {
  8222. var pk = this.packPublic()
  8223. if (this.type === KEY_TYPE) pk = pk.substring(2)
  8224. pk = CryptoJS.enc.Latin1.parse(pk)
  8225. return CryptoJS.SHA1(pk).toString(CryptoJS.enc.Hex)
  8226. }
  8227. }
  8228. DSA.parsePublic = function (str, priv) {
  8229. var fields = ['SHORT', 'MPI', 'MPI', 'MPI', 'MPI']
  8230. if (priv) fields.push('MPI')
  8231. str = HLP.splitype(fields, str)
  8232. var obj = {
  8233. type: str[0]
  8234. , p: HLP.readMPI(str[1])
  8235. , q: HLP.readMPI(str[2])
  8236. , g: HLP.readMPI(str[3])
  8237. , y: HLP.readMPI(str[4])
  8238. }
  8239. if (priv) obj.x = HLP.readMPI(str[5])
  8240. return new DSA(obj)
  8241. }
  8242. function tokenizeStr(str) {
  8243. var start, end
  8244. start = str.indexOf("(")
  8245. end = str.lastIndexOf(")")
  8246. if (start < 0 || end < 0)
  8247. throw new Error("Malformed S-Expression")
  8248. str = str.substring(start + 1, end)
  8249. var splt = str.search(/\s/)
  8250. var obj = {
  8251. type: str.substring(0, splt)
  8252. , val: []
  8253. }
  8254. str = str.substring(splt + 1, end)
  8255. start = str.indexOf("(")
  8256. if (start < 0) obj.val.push(str)
  8257. else {
  8258. var i, len, ss, es
  8259. while (start > -1) {
  8260. i = start + 1
  8261. len = str.length
  8262. for (ss = 1, es = 0; i < len && es < ss; i++) {
  8263. if (str[i] === "(") ss++
  8264. if (str[i] === ")") es++
  8265. }
  8266. obj.val.push(tokenizeStr(str.substring(start, ++i)))
  8267. str = str.substring(++i)
  8268. start = str.indexOf("(")
  8269. }
  8270. }
  8271. return obj
  8272. }
  8273. function parseLibotr(obj) {
  8274. if (!obj.type) throw new Error("Parse error.")
  8275. var o, val
  8276. if (obj.type === "privkeys") {
  8277. o = []
  8278. obj.val.forEach(function (i) {
  8279. o.push(parseLibotr(i))
  8280. })
  8281. return o
  8282. }
  8283. o = {}
  8284. obj.val.forEach(function (i) {
  8285. val = i.val[0]
  8286. if (typeof val === "string") {
  8287. if (val.indexOf("#") === 0) {
  8288. val = val.substring(1, val.lastIndexOf("#"))
  8289. val = BigInt.str2bigInt(val, 16)
  8290. }
  8291. } else {
  8292. val = parseLibotr(i)
  8293. }
  8294. o[i.type] = val
  8295. })
  8296. return o
  8297. }
  8298. DSA.parsePrivate = function (str, libotr) {
  8299. if (!libotr) {
  8300. str = CryptoJS.enc.Base64.parse(str)
  8301. str = str.toString(CryptoJS.enc.Latin1)
  8302. return DSA.parsePublic(str, true)
  8303. }
  8304. // only returning the first key found
  8305. return parseLibotr(tokenizeStr(str))[0]["private-key"].dsa
  8306. }
  8307. DSA.verify = function (key, m, r, s) {
  8308. if (!HLP.between(r, ZERO, key.q) || !HLP.between(s, ZERO, key.q))
  8309. return false
  8310. var hm = CryptoJS.enc.Latin1.parse(m) // CryptoJS.SHA1(m)
  8311. hm = BigInt.str2bigInt(hm.toString(CryptoJS.enc.Hex), 16)
  8312. var w = BigInt.inverseMod(s, key.q)
  8313. var u1 = BigInt.multMod(hm, w, key.q)
  8314. var u2 = BigInt.multMod(r, w, key.q)
  8315. u1 = BigInt.powMod(key.g, u1, key.p)
  8316. u2 = BigInt.powMod(key.y, u2, key.p)
  8317. var v = BigInt.mod(BigInt.multMod(u1, u2, key.p), key.q)
  8318. return BigInt.equals(v, r)
  8319. }
  8320. DSA.createInWebWorker = function (options, cb) {
  8321. var opts = {
  8322. path: WWPath
  8323. , seed: BigInt.getSeed
  8324. }
  8325. if (options && typeof options === 'object')
  8326. Object.keys(options).forEach(function (k) {
  8327. opts[k] = options[k]
  8328. })
  8329. // load optional dep. in node
  8330. if (typeof module !== 'undefined' && module.exports)
  8331. Worker = require('webworker-threads').Worker
  8332. var worker = new Worker(opts.path)
  8333. worker.onmessage = function (e) {
  8334. var data = e.data
  8335. switch (data.type) {
  8336. case "debug":
  8337. if (!DEBUG || typeof console === 'undefined') return
  8338. console.log(data.val)
  8339. break;
  8340. case "data":
  8341. worker.terminate()
  8342. cb(DSA.parsePrivate(data.val))
  8343. break;
  8344. default:
  8345. throw new Error("Unrecognized type.")
  8346. }
  8347. }
  8348. worker.postMessage({
  8349. seed: opts.seed()
  8350. , imports: opts.imports
  8351. , debug: DEBUG
  8352. })
  8353. }
  8354. }).call(this)
  8355. ;(function () {
  8356. var root = this
  8357. var Parse = {}, CryptoJS, CONST, HLP
  8358. if (typeof module !== 'undefined' && module.exports) {
  8359. module.exports = Parse
  8360. CryptoJS = require('../vendor/crypto.js')
  8361. CONST = require('./const.js')
  8362. HLP = require('./helpers.js')
  8363. } else {
  8364. root.OTR.Parse = Parse
  8365. CryptoJS = root.CryptoJS
  8366. CONST = root.OTR.CONST
  8367. HLP = root.OTR.HLP
  8368. }
  8369. // whitespace tags
  8370. var tags = {}
  8371. tags[CONST.WHITESPACE_TAG_V2] = CONST.OTR_VERSION_2
  8372. tags[CONST.WHITESPACE_TAG_V3] = CONST.OTR_VERSION_3
  8373. Parse.parseMsg = function (otr, msg) {
  8374. var ver = []
  8375. // is this otr?
  8376. var start = msg.indexOf(CONST.OTR_TAG)
  8377. if (!~start) {
  8378. // restart fragments
  8379. this.initFragment(otr)
  8380. // whitespace tags
  8381. ind = msg.indexOf(CONST.WHITESPACE_TAG)
  8382. if (~ind) {
  8383. msg = msg.split('')
  8384. msg.splice(ind, 16)
  8385. var tag, len = msg.length
  8386. for (; ind < len;) {
  8387. tag = msg.slice(ind, ind + 8).join('')
  8388. if (Object.hasOwnProperty.call(tags, tag)) {
  8389. msg.splice(ind, 8)
  8390. ver.push(tags[tag])
  8391. continue
  8392. }
  8393. ind += 8
  8394. }
  8395. msg = msg.join('')
  8396. }
  8397. return { msg: msg, ver: ver }
  8398. }
  8399. var ind = start + CONST.OTR_TAG.length
  8400. var com = msg[ind]
  8401. // message fragment
  8402. if (com === ',' || com === '|') {
  8403. return this.msgFragment(otr, msg.substring(ind + 1), (com === '|'))
  8404. }
  8405. this.initFragment(otr)
  8406. // query message
  8407. if (~['?', 'v'].indexOf(com)) {
  8408. // version 1
  8409. if (msg[ind] === '?') {
  8410. ver.push(CONST.OTR_VERSION_1)
  8411. ind += 1
  8412. }
  8413. // other versions
  8414. var vers = {
  8415. '2': CONST.OTR_VERSION_2
  8416. , '3': CONST.OTR_VERSION_3
  8417. }
  8418. var qs = msg.substring(ind + 1)
  8419. var qi = qs.indexOf('?')
  8420. if (qi >= 1) {
  8421. qs = qs.substring(0, qi).split('')
  8422. if (msg[ind] === 'v') {
  8423. qs.forEach(function (q) {
  8424. if (Object.hasOwnProperty.call(vers, q)) ver.push(vers[q])
  8425. })
  8426. }
  8427. }
  8428. return { cls: 'query', ver: ver }
  8429. }
  8430. // otr message
  8431. if (com === ':') {
  8432. ind += 1
  8433. var info = msg.substring(ind, ind + 4)
  8434. if (info.length < 4) return { msg: msg }
  8435. info = CryptoJS.enc.Base64.parse(info).toString(CryptoJS.enc.Latin1)
  8436. var version = info.substring(0, 2)
  8437. var type = info.substring(2)
  8438. // supporting otr versions 2 and 3
  8439. if (!otr['ALLOW_V' + HLP.unpackSHORT(version)]) return { msg: msg }
  8440. ind += 4
  8441. var end = msg.substring(ind).indexOf('.')
  8442. if (!~end) return { msg: msg }
  8443. msg = CryptoJS.enc.Base64.parse(msg.substring(ind, ind + end))
  8444. msg = CryptoJS.enc.Latin1.stringify(msg)
  8445. // instance tags
  8446. var instance_tags
  8447. if (version === CONST.OTR_VERSION_3) {
  8448. instance_tags = msg.substring(0, 8)
  8449. msg = msg.substring(8)
  8450. }
  8451. var cls
  8452. if (~['\x02', '\x0a', '\x11', '\x12'].indexOf(type)) {
  8453. cls = 'ake'
  8454. } else if (type === '\x03') {
  8455. cls = 'data'
  8456. }
  8457. return {
  8458. version: version
  8459. , type: type
  8460. , msg: msg
  8461. , cls: cls
  8462. , instance_tags: instance_tags
  8463. }
  8464. }
  8465. // error message
  8466. if (msg.substring(ind, ind + 7) === ' Error:') {
  8467. if (otr.ERROR_START_AKE) {
  8468. otr.sendQueryMsg()
  8469. }
  8470. return { msg: msg.substring(ind + 7), cls: 'error' }
  8471. }
  8472. return { msg: msg }
  8473. }
  8474. Parse.initFragment = function (otr) {
  8475. otr.fragment = { s: '', j: 0, k: 0 }
  8476. }
  8477. Parse.msgFragment = function (otr, msg, v3) {
  8478. msg = msg.split(',')
  8479. // instance tags
  8480. if (v3) {
  8481. var its = msg.shift().split('|')
  8482. var their_it = HLP.packINT(parseInt(its[0], 16))
  8483. var our_it = HLP.packINT(parseInt(its[1], 16))
  8484. if (otr.checkInstanceTags(their_it + our_it)) return // ignore
  8485. }
  8486. if (msg.length < 4 ||
  8487. isNaN(parseInt(msg[0], 10)) ||
  8488. isNaN(parseInt(msg[1], 10))
  8489. ) return
  8490. var k = parseInt(msg[0], 10)
  8491. var n = parseInt(msg[1], 10)
  8492. msg = msg[2]
  8493. if (n < k || n === 0 || k === 0) {
  8494. this.initFragment(otr)
  8495. return
  8496. }
  8497. if (k === 1) {
  8498. this.initFragment(otr)
  8499. otr.fragment = { k: 1, n: n, s: msg }
  8500. } else if (n === otr.fragment.n && k === (otr.fragment.k + 1)) {
  8501. otr.fragment.s += msg
  8502. otr.fragment.k += 1
  8503. } else {
  8504. this.initFragment(otr)
  8505. }
  8506. if (n === k) {
  8507. msg = otr.fragment.s
  8508. this.initFragment(otr)
  8509. return this.parseMsg(otr, msg)
  8510. }
  8511. return
  8512. }
  8513. }).call(this)
  8514. ;(function () {
  8515. var root = this
  8516. var CryptoJS, BigInt, CONST, HLP, DSA
  8517. if (typeof module !== 'undefined' && module.exports) {
  8518. module.exports = AKE
  8519. CryptoJS = require('../vendor/crypto.js')
  8520. BigInt = require('../vendor/bigint.js')
  8521. CONST = require('./const.js')
  8522. HLP = require('./helpers.js')
  8523. DSA = require('./dsa.js')
  8524. } else {
  8525. root.OTR.AKE = AKE
  8526. CryptoJS = root.CryptoJS
  8527. BigInt = root.BigInt
  8528. CONST = root.OTR.CONST
  8529. HLP = root.OTR.HLP
  8530. DSA = root.DSA
  8531. }
  8532. // diffie-hellman modulus
  8533. // see group 5, RFC 3526
  8534. var N = BigInt.str2bigInt(CONST.N, 16)
  8535. var N_MINUS_2 = BigInt.sub(N, BigInt.str2bigInt('2', 10))
  8536. function hMac(gx, gy, pk, kid, m) {
  8537. var pass = CryptoJS.enc.Latin1.parse(m)
  8538. var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, pass)
  8539. hmac.update(CryptoJS.enc.Latin1.parse(HLP.packMPI(gx)))
  8540. hmac.update(CryptoJS.enc.Latin1.parse(HLP.packMPI(gy)))
  8541. hmac.update(CryptoJS.enc.Latin1.parse(pk))
  8542. hmac.update(CryptoJS.enc.Latin1.parse(kid))
  8543. return (hmac.finalize()).toString(CryptoJS.enc.Latin1)
  8544. }
  8545. // AKE constructor
  8546. function AKE(otr) {
  8547. if (!(this instanceof AKE)) return new AKE(otr)
  8548. // otr instance
  8549. this.otr = otr
  8550. // our keys
  8551. this.our_dh = otr.our_old_dh
  8552. this.our_keyid = otr.our_keyid - 1
  8553. // their keys
  8554. this.their_y = null
  8555. this.their_keyid = null
  8556. this.their_priv_pk = null
  8557. // state
  8558. this.ssid = null
  8559. this.transmittedRS = false
  8560. this.r = null
  8561. // bind methods
  8562. var self = this
  8563. ;['sendMsg'].forEach(function (meth) {
  8564. self[meth] = self[meth].bind(self)
  8565. })
  8566. }
  8567. AKE.prototype = {
  8568. constructor: AKE,
  8569. createKeys: function(g) {
  8570. var s = BigInt.powMod(g, this.our_dh.privateKey, N)
  8571. var secbytes = HLP.packMPI(s)
  8572. this.ssid = HLP.mask(HLP.h2('\x00', secbytes), 0, 64) // first 64-bits
  8573. var tmp = HLP.h2('\x01', secbytes)
  8574. this.c = HLP.mask(tmp, 0, 128) // first 128-bits
  8575. this.c_prime = HLP.mask(tmp, 128, 128) // second 128-bits
  8576. this.m1 = HLP.h2('\x02', secbytes)
  8577. this.m2 = HLP.h2('\x03', secbytes)
  8578. this.m1_prime = HLP.h2('\x04', secbytes)
  8579. this.m2_prime = HLP.h2('\x05', secbytes)
  8580. },
  8581. verifySignMac: function (mac, aesctr, m2, c, their_y, our_dh_pk, m1, ctr) {
  8582. // verify mac
  8583. var vmac = HLP.makeMac(aesctr, m2)
  8584. if (!HLP.compare(mac, vmac))
  8585. return ['MACs do not match.']
  8586. // decrypt x
  8587. var x = HLP.decryptAes(aesctr.substring(4), c, ctr)
  8588. x = HLP.splitype(['PUBKEY', 'INT', 'SIG'], x.toString(CryptoJS.enc.Latin1))
  8589. var m = hMac(their_y, our_dh_pk, x[0], x[1], m1)
  8590. var pub = DSA.parsePublic(x[0])
  8591. var r = HLP.bits2bigInt(x[2].substring(0, 20))
  8592. var s = HLP.bits2bigInt(x[2].substring(20))
  8593. // verify sign m
  8594. if (!DSA.verify(pub, m, r, s)) return ['Cannot verify signature of m.']
  8595. return [null, HLP.readLen(x[1]), pub]
  8596. },
  8597. makeM: function (their_y, m1, c, m2) {
  8598. var pk = this.otr.priv.packPublic()
  8599. var kid = HLP.packINT(this.our_keyid)
  8600. var m = hMac(this.our_dh.publicKey, their_y, pk, kid, m1)
  8601. m = this.otr.priv.sign(m)
  8602. var msg = pk + kid
  8603. msg += BigInt.bigInt2bits(m[0], 20) // pad to 20 bytes
  8604. msg += BigInt.bigInt2bits(m[1], 20)
  8605. msg = CryptoJS.enc.Latin1.parse(msg)
  8606. var aesctr = HLP.packData(HLP.encryptAes(msg, c, HLP.packCtr(0)))
  8607. var mac = HLP.makeMac(aesctr, m2)
  8608. return aesctr + mac
  8609. },
  8610. akeSuccess: function (version) {
  8611. HLP.debug.call(this.otr, 'success')
  8612. if (BigInt.equals(this.their_y, this.our_dh.publicKey))
  8613. return this.otr.error('equal keys - we have a problem.', true)
  8614. this.otr.our_old_dh = this.our_dh
  8615. this.otr.their_priv_pk = this.their_priv_pk
  8616. if (!(
  8617. (this.their_keyid === this.otr.their_keyid &&
  8618. BigInt.equals(this.their_y, this.otr.their_y)) ||
  8619. (this.their_keyid === (this.otr.their_keyid - 1) &&
  8620. BigInt.equals(this.their_y, this.otr.their_old_y))
  8621. )) {
  8622. this.otr.their_y = this.their_y
  8623. this.otr.their_old_y = null
  8624. this.otr.their_keyid = this.their_keyid
  8625. // rotate keys
  8626. this.otr.sessKeys[0] = [ new this.otr.DHSession(
  8627. this.otr.our_dh
  8628. , this.otr.their_y
  8629. ), null ]
  8630. this.otr.sessKeys[1] = [ new this.otr.DHSession(
  8631. this.otr.our_old_dh
  8632. , this.otr.their_y
  8633. ), null ]
  8634. }
  8635. // ake info
  8636. this.otr.ssid = this.ssid
  8637. this.otr.transmittedRS = this.transmittedRS
  8638. this.otr_version = version
  8639. // go encrypted
  8640. this.otr.authstate = CONST.AUTHSTATE_NONE
  8641. this.otr.msgstate = CONST.MSGSTATE_ENCRYPTED
  8642. // null out values
  8643. this.r = null
  8644. this.myhashed = null
  8645. this.dhcommit = null
  8646. this.encrypted = null
  8647. this.hashed = null
  8648. this.otr.trigger('status', [CONST.STATUS_AKE_SUCCESS])
  8649. // send stored msgs
  8650. this.otr.sendStored()
  8651. },
  8652. handleAKE: function (msg) {
  8653. var send, vsm, type
  8654. var version = msg.version
  8655. switch (msg.type) {
  8656. case '\x02':
  8657. HLP.debug.call(this.otr, 'd-h key message')
  8658. msg = HLP.splitype(['DATA', 'DATA'], msg.msg)
  8659. if (this.otr.authstate === CONST.AUTHSTATE_AWAITING_DHKEY) {
  8660. var ourHash = HLP.readMPI(this.myhashed)
  8661. var theirHash = HLP.readMPI(msg[1])
  8662. if (BigInt.greater(ourHash, theirHash)) {
  8663. type = '\x02'
  8664. send = this.dhcommit
  8665. break // ignore
  8666. } else {
  8667. // forget
  8668. this.our_dh = this.otr.dh()
  8669. this.otr.authstate = CONST.AUTHSTATE_NONE
  8670. this.r = null
  8671. this.myhashed = null
  8672. }
  8673. } else if (
  8674. this.otr.authstate === CONST.AUTHSTATE_AWAITING_SIG
  8675. ) this.our_dh = this.otr.dh()
  8676. this.otr.authstate = CONST.AUTHSTATE_AWAITING_REVEALSIG
  8677. this.encrypted = msg[0].substring(4)
  8678. this.hashed = msg[1].substring(4)
  8679. type = '\x0a'
  8680. send = HLP.packMPI(this.our_dh.publicKey)
  8681. break
  8682. case '\x0a':
  8683. HLP.debug.call(this.otr, 'reveal signature message')
  8684. msg = HLP.splitype(['MPI'], msg.msg)
  8685. if (this.otr.authstate !== CONST.AUTHSTATE_AWAITING_DHKEY) {
  8686. if (this.otr.authstate === CONST.AUTHSTATE_AWAITING_SIG) {
  8687. if (!BigInt.equals(this.their_y, HLP.readMPI(msg[0]))) return
  8688. } else {
  8689. return // ignore
  8690. }
  8691. }
  8692. this.otr.authstate = CONST.AUTHSTATE_AWAITING_SIG
  8693. this.their_y = HLP.readMPI(msg[0])
  8694. // verify gy is legal 2 <= gy <= N-2
  8695. if (!HLP.checkGroup(this.their_y, N_MINUS_2))
  8696. return this.otr.error('Illegal g^y.', true)
  8697. this.createKeys(this.their_y)
  8698. type = '\x11'
  8699. send = HLP.packMPI(this.r)
  8700. send += this.makeM(this.their_y, this.m1, this.c, this.m2)
  8701. this.m1 = null
  8702. this.m2 = null
  8703. this.c = null
  8704. break
  8705. case '\x11':
  8706. HLP.debug.call(this.otr, 'signature message')
  8707. if (this.otr.authstate !== CONST.AUTHSTATE_AWAITING_REVEALSIG)
  8708. return // ignore
  8709. msg = HLP.splitype(['DATA', 'DATA', 'MAC'], msg.msg)
  8710. this.r = HLP.readMPI(msg[0])
  8711. // decrypt their_y
  8712. var key = CryptoJS.enc.Hex.parse(BigInt.bigInt2str(this.r, 16))
  8713. key = CryptoJS.enc.Latin1.stringify(key)
  8714. var gxmpi = HLP.decryptAes(this.encrypted, key, HLP.packCtr(0))
  8715. gxmpi = gxmpi.toString(CryptoJS.enc.Latin1)
  8716. this.their_y = HLP.readMPI(gxmpi)
  8717. // verify hash
  8718. var hash = CryptoJS.SHA256(CryptoJS.enc.Latin1.parse(gxmpi))
  8719. if (!HLP.compare(this.hashed, hash.toString(CryptoJS.enc.Latin1)))
  8720. return this.otr.error('Hashed g^x does not match.', true)
  8721. // verify gx is legal 2 <= g^x <= N-2
  8722. if (!HLP.checkGroup(this.their_y, N_MINUS_2))
  8723. return this.otr.error('Illegal g^x.', true)
  8724. this.createKeys(this.their_y)
  8725. vsm = this.verifySignMac(
  8726. msg[2]
  8727. , msg[1]
  8728. , this.m2
  8729. , this.c
  8730. , this.their_y
  8731. , this.our_dh.publicKey
  8732. , this.m1
  8733. , HLP.packCtr(0)
  8734. )
  8735. if (vsm[0]) return this.otr.error(vsm[0], true)
  8736. // store their key
  8737. this.their_keyid = vsm[1]
  8738. this.their_priv_pk = vsm[2]
  8739. send = this.makeM(
  8740. this.their_y
  8741. , this.m1_prime
  8742. , this.c_prime
  8743. , this.m2_prime
  8744. )
  8745. this.m1 = null
  8746. this.m2 = null
  8747. this.m1_prime = null
  8748. this.m2_prime = null
  8749. this.c = null
  8750. this.c_prime = null
  8751. this.sendMsg(version, '\x12', send)
  8752. this.akeSuccess(version)
  8753. return
  8754. case '\x12':
  8755. HLP.debug.call(this.otr, 'data message')
  8756. if (this.otr.authstate !== CONST.AUTHSTATE_AWAITING_SIG)
  8757. return // ignore
  8758. msg = HLP.splitype(['DATA', 'MAC'], msg.msg)
  8759. vsm = this.verifySignMac(
  8760. msg[1]
  8761. , msg[0]
  8762. , this.m2_prime
  8763. , this.c_prime
  8764. , this.their_y
  8765. , this.our_dh.publicKey
  8766. , this.m1_prime
  8767. , HLP.packCtr(0)
  8768. )
  8769. if (vsm[0]) return this.otr.error(vsm[0], true)
  8770. // store their key
  8771. this.their_keyid = vsm[1]
  8772. this.their_priv_pk = vsm[2]
  8773. this.m1_prime = null
  8774. this.m2_prime = null
  8775. this.c_prime = null
  8776. this.transmittedRS = true
  8777. this.akeSuccess(version)
  8778. return
  8779. default:
  8780. return // ignore
  8781. }
  8782. this.sendMsg(version, type, send)
  8783. },
  8784. sendMsg: function (version, type, msg) {
  8785. var send = version + type
  8786. var v3 = (version === CONST.OTR_VERSION_3)
  8787. // instance tags for v3
  8788. if (v3) {
  8789. HLP.debug.call(this.otr, 'instance tags')
  8790. send += this.otr.our_instance_tag
  8791. send += this.otr.their_instance_tag
  8792. }
  8793. send += msg
  8794. // fragment message if necessary
  8795. send = HLP.wrapMsg(
  8796. send
  8797. , this.otr.fragment_size
  8798. , v3
  8799. , this.otr.our_instance_tag
  8800. , this.otr.their_instance_tag
  8801. )
  8802. if (send[0]) return this.otr.error(send[0])
  8803. this.otr.io(send[1])
  8804. },
  8805. initiateAKE: function (version) {
  8806. HLP.debug.call(this.otr, 'd-h commit message')
  8807. this.otr.trigger('status', [CONST.STATUS_AKE_INIT])
  8808. this.otr.authstate = CONST.AUTHSTATE_AWAITING_DHKEY
  8809. var gxmpi = HLP.packMPI(this.our_dh.publicKey)
  8810. gxmpi = CryptoJS.enc.Latin1.parse(gxmpi)
  8811. this.r = BigInt.randBigInt(128)
  8812. var key = CryptoJS.enc.Hex.parse(BigInt.bigInt2str(this.r, 16))
  8813. key = CryptoJS.enc.Latin1.stringify(key)
  8814. this.myhashed = CryptoJS.SHA256(gxmpi)
  8815. this.myhashed = HLP.packData(this.myhashed.toString(CryptoJS.enc.Latin1))
  8816. this.dhcommit = HLP.packData(HLP.encryptAes(gxmpi, key, HLP.packCtr(0)))
  8817. this.dhcommit += this.myhashed
  8818. this.sendMsg(version, '\x02', this.dhcommit)
  8819. }
  8820. }
  8821. }).call(this)
  8822. ;(function () {
  8823. var root = this
  8824. var CryptoJS, BigInt, EventEmitter, CONST, HLP
  8825. if (typeof module !== 'undefined' && module.exports) {
  8826. module.exports = SM
  8827. CryptoJS = require('../vendor/crypto.js')
  8828. BigInt = require('../vendor/bigint.js')
  8829. EventEmitter = require('../vendor/eventemitter.js')
  8830. CONST = require('./const.js')
  8831. HLP = require('./helpers.js')
  8832. } else {
  8833. root.OTR.SM = SM
  8834. CryptoJS = root.CryptoJS
  8835. BigInt = root.BigInt
  8836. EventEmitter = root.EventEmitter
  8837. CONST = root.OTR.CONST
  8838. HLP = root.OTR.HLP
  8839. }
  8840. // diffie-hellman modulus and generator
  8841. // see group 5, RFC 3526
  8842. var G = BigInt.str2bigInt(CONST.G, 10)
  8843. var N = BigInt.str2bigInt(CONST.N, 16)
  8844. var N_MINUS_2 = BigInt.sub(N, BigInt.str2bigInt('2', 10))
  8845. // to calculate D's for zero-knowledge proofs
  8846. var Q = BigInt.sub(N, BigInt.str2bigInt('1', 10))
  8847. BigInt.divInt_(Q, 2) // meh
  8848. function SM(reqs) {
  8849. if (!(this instanceof SM)) return new SM(reqs)
  8850. this.version = 1
  8851. this.our_fp = reqs.our_fp
  8852. this.their_fp = reqs.their_fp
  8853. this.ssid = reqs.ssid
  8854. this.debug = !!reqs.debug
  8855. // initial state
  8856. this.init()
  8857. }
  8858. // inherit from EE
  8859. HLP.extend(SM, EventEmitter)
  8860. // set the initial values
  8861. // also used when aborting
  8862. SM.prototype.init = function () {
  8863. this.smpstate = CONST.SMPSTATE_EXPECT1
  8864. this.secret = null
  8865. }
  8866. SM.prototype.makeSecret = function (our, secret) {
  8867. var sha256 = CryptoJS.algo.SHA256.create()
  8868. sha256.update(CryptoJS.enc.Latin1.parse(HLP.packBytes(this.version, 1)))
  8869. sha256.update(CryptoJS.enc.Hex.parse(our ? this.our_fp : this.their_fp))
  8870. sha256.update(CryptoJS.enc.Hex.parse(our ? this.their_fp : this.our_fp))
  8871. sha256.update(CryptoJS.enc.Latin1.parse(this.ssid))
  8872. sha256.update(CryptoJS.enc.Latin1.parse(secret))
  8873. var hash = sha256.finalize()
  8874. this.secret = HLP.bits2bigInt(hash.toString(CryptoJS.enc.Latin1))
  8875. }
  8876. SM.prototype.makeG2s = function () {
  8877. this.a2 = HLP.randomExponent()
  8878. this.a3 = HLP.randomExponent()
  8879. this.g2a = BigInt.powMod(G, this.a2, N)
  8880. this.g3a = BigInt.powMod(G, this.a3, N)
  8881. if ( !HLP.checkGroup(this.g2a, N_MINUS_2) ||
  8882. !HLP.checkGroup(this.g3a, N_MINUS_2)
  8883. ) this.makeG2s()
  8884. }
  8885. SM.prototype.computeGs = function (g2a, g3a) {
  8886. this.g2 = BigInt.powMod(g2a, this.a2, N)
  8887. this.g3 = BigInt.powMod(g3a, this.a3, N)
  8888. }
  8889. SM.prototype.computePQ = function (r) {
  8890. this.p = BigInt.powMod(this.g3, r, N)
  8891. this.q = HLP.multPowMod(G, r, this.g2, this.secret, N)
  8892. }
  8893. SM.prototype.computeR = function () {
  8894. this.r = BigInt.powMod(this.QoQ, this.a3, N)
  8895. }
  8896. SM.prototype.computeRab = function (r) {
  8897. return BigInt.powMod(r, this.a3, N)
  8898. }
  8899. SM.prototype.computeC = function (v, r) {
  8900. return HLP.smpHash(v, BigInt.powMod(G, r, N))
  8901. }
  8902. SM.prototype.computeD = function (r, a, c) {
  8903. return BigInt.subMod(r, BigInt.multMod(a, c, Q), Q)
  8904. }
  8905. // the bulk of the work
  8906. SM.prototype.handleSM = function (msg) {
  8907. var send, r2, r3, r7, t1, t2, t3, t4, rab, tmp2, cR, d7, ms, trust
  8908. var expectStates = {
  8909. 2: CONST.SMPSTATE_EXPECT1
  8910. , 3: CONST.SMPSTATE_EXPECT2
  8911. , 4: CONST.SMPSTATE_EXPECT3
  8912. , 5: CONST.SMPSTATE_EXPECT4
  8913. , 7: CONST.SMPSTATE_EXPECT1
  8914. }
  8915. if (msg.type === 6) {
  8916. this.init()
  8917. this.trigger('abort')
  8918. return
  8919. }
  8920. // abort! there was an error
  8921. if (this.smpstate !== expectStates[msg.type])
  8922. return this.abort()
  8923. switch (this.smpstate) {
  8924. case CONST.SMPSTATE_EXPECT1:
  8925. HLP.debug.call(this, 'smp tlv 2')
  8926. // user specified question
  8927. var ind, question
  8928. if (msg.type === 7) {
  8929. ind = msg.msg.indexOf('\x00')
  8930. question = msg.msg.substring(0, ind)
  8931. msg.msg = msg.msg.substring(ind + 1)
  8932. }
  8933. // 0:g2a, 1:c2, 2:d2, 3:g3a, 4:c3, 5:d3
  8934. ms = HLP.readLen(msg.msg.substr(0, 4))
  8935. if (ms !== 6) return this.abort()
  8936. msg = HLP.unpackMPIs(6, msg.msg.substring(4))
  8937. if ( !HLP.checkGroup(msg[0], N_MINUS_2) ||
  8938. !HLP.checkGroup(msg[3], N_MINUS_2)
  8939. ) return this.abort()
  8940. // verify znp's
  8941. if (!HLP.ZKP(1, msg[1], HLP.multPowMod(G, msg[2], msg[0], msg[1], N)))
  8942. return this.abort()
  8943. if (!HLP.ZKP(2, msg[4], HLP.multPowMod(G, msg[5], msg[3], msg[4], N)))
  8944. return this.abort()
  8945. this.g3ao = msg[3] // save for later
  8946. this.makeG2s()
  8947. // zero-knowledge proof that the exponents
  8948. // associated with g2a & g3a are known
  8949. r2 = HLP.randomExponent()
  8950. r3 = HLP.randomExponent()
  8951. this.c2 = this.computeC(3, r2)
  8952. this.c3 = this.computeC(4, r3)
  8953. this.d2 = this.computeD(r2, this.a2, this.c2)
  8954. this.d3 = this.computeD(r3, this.a3, this.c3)
  8955. this.computeGs(msg[0], msg[3])
  8956. this.smpstate = CONST.SMPSTATE_EXPECT0
  8957. // assume utf8 question
  8958. question = CryptoJS.enc.Latin1
  8959. .parse(question)
  8960. .toString(CryptoJS.enc.Utf8)
  8961. // invoke question
  8962. this.trigger('question', [question])
  8963. return
  8964. case CONST.SMPSTATE_EXPECT2:
  8965. HLP.debug.call(this, 'smp tlv 3')
  8966. // 0:g2a, 1:c2, 2:d2, 3:g3a, 4:c3, 5:d3, 6:p, 7:q, 8:cP, 9:d5, 10:d6
  8967. ms = HLP.readLen(msg.msg.substr(0, 4))
  8968. if (ms !== 11) return this.abort()
  8969. msg = HLP.unpackMPIs(11, msg.msg.substring(4))
  8970. if ( !HLP.checkGroup(msg[0], N_MINUS_2) ||
  8971. !HLP.checkGroup(msg[3], N_MINUS_2) ||
  8972. !HLP.checkGroup(msg[6], N_MINUS_2) ||
  8973. !HLP.checkGroup(msg[7], N_MINUS_2)
  8974. ) return this.abort()
  8975. // verify znp of c3 / c3
  8976. if (!HLP.ZKP(3, msg[1], HLP.multPowMod(G, msg[2], msg[0], msg[1], N)))
  8977. return this.abort()
  8978. if (!HLP.ZKP(4, msg[4], HLP.multPowMod(G, msg[5], msg[3], msg[4], N)))
  8979. return this.abort()
  8980. this.g3ao = msg[3] // save for later
  8981. this.computeGs(msg[0], msg[3])
  8982. // verify znp of cP
  8983. t1 = HLP.multPowMod(this.g3, msg[9], msg[6], msg[8], N)
  8984. t2 = HLP.multPowMod(G, msg[9], this.g2, msg[10], N)
  8985. t2 = BigInt.multMod(t2, BigInt.powMod(msg[7], msg[8], N), N)
  8986. if (!HLP.ZKP(5, msg[8], t1, t2))
  8987. return this.abort()
  8988. var r4 = HLP.randomExponent()
  8989. this.computePQ(r4)
  8990. // zero-knowledge proof that P & Q
  8991. // were generated according to the protocol
  8992. var r5 = HLP.randomExponent()
  8993. var r6 = HLP.randomExponent()
  8994. var tmp = HLP.multPowMod(G, r5, this.g2, r6, N)
  8995. var cP = HLP.smpHash(6, BigInt.powMod(this.g3, r5, N), tmp)
  8996. var d5 = this.computeD(r5, r4, cP)
  8997. var d6 = this.computeD(r6, this.secret, cP)
  8998. // store these
  8999. this.QoQ = BigInt.divMod(this.q, msg[7], N)
  9000. this.PoP = BigInt.divMod(this.p, msg[6], N)
  9001. this.computeR()
  9002. // zero-knowledge proof that R
  9003. // was generated according to the protocol
  9004. r7 = HLP.randomExponent()
  9005. tmp2 = BigInt.powMod(this.QoQ, r7, N)
  9006. cR = HLP.smpHash(7, BigInt.powMod(G, r7, N), tmp2)
  9007. d7 = this.computeD(r7, this.a3, cR)
  9008. this.smpstate = CONST.SMPSTATE_EXPECT4
  9009. send = HLP.packINT(8) + HLP.packMPIs([
  9010. this.p
  9011. , this.q
  9012. , cP
  9013. , d5
  9014. , d6
  9015. , this.r
  9016. , cR
  9017. , d7
  9018. ])
  9019. // TLV
  9020. send = HLP.packTLV(4, send)
  9021. break
  9022. case CONST.SMPSTATE_EXPECT3:
  9023. HLP.debug.call(this, 'smp tlv 4')
  9024. // 0:p, 1:q, 2:cP, 3:d5, 4:d6, 5:r, 6:cR, 7:d7
  9025. ms = HLP.readLen(msg.msg.substr(0, 4))
  9026. if (ms !== 8) return this.abort()
  9027. msg = HLP.unpackMPIs(8, msg.msg.substring(4))
  9028. if ( !HLP.checkGroup(msg[0], N_MINUS_2) ||
  9029. !HLP.checkGroup(msg[1], N_MINUS_2) ||
  9030. !HLP.checkGroup(msg[5], N_MINUS_2)
  9031. ) return this.abort()
  9032. // verify znp of cP
  9033. t1 = HLP.multPowMod(this.g3, msg[3], msg[0], msg[2], N)
  9034. t2 = HLP.multPowMod(G, msg[3], this.g2, msg[4], N)
  9035. t2 = BigInt.multMod(t2, BigInt.powMod(msg[1], msg[2], N), N)
  9036. if (!HLP.ZKP(6, msg[2], t1, t2))
  9037. return this.abort()
  9038. // verify znp of cR
  9039. t3 = HLP.multPowMod(G, msg[7], this.g3ao, msg[6], N)
  9040. this.QoQ = BigInt.divMod(msg[1], this.q, N) // save Q over Q
  9041. t4 = HLP.multPowMod(this.QoQ, msg[7], msg[5], msg[6], N)
  9042. if (!HLP.ZKP(7, msg[6], t3, t4))
  9043. return this.abort()
  9044. this.computeR()
  9045. // zero-knowledge proof that R
  9046. // was generated according to the protocol
  9047. r7 = HLP.randomExponent()
  9048. tmp2 = BigInt.powMod(this.QoQ, r7, N)
  9049. cR = HLP.smpHash(8, BigInt.powMod(G, r7, N), tmp2)
  9050. d7 = this.computeD(r7, this.a3, cR)
  9051. send = HLP.packINT(3) + HLP.packMPIs([ this.r, cR, d7 ])
  9052. send = HLP.packTLV(5, send)
  9053. rab = this.computeRab(msg[5])
  9054. trust = !!BigInt.equals(rab, BigInt.divMod(msg[0], this.p, N))
  9055. this.trigger('trust', [trust, 'answered'])
  9056. this.init()
  9057. break
  9058. case CONST.SMPSTATE_EXPECT4:
  9059. HLP.debug.call(this, 'smp tlv 5')
  9060. // 0:r, 1:cR, 2:d7
  9061. ms = HLP.readLen(msg.msg.substr(0, 4))
  9062. if (ms !== 3) return this.abort()
  9063. msg = HLP.unpackMPIs(3, msg.msg.substring(4))
  9064. if (!HLP.checkGroup(msg[0], N_MINUS_2)) return this.abort()
  9065. // verify znp of cR
  9066. t3 = HLP.multPowMod(G, msg[2], this.g3ao, msg[1], N)
  9067. t4 = HLP.multPowMod(this.QoQ, msg[2], msg[0], msg[1], N)
  9068. if (!HLP.ZKP(8, msg[1], t3, t4))
  9069. return this.abort()
  9070. rab = this.computeRab(msg[0])
  9071. trust = !!BigInt.equals(rab, this.PoP)
  9072. this.trigger('trust', [trust, 'asked'])
  9073. this.init()
  9074. return
  9075. }
  9076. this.sendMsg(send)
  9077. }
  9078. // send a message
  9079. SM.prototype.sendMsg = function (send) {
  9080. this.trigger('send', [this.ssid, '\x00' + send])
  9081. }
  9082. SM.prototype.rcvSecret = function (secret, question) {
  9083. HLP.debug.call(this, 'receive secret')
  9084. var fn, our = false
  9085. if (this.smpstate === CONST.SMPSTATE_EXPECT0) {
  9086. fn = this.answer
  9087. } else {
  9088. fn = this.initiate
  9089. our = true
  9090. }
  9091. this.makeSecret(our, secret)
  9092. fn.call(this, question)
  9093. }
  9094. SM.prototype.answer = function () {
  9095. HLP.debug.call(this, 'smp answer')
  9096. var r4 = HLP.randomExponent()
  9097. this.computePQ(r4)
  9098. // zero-knowledge proof that P & Q
  9099. // were generated according to the protocol
  9100. var r5 = HLP.randomExponent()
  9101. var r6 = HLP.randomExponent()
  9102. var tmp = HLP.multPowMod(G, r5, this.g2, r6, N)
  9103. var cP = HLP.smpHash(5, BigInt.powMod(this.g3, r5, N), tmp)
  9104. var d5 = this.computeD(r5, r4, cP)
  9105. var d6 = this.computeD(r6, this.secret, cP)
  9106. this.smpstate = CONST.SMPSTATE_EXPECT3
  9107. var send = HLP.packINT(11) + HLP.packMPIs([
  9108. this.g2a
  9109. , this.c2
  9110. , this.d2
  9111. , this.g3a
  9112. , this.c3
  9113. , this.d3
  9114. , this.p
  9115. , this.q
  9116. , cP
  9117. , d5
  9118. , d6
  9119. ])
  9120. this.sendMsg(HLP.packTLV(3, send))
  9121. }
  9122. SM.prototype.initiate = function (question) {
  9123. HLP.debug.call(this, 'smp initiate')
  9124. if (this.smpstate !== CONST.SMPSTATE_EXPECT1)
  9125. this.abort() // abort + restart
  9126. this.makeG2s()
  9127. // zero-knowledge proof that the exponents
  9128. // associated with g2a & g3a are known
  9129. var r2 = HLP.randomExponent()
  9130. var r3 = HLP.randomExponent()
  9131. this.c2 = this.computeC(1, r2)
  9132. this.c3 = this.computeC(2, r3)
  9133. this.d2 = this.computeD(r2, this.a2, this.c2)
  9134. this.d3 = this.computeD(r3, this.a3, this.c3)
  9135. // set the next expected state
  9136. this.smpstate = CONST.SMPSTATE_EXPECT2
  9137. var send = ''
  9138. var type = 2
  9139. if (question) {
  9140. send += question
  9141. send += '\x00'
  9142. type = 7
  9143. }
  9144. send += HLP.packINT(6) + HLP.packMPIs([
  9145. this.g2a
  9146. , this.c2
  9147. , this.d2
  9148. , this.g3a
  9149. , this.c3
  9150. , this.d3
  9151. ])
  9152. this.sendMsg(HLP.packTLV(type, send))
  9153. }
  9154. SM.prototype.abort = function () {
  9155. this.init()
  9156. this.sendMsg(HLP.packTLV(6, ''))
  9157. this.trigger('abort')
  9158. }
  9159. }).call(this)
  9160. ;(function () {
  9161. var root = this
  9162. var CryptoJS, BigInt, EventEmitter, Worker, SMWPath
  9163. , CONST, HLP, Parse, AKE, SM, DSA
  9164. if (typeof module !== 'undefined' && module.exports) {
  9165. module.exports = OTR
  9166. CryptoJS = require('../vendor/crypto.js')
  9167. BigInt = require('../vendor/bigint.js')
  9168. EventEmitter = require('../vendor/eventemitter.js')
  9169. SMWPath = require('path').join(__dirname, '/sm-webworker.js')
  9170. CONST = require('./const.js')
  9171. HLP = require('./helpers.js')
  9172. Parse = require('./parse.js')
  9173. AKE = require('./ake.js')
  9174. SM = require('./sm.js')
  9175. DSA = require('./dsa.js')
  9176. // expose CONST for consistency with docs
  9177. OTR.CONST = CONST
  9178. } else {
  9179. // copy over and expose internals
  9180. Object.keys(root.OTR).forEach(function (k) {
  9181. OTR[k] = root.OTR[k]
  9182. })
  9183. root.OTR = OTR
  9184. CryptoJS = root.CryptoJS
  9185. BigInt = root.BigInt
  9186. EventEmitter = root.EventEmitter
  9187. Worker = root.Worker
  9188. SMWPath = 'sm-webworker.js'
  9189. CONST = OTR.CONST
  9190. HLP = OTR.HLP
  9191. Parse = OTR.Parse
  9192. AKE = OTR.AKE
  9193. SM = OTR.SM
  9194. DSA = root.DSA
  9195. }
  9196. // diffie-hellman modulus and generator
  9197. // see group 5, RFC 3526
  9198. var G = BigInt.str2bigInt(CONST.G, 10)
  9199. var N = BigInt.str2bigInt(CONST.N, 16)
  9200. // JavaScript integers
  9201. var MAX_INT = Math.pow(2, 53) - 1 // doubles
  9202. var MAX_UINT = Math.pow(2, 31) - 1 // bitwise operators
  9203. // OTR contructor
  9204. function OTR(options) {
  9205. if (!(this instanceof OTR)) return new OTR(options)
  9206. // options
  9207. options = options || {}
  9208. // private keys
  9209. if (options.priv && !(options.priv instanceof DSA))
  9210. throw new Error('Requires long-lived DSA key.')
  9211. this.priv = options.priv ? options.priv : new DSA()
  9212. this.fragment_size = options.fragment_size || 0
  9213. if (this.fragment_size < 0)
  9214. throw new Error('Fragment size must be a positive integer.')
  9215. this.send_interval = options.send_interval || 0
  9216. if (this.send_interval < 0)
  9217. throw new Error('Send interval must be a positive integer.')
  9218. this.outgoing = []
  9219. // instance tag
  9220. this.our_instance_tag = options.instance_tag || OTR.makeInstanceTag()
  9221. // debug
  9222. this.debug = !!options.debug
  9223. // smp in webworker options
  9224. // this is still experimental and undocumented
  9225. this.smw = options.smw
  9226. // init vals
  9227. this.init()
  9228. // bind methods
  9229. var self = this
  9230. ;['sendMsg', 'receiveMsg'].forEach(function (meth) {
  9231. self[meth] = self[meth].bind(self)
  9232. })
  9233. EventEmitter.call(this)
  9234. }
  9235. // inherit from EE
  9236. HLP.extend(OTR, EventEmitter)
  9237. // add to prototype
  9238. OTR.prototype.init = function () {
  9239. this.msgstate = CONST.MSGSTATE_PLAINTEXT
  9240. this.authstate = CONST.AUTHSTATE_NONE
  9241. this.ALLOW_V2 = true
  9242. this.ALLOW_V3 = true
  9243. this.REQUIRE_ENCRYPTION = false
  9244. this.SEND_WHITESPACE_TAG = false
  9245. this.WHITESPACE_START_AKE = false
  9246. this.ERROR_START_AKE = false
  9247. Parse.initFragment(this)
  9248. // their keys
  9249. this.their_y = null
  9250. this.their_old_y = null
  9251. this.their_keyid = 0
  9252. this.their_priv_pk = null
  9253. this.their_instance_tag = '\x00\x00\x00\x00'
  9254. // our keys
  9255. this.our_dh = this.dh()
  9256. this.our_old_dh = this.dh()
  9257. this.our_keyid = 2
  9258. // session keys
  9259. this.sessKeys = [ new Array(2), new Array(2) ]
  9260. // saved
  9261. this.storedMgs = []
  9262. this.oldMacKeys = []
  9263. // smp
  9264. this.sm = null // initialized after AKE
  9265. // when ake is complete
  9266. // save their keys and the session
  9267. this._akeInit()
  9268. // receive plaintext message since switching to plaintext
  9269. // used to decide when to stop sending pt tags when SEND_WHITESPACE_TAG
  9270. this.receivedPlaintext = false
  9271. }
  9272. OTR.prototype._akeInit = function () {
  9273. this.ake = new AKE(this)
  9274. this.transmittedRS = false
  9275. this.ssid = null
  9276. }
  9277. // smp over webworker
  9278. OTR.prototype._SMW = function (otr, reqs) {
  9279. this.otr = otr
  9280. var opts = {
  9281. path: SMWPath
  9282. , seed: BigInt.getSeed
  9283. }
  9284. if (typeof otr.smw === 'object')
  9285. Object.keys(otr.smw).forEach(function (k) {
  9286. opts[k] = otr.smw[k]
  9287. })
  9288. // load optional dep. in node
  9289. if (typeof module !== 'undefined' && module.exports)
  9290. Worker = require('webworker-threads').Worker
  9291. this.worker = new Worker(opts.path)
  9292. var self = this
  9293. this.worker.onmessage = function (e) {
  9294. var d = e.data
  9295. if (!d) return
  9296. self.trigger(d.method, d.args)
  9297. }
  9298. this.worker.postMessage({
  9299. type: 'seed'
  9300. , seed: opts.seed()
  9301. , imports: opts.imports
  9302. })
  9303. this.worker.postMessage({
  9304. type: 'init'
  9305. , reqs: reqs
  9306. })
  9307. }
  9308. // inherit from EE
  9309. HLP.extend(OTR.prototype._SMW, EventEmitter)
  9310. // shim sm methods
  9311. ;['handleSM', 'rcvSecret', 'abort'].forEach(function (m) {
  9312. OTR.prototype._SMW.prototype[m] = function () {
  9313. this.worker.postMessage({
  9314. type: 'method'
  9315. , method: m
  9316. , args: Array.prototype.slice.call(arguments, 0)
  9317. })
  9318. }
  9319. })
  9320. OTR.prototype._smInit = function () {
  9321. var reqs = {
  9322. ssid: this.ssid
  9323. , our_fp: this.priv.fingerprint()
  9324. , their_fp: this.their_priv_pk.fingerprint()
  9325. , debug: this.debug
  9326. }
  9327. if (this.smw) {
  9328. if (this.sm) this.sm.worker.terminate() // destroy prev webworker
  9329. this.sm = new this._SMW(this, reqs)
  9330. } else {
  9331. this.sm = new SM(reqs)
  9332. }
  9333. var self = this
  9334. ;['trust', 'abort', 'question'].forEach(function (e) {
  9335. self.sm.on(e, function () {
  9336. self.trigger('smp', [e].concat(Array.prototype.slice.call(arguments)))
  9337. })
  9338. })
  9339. this.sm.on('send', function (ssid, send) {
  9340. if (self.ssid === ssid) {
  9341. send = self.prepareMsg(send)
  9342. self.io(send)
  9343. }
  9344. })
  9345. }
  9346. OTR.prototype.io = function (msg, meta) {
  9347. // buffer
  9348. msg = ([].concat(msg)).map(function(m){
  9349. return { msg: m, meta: meta }
  9350. })
  9351. this.outgoing = this.outgoing.concat(msg)
  9352. var self = this
  9353. ;(function send(first) {
  9354. if (!first) {
  9355. if (!self.outgoing.length) return
  9356. var elem = self.outgoing.shift()
  9357. self.trigger('io', [elem.msg, elem.meta])
  9358. }
  9359. setTimeout(send, first ? 0 : self.send_interval)
  9360. }(true))
  9361. }
  9362. OTR.prototype.dh = function dh() {
  9363. var keys = { privateKey: BigInt.randBigInt(320) }
  9364. keys.publicKey = BigInt.powMod(G, keys.privateKey, N)
  9365. return keys
  9366. }
  9367. // session constructor
  9368. OTR.prototype.DHSession = function DHSession(our_dh, their_y) {
  9369. if (!(this instanceof DHSession)) return new DHSession(our_dh, their_y)
  9370. // shared secret
  9371. var s = BigInt.powMod(their_y, our_dh.privateKey, N)
  9372. var secbytes = HLP.packMPI(s)
  9373. // session id
  9374. this.id = HLP.mask(HLP.h2('\x00', secbytes), 0, 64) // first 64-bits
  9375. // are we the high or low end of the connection?
  9376. var sq = BigInt.greater(our_dh.publicKey, their_y)
  9377. var sendbyte = sq ? '\x01' : '\x02'
  9378. var rcvbyte = sq ? '\x02' : '\x01'
  9379. // sending and receiving keys
  9380. this.sendenc = HLP.mask(HLP.h1(sendbyte, secbytes), 0, 128) // f16 bytes
  9381. this.sendmac = CryptoJS.SHA1(CryptoJS.enc.Latin1.parse(this.sendenc))
  9382. this.sendmac = this.sendmac.toString(CryptoJS.enc.Latin1)
  9383. this.rcvenc = HLP.mask(HLP.h1(rcvbyte, secbytes), 0, 128)
  9384. this.rcvmac = CryptoJS.SHA1(CryptoJS.enc.Latin1.parse(this.rcvenc))
  9385. this.rcvmac = this.rcvmac.toString(CryptoJS.enc.Latin1)
  9386. this.rcvmacused = false
  9387. // extra symmetric key
  9388. this.extra_symkey = HLP.h2('\xff', secbytes)
  9389. // counters
  9390. this.send_counter = 0
  9391. this.rcv_counter = 0
  9392. }
  9393. OTR.prototype.rotateOurKeys = function () {
  9394. // reveal old mac keys
  9395. var self = this
  9396. this.sessKeys[1].forEach(function (sk) {
  9397. if (sk && sk.rcvmacused) self.oldMacKeys.push(sk.rcvmac)
  9398. })
  9399. // rotate our keys
  9400. this.our_old_dh = this.our_dh
  9401. this.our_dh = this.dh()
  9402. this.our_keyid += 1
  9403. this.sessKeys[1][0] = this.sessKeys[0][0]
  9404. this.sessKeys[1][1] = this.sessKeys[0][1]
  9405. this.sessKeys[0] = [
  9406. this.their_y ?
  9407. new this.DHSession(this.our_dh, this.their_y) : null
  9408. , this.their_old_y ?
  9409. new this.DHSession(this.our_dh, this.their_old_y) : null
  9410. ]
  9411. }
  9412. OTR.prototype.rotateTheirKeys = function (their_y) {
  9413. // increment their keyid
  9414. this.their_keyid += 1
  9415. // reveal old mac keys
  9416. var self = this
  9417. this.sessKeys.forEach(function (sk) {
  9418. if (sk[1] && sk[1].rcvmacused) self.oldMacKeys.push(sk[1].rcvmac)
  9419. })
  9420. // rotate their keys / session
  9421. this.their_old_y = this.their_y
  9422. this.sessKeys[0][1] = this.sessKeys[0][0]
  9423. this.sessKeys[1][1] = this.sessKeys[1][0]
  9424. // new keys / sessions
  9425. this.their_y = their_y
  9426. this.sessKeys[0][0] = new this.DHSession(this.our_dh, this.their_y)
  9427. this.sessKeys[1][0] = new this.DHSession(this.our_old_dh, this.their_y)
  9428. }
  9429. OTR.prototype.prepareMsg = function (msg, esk) {
  9430. if (this.msgstate !== CONST.MSGSTATE_ENCRYPTED || this.their_keyid === 0)
  9431. return this.error('Not ready to encrypt.')
  9432. var sessKeys = this.sessKeys[1][0]
  9433. if (sessKeys.send_counter >= MAX_INT)
  9434. return this.error('Should have rekeyed by now.')
  9435. sessKeys.send_counter += 1
  9436. var ctr = HLP.packCtr(sessKeys.send_counter)
  9437. var send = this.ake.otr_version + '\x03' // version and type
  9438. var v3 = (this.ake.otr_version === CONST.OTR_VERSION_3)
  9439. if (v3) {
  9440. send += this.our_instance_tag
  9441. send += this.their_instance_tag
  9442. }
  9443. send += '\x00' // flag
  9444. send += HLP.packINT(this.our_keyid - 1)
  9445. send += HLP.packINT(this.their_keyid)
  9446. send += HLP.packMPI(this.our_dh.publicKey)
  9447. send += ctr.substring(0, 8)
  9448. if (Math.ceil(msg.length / 8) >= MAX_UINT) // * 16 / 128
  9449. return this.error('Message is too long.')
  9450. var aes = HLP.encryptAes(
  9451. CryptoJS.enc.Latin1.parse(msg)
  9452. , sessKeys.sendenc
  9453. , ctr
  9454. )
  9455. send += HLP.packData(aes)
  9456. send += HLP.make1Mac(send, sessKeys.sendmac)
  9457. send += HLP.packData(this.oldMacKeys.splice(0).join(''))
  9458. send = HLP.wrapMsg(
  9459. send
  9460. , this.fragment_size
  9461. , v3
  9462. , this.our_instance_tag
  9463. , this.their_instance_tag
  9464. )
  9465. if (send[0]) return this.error(send[0])
  9466. // emit extra symmetric key
  9467. if (esk) this.trigger('file', ['send', sessKeys.extra_symkey, esk])
  9468. return send[1]
  9469. }
  9470. OTR.prototype.handleDataMsg = function (msg) {
  9471. var vt = msg.version + msg.type
  9472. if (this.ake.otr_version === CONST.OTR_VERSION_3)
  9473. vt += msg.instance_tags
  9474. var types = ['BYTE', 'INT', 'INT', 'MPI', 'CTR', 'DATA', 'MAC', 'DATA']
  9475. msg = HLP.splitype(types, msg.msg)
  9476. // ignore flag
  9477. var ign = (msg[0] === '\x01')
  9478. if (this.msgstate !== CONST.MSGSTATE_ENCRYPTED || msg.length !== 8) {
  9479. if (!ign) this.error('Received an unreadable encrypted message.', true)
  9480. return
  9481. }
  9482. var our_keyid = this.our_keyid - HLP.readLen(msg[2])
  9483. var their_keyid = this.their_keyid - HLP.readLen(msg[1])
  9484. if (our_keyid < 0 || our_keyid > 1) {
  9485. if (!ign) this.error('Not of our latest keys.', true)
  9486. return
  9487. }
  9488. if (their_keyid < 0 || their_keyid > 1) {
  9489. if (!ign) this.error('Not of your latest keys.', true)
  9490. return
  9491. }
  9492. var their_y = their_keyid ? this.their_old_y : this.their_y
  9493. if (their_keyid === 1 && !their_y) {
  9494. if (!ign) this.error('Do not have that key.')
  9495. return
  9496. }
  9497. var sessKeys = this.sessKeys[our_keyid][their_keyid]
  9498. var ctr = HLP.unpackCtr(msg[4])
  9499. if (ctr <= sessKeys.rcv_counter) {
  9500. if (!ign) this.error('Counter in message is not larger.')
  9501. return
  9502. }
  9503. sessKeys.rcv_counter = ctr
  9504. // verify mac
  9505. vt += msg.slice(0, 6).join('')
  9506. var vmac = HLP.make1Mac(vt, sessKeys.rcvmac)
  9507. if (!HLP.compare(msg[6], vmac)) {
  9508. if (!ign) this.error('MACs do not match.')
  9509. return
  9510. }
  9511. sessKeys.rcvmacused = true
  9512. var out = HLP.decryptAes(
  9513. msg[5].substring(4)
  9514. , sessKeys.rcvenc
  9515. , HLP.padCtr(msg[4])
  9516. )
  9517. out = out.toString(CryptoJS.enc.Latin1)
  9518. if (!our_keyid) this.rotateOurKeys()
  9519. if (!their_keyid) this.rotateTheirKeys(HLP.readMPI(msg[3]))
  9520. // parse TLVs
  9521. var ind = out.indexOf('\x00')
  9522. if (~ind) {
  9523. this.handleTLVs(out.substring(ind + 1), sessKeys)
  9524. out = out.substring(0, ind)
  9525. }
  9526. out = CryptoJS.enc.Latin1.parse(out)
  9527. return out.toString(CryptoJS.enc.Utf8)
  9528. }
  9529. OTR.prototype.handleTLVs = function (tlvs, sessKeys) {
  9530. var type, len, msg
  9531. for (; tlvs.length; ) {
  9532. type = HLP.unpackSHORT(tlvs.substr(0, 2))
  9533. len = HLP.unpackSHORT(tlvs.substr(2, 2))
  9534. msg = tlvs.substr(4, len)
  9535. // TODO: handle pathological cases better
  9536. if (msg.length < len) break
  9537. switch (type) {
  9538. case 1:
  9539. // Disconnected
  9540. this.msgstate = CONST.MSGSTATE_FINISHED
  9541. this.trigger('status', [CONST.STATUS_END_OTR])
  9542. break
  9543. case 2: case 3: case 4:
  9544. case 5: case 6: case 7:
  9545. // SMP
  9546. if (this.msgstate !== CONST.MSGSTATE_ENCRYPTED) {
  9547. if (this.sm) this.sm.abort()
  9548. return
  9549. }
  9550. if (!this.sm) this._smInit()
  9551. this.sm.handleSM({ msg: msg, type: type })
  9552. break
  9553. case 8:
  9554. // utf8 filenames
  9555. msg = msg.substring(4) // remove 4-byte indication
  9556. msg = CryptoJS.enc.Latin1.parse(msg)
  9557. msg = msg.toString(CryptoJS.enc.Utf8)
  9558. // Extra Symkey
  9559. this.trigger('file', ['receive', sessKeys.extra_symkey, msg])
  9560. break
  9561. }
  9562. tlvs = tlvs.substring(4 + len)
  9563. }
  9564. }
  9565. OTR.prototype.smpSecret = function (secret, question) {
  9566. if (this.msgstate !== CONST.MSGSTATE_ENCRYPTED)
  9567. return this.error('Must be encrypted for SMP.')
  9568. if (typeof secret !== 'string' || secret.length < 1)
  9569. return this.error('Secret is required.')
  9570. if (!this.sm) this._smInit()
  9571. // utf8 inputs
  9572. secret = CryptoJS.enc.Utf8.parse(secret).toString(CryptoJS.enc.Latin1)
  9573. question = CryptoJS.enc.Utf8.parse(question).toString(CryptoJS.enc.Latin1)
  9574. this.sm.rcvSecret(secret, question)
  9575. }
  9576. OTR.prototype.sendQueryMsg = function () {
  9577. var versions = {}
  9578. , msg = CONST.OTR_TAG
  9579. if (this.ALLOW_V2) versions['2'] = true
  9580. if (this.ALLOW_V3) versions['3'] = true
  9581. // but we don't allow v1
  9582. // if (versions['1']) msg += '?'
  9583. var vs = Object.keys(versions)
  9584. if (vs.length) {
  9585. msg += 'v'
  9586. vs.forEach(function (v) {
  9587. if (v !== '1') msg += v
  9588. })
  9589. msg += '?'
  9590. }
  9591. this.io(msg)
  9592. this.trigger('status', [CONST.STATUS_SEND_QUERY])
  9593. }
  9594. OTR.prototype.sendMsg = function (msg, meta) {
  9595. if ( this.REQUIRE_ENCRYPTION ||
  9596. this.msgstate !== CONST.MSGSTATE_PLAINTEXT
  9597. ) {
  9598. msg = CryptoJS.enc.Utf8.parse(msg)
  9599. msg = msg.toString(CryptoJS.enc.Latin1)
  9600. }
  9601. switch (this.msgstate) {
  9602. case CONST.MSGSTATE_PLAINTEXT:
  9603. if (this.REQUIRE_ENCRYPTION) {
  9604. this.storedMgs.push({msg: msg, meta: meta})
  9605. this.sendQueryMsg()
  9606. return
  9607. }
  9608. if (this.SEND_WHITESPACE_TAG && !this.receivedPlaintext) {
  9609. msg += CONST.WHITESPACE_TAG // 16 byte tag
  9610. if (this.ALLOW_V3) msg += CONST.WHITESPACE_TAG_V3
  9611. if (this.ALLOW_V2) msg += CONST.WHITESPACE_TAG_V2
  9612. }
  9613. break
  9614. case CONST.MSGSTATE_FINISHED:
  9615. this.storedMgs.push({msg: msg, meta: meta})
  9616. this.error('Message cannot be sent at this time.')
  9617. return
  9618. case CONST.MSGSTATE_ENCRYPTED:
  9619. msg = this.prepareMsg(msg)
  9620. break
  9621. default:
  9622. throw new Error('Unknown message state.')
  9623. }
  9624. if (msg) this.io(msg, meta)
  9625. }
  9626. OTR.prototype.receiveMsg = function (msg) {
  9627. // parse type
  9628. msg = Parse.parseMsg(this, msg)
  9629. if (!msg) return
  9630. switch (msg.cls) {
  9631. case 'error':
  9632. this.error(msg.msg)
  9633. return
  9634. case 'ake':
  9635. if ( msg.version === CONST.OTR_VERSION_3 &&
  9636. this.checkInstanceTags(msg.instance_tags)
  9637. ) return // ignore
  9638. this.ake.handleAKE(msg)
  9639. return
  9640. case 'data':
  9641. if ( msg.version === CONST.OTR_VERSION_3 &&
  9642. this.checkInstanceTags(msg.instance_tags)
  9643. ) return // ignore
  9644. msg.msg = this.handleDataMsg(msg)
  9645. msg.encrypted = true
  9646. break
  9647. case 'query':
  9648. if (this.msgstate === CONST.MSGSTATE_ENCRYPTED) this._akeInit()
  9649. this.doAKE(msg)
  9650. break
  9651. default:
  9652. // check for encrypted
  9653. if ( this.REQUIRE_ENCRYPTION ||
  9654. this.msgstate !== CONST.MSGSTATE_PLAINTEXT
  9655. ) this.error('Received an unencrypted message.')
  9656. // received a plaintext message
  9657. // stop sending the whitespace tag
  9658. this.receivedPlaintext = true
  9659. // received a whitespace tag
  9660. if (this.WHITESPACE_START_AKE && msg.ver.length > 0)
  9661. this.doAKE(msg)
  9662. }
  9663. if (msg.msg) this.trigger('ui', [msg.msg, !!msg.encrypted])
  9664. }
  9665. OTR.prototype.checkInstanceTags = function (it) {
  9666. var their_it = HLP.readLen(it.substr(0, 4))
  9667. var our_it = HLP.readLen(it.substr(4, 4))
  9668. if (our_it && our_it !== HLP.readLen(this.our_instance_tag))
  9669. return true
  9670. if (HLP.readLen(this.their_instance_tag)) {
  9671. if (HLP.readLen(this.their_instance_tag) !== their_it) return true
  9672. } else {
  9673. if (their_it < 100) return true
  9674. this.their_instance_tag = HLP.packINT(their_it)
  9675. }
  9676. }
  9677. OTR.prototype.doAKE = function (msg) {
  9678. if (this.ALLOW_V3 && ~msg.ver.indexOf(CONST.OTR_VERSION_3)) {
  9679. this.ake.initiateAKE(CONST.OTR_VERSION_3)
  9680. } else if (this.ALLOW_V2 && ~msg.ver.indexOf(CONST.OTR_VERSION_2)) {
  9681. this.ake.initiateAKE(CONST.OTR_VERSION_2)
  9682. } else {
  9683. // is this an error?
  9684. this.error('OTR conversation requested, ' +
  9685. 'but no compatible protocol version found.')
  9686. }
  9687. }
  9688. OTR.prototype.error = function (err, send) {
  9689. if (send) {
  9690. if (!this.debug) err = "An OTR error has occurred."
  9691. err = '?OTR Error:' + err
  9692. this.io(err)
  9693. return
  9694. }
  9695. this.trigger('error', [err])
  9696. }
  9697. OTR.prototype.sendStored = function () {
  9698. var self = this
  9699. ;(this.storedMgs.splice(0)).forEach(function (elem) {
  9700. var msg = self.prepareMsg(elem.msg)
  9701. self.io(msg, elem.meta)
  9702. })
  9703. }
  9704. OTR.prototype.sendFile = function (filename) {
  9705. if (this.msgstate !== CONST.MSGSTATE_ENCRYPTED)
  9706. return this.error('Not ready to encrypt.')
  9707. if (this.ake.otr_version !== CONST.OTR_VERSION_3)
  9708. return this.error('Protocol v3 required.')
  9709. if (!filename) return this.error('Please specify a filename.')
  9710. // utf8 filenames
  9711. var l1name = CryptoJS.enc.Utf8.parse(filename)
  9712. l1name = l1name.toString(CryptoJS.enc.Latin1)
  9713. if (l1name.length >= 65532) return this.error('filename is too long.')
  9714. var msg = '\x00' // null byte
  9715. msg += '\x00\x08' // type 8 tlv
  9716. msg += HLP.packSHORT(4 + l1name.length) // length of value
  9717. msg += '\x00\x00\x00\x01' // four bytes indicating file
  9718. msg += l1name
  9719. msg = this.prepareMsg(msg, filename)
  9720. this.io(msg)
  9721. }
  9722. OTR.prototype.endOtr = function () {
  9723. if (this.msgstate === CONST.MSGSTATE_ENCRYPTED) {
  9724. this.sendMsg('\x00\x00\x01\x00\x00')
  9725. if (this.sm) {
  9726. if (this.smw) this.sm.worker.terminate() // destroy webworker
  9727. this.sm = null
  9728. }
  9729. }
  9730. this.msgstate = CONST.MSGSTATE_PLAINTEXT
  9731. this.receivedPlaintext = false
  9732. this.trigger('status', [CONST.STATUS_END_OTR])
  9733. }
  9734. // attach methods
  9735. OTR.makeInstanceTag = function () {
  9736. var num = BigInt.randBigInt(32)
  9737. if (BigInt.greater(BigInt.str2bigInt('100', 16), num))
  9738. return OTR.makeInstanceTag()
  9739. return HLP.packINT(parseInt(BigInt.bigInt2str(num, 10), 10))
  9740. }
  9741. }).call(this)
  9742. return {
  9743. OTR: this.OTR
  9744. , DSA: this.DSA
  9745. }
  9746. }))
  9747. ;
  9748. //! moment.js
  9749. //! version : 2.6.0
  9750. //! authors : Tim Wood, Iskren Chernev, Moment.js contributors
  9751. //! license : MIT
  9752. //! momentjs.com
  9753. (function (undefined) {
  9754. /************************************
  9755. Constants
  9756. ************************************/
  9757. var moment,
  9758. VERSION = "2.6.0",
  9759. // the global-scope this is NOT the global object in Node.js
  9760. globalScope = typeof global !== 'undefined' ? global : this,
  9761. oldGlobalMoment,
  9762. round = Math.round,
  9763. i,
  9764. YEAR = 0,
  9765. MONTH = 1,
  9766. DATE = 2,
  9767. HOUR = 3,
  9768. MINUTE = 4,
  9769. SECOND = 5,
  9770. MILLISECOND = 6,
  9771. // internal storage for language config files
  9772. languages = {},
  9773. // moment internal properties
  9774. momentProperties = {
  9775. _isAMomentObject: null,
  9776. _i : null,
  9777. _f : null,
  9778. _l : null,
  9779. _strict : null,
  9780. _isUTC : null,
  9781. _offset : null, // optional. Combine with _isUTC
  9782. _pf : null,
  9783. _lang : null // optional
  9784. },
  9785. // check for nodeJS
  9786. hasModule = (typeof module !== 'undefined' && module.exports),
  9787. // ASP.NET json date format regex
  9788. aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
  9789. aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,
  9790. // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
  9791. // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
  9792. isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,
  9793. // format tokens
  9794. formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,
  9795. localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,
  9796. // parsing token regexes
  9797. parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
  9798. parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
  9799. parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999
  9800. parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999
  9801. parseTokenDigits = /\d+/, // nonzero number of digits
  9802. parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic.
  9803. parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z
  9804. parseTokenT = /T/i, // T (ISO separator)
  9805. parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123
  9806. parseTokenOrdinal = /\d{1,2}/,
  9807. //strict parsing regexes
  9808. parseTokenOneDigit = /\d/, // 0 - 9
  9809. parseTokenTwoDigits = /\d\d/, // 00 - 99
  9810. parseTokenThreeDigits = /\d{3}/, // 000 - 999
  9811. parseTokenFourDigits = /\d{4}/, // 0000 - 9999
  9812. parseTokenSixDigits = /[+-]?\d{6}/, // -999,999 - 999,999
  9813. parseTokenSignedNumber = /[+-]?\d+/, // -inf - inf
  9814. // iso 8601 regex
  9815. // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
  9816. isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,
  9817. isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
  9818. isoDates = [
  9819. ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/],
  9820. ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/],
  9821. ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/],
  9822. ['GGGG-[W]WW', /\d{4}-W\d{2}/],
  9823. ['YYYY-DDD', /\d{4}-\d{3}/]
  9824. ],
  9825. // iso time formats and regexes
  9826. isoTimes = [
  9827. ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/],
  9828. ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/],
  9829. ['HH:mm', /(T| )\d\d:\d\d/],
  9830. ['HH', /(T| )\d\d/]
  9831. ],
  9832. // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"]
  9833. parseTimezoneChunker = /([\+\-]|\d\d)/gi,
  9834. // getter and setter names
  9835. proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
  9836. unitMillisecondFactors = {
  9837. 'Milliseconds' : 1,
  9838. 'Seconds' : 1e3,
  9839. 'Minutes' : 6e4,
  9840. 'Hours' : 36e5,
  9841. 'Days' : 864e5,
  9842. 'Months' : 2592e6,
  9843. 'Years' : 31536e6
  9844. },
  9845. unitAliases = {
  9846. ms : 'millisecond',
  9847. s : 'second',
  9848. m : 'minute',
  9849. h : 'hour',
  9850. d : 'day',
  9851. D : 'date',
  9852. w : 'week',
  9853. W : 'isoWeek',
  9854. M : 'month',
  9855. Q : 'quarter',
  9856. y : 'year',
  9857. DDD : 'dayOfYear',
  9858. e : 'weekday',
  9859. E : 'isoWeekday',
  9860. gg: 'weekYear',
  9861. GG: 'isoWeekYear'
  9862. },
  9863. camelFunctions = {
  9864. dayofyear : 'dayOfYear',
  9865. isoweekday : 'isoWeekday',
  9866. isoweek : 'isoWeek',
  9867. weekyear : 'weekYear',
  9868. isoweekyear : 'isoWeekYear'
  9869. },
  9870. // format function strings
  9871. formatFunctions = {},
  9872. // tokens to ordinalize and pad
  9873. ordinalizeTokens = 'DDD w W M D d'.split(' '),
  9874. paddedTokens = 'M D H h m s w W'.split(' '),
  9875. formatTokenFunctions = {
  9876. M : function () {
  9877. return this.month() + 1;
  9878. },
  9879. MMM : function (format) {
  9880. return this.lang().monthsShort(this, format);
  9881. },
  9882. MMMM : function (format) {
  9883. return this.lang().months(this, format);
  9884. },
  9885. D : function () {
  9886. return this.date();
  9887. },
  9888. DDD : function () {
  9889. return this.dayOfYear();
  9890. },
  9891. d : function () {
  9892. return this.day();
  9893. },
  9894. dd : function (format) {
  9895. return this.lang().weekdaysMin(this, format);
  9896. },
  9897. ddd : function (format) {
  9898. return this.lang().weekdaysShort(this, format);
  9899. },
  9900. dddd : function (format) {
  9901. return this.lang().weekdays(this, format);
  9902. },
  9903. w : function () {
  9904. return this.week();
  9905. },
  9906. W : function () {
  9907. return this.isoWeek();
  9908. },
  9909. YY : function () {
  9910. return leftZeroFill(this.year() % 100, 2);
  9911. },
  9912. YYYY : function () {
  9913. return leftZeroFill(this.year(), 4);
  9914. },
  9915. YYYYY : function () {
  9916. return leftZeroFill(this.year(), 5);
  9917. },
  9918. YYYYYY : function () {
  9919. var y = this.year(), sign = y >= 0 ? '+' : '-';
  9920. return sign + leftZeroFill(Math.abs(y), 6);
  9921. },
  9922. gg : function () {
  9923. return leftZeroFill(this.weekYear() % 100, 2);
  9924. },
  9925. gggg : function () {
  9926. return leftZeroFill(this.weekYear(), 4);
  9927. },
  9928. ggggg : function () {
  9929. return leftZeroFill(this.weekYear(), 5);
  9930. },
  9931. GG : function () {
  9932. return leftZeroFill(this.isoWeekYear() % 100, 2);
  9933. },
  9934. GGGG : function () {
  9935. return leftZeroFill(this.isoWeekYear(), 4);
  9936. },
  9937. GGGGG : function () {
  9938. return leftZeroFill(this.isoWeekYear(), 5);
  9939. },
  9940. e : function () {
  9941. return this.weekday();
  9942. },
  9943. E : function () {
  9944. return this.isoWeekday();
  9945. },
  9946. a : function () {
  9947. return this.lang().meridiem(this.hours(), this.minutes(), true);
  9948. },
  9949. A : function () {
  9950. return this.lang().meridiem(this.hours(), this.minutes(), false);
  9951. },
  9952. H : function () {
  9953. return this.hours();
  9954. },
  9955. h : function () {
  9956. return this.hours() % 12 || 12;
  9957. },
  9958. m : function () {
  9959. return this.minutes();
  9960. },
  9961. s : function () {
  9962. return this.seconds();
  9963. },
  9964. S : function () {
  9965. return toInt(this.milliseconds() / 100);
  9966. },
  9967. SS : function () {
  9968. return leftZeroFill(toInt(this.milliseconds() / 10), 2);
  9969. },
  9970. SSS : function () {
  9971. return leftZeroFill(this.milliseconds(), 3);
  9972. },
  9973. SSSS : function () {
  9974. return leftZeroFill(this.milliseconds(), 3);
  9975. },
  9976. Z : function () {
  9977. var a = -this.zone(),
  9978. b = "+";
  9979. if (a < 0) {
  9980. a = -a;
  9981. b = "-";
  9982. }
  9983. return b + leftZeroFill(toInt(a / 60), 2) + ":" + leftZeroFill(toInt(a) % 60, 2);
  9984. },
  9985. ZZ : function () {
  9986. var a = -this.zone(),
  9987. b = "+";
  9988. if (a < 0) {
  9989. a = -a;
  9990. b = "-";
  9991. }
  9992. return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2);
  9993. },
  9994. z : function () {
  9995. return this.zoneAbbr();
  9996. },
  9997. zz : function () {
  9998. return this.zoneName();
  9999. },
  10000. X : function () {
  10001. return this.unix();
  10002. },
  10003. Q : function () {
  10004. return this.quarter();
  10005. }
  10006. },
  10007. lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'];
  10008. function defaultParsingFlags() {
  10009. // We need to deep clone this object, and es5 standard is not very
  10010. // helpful.
  10011. return {
  10012. empty : false,
  10013. unusedTokens : [],
  10014. unusedInput : [],
  10015. overflow : -2,
  10016. charsLeftOver : 0,
  10017. nullInput : false,
  10018. invalidMonth : null,
  10019. invalidFormat : false,
  10020. userInvalidated : false,
  10021. iso: false
  10022. };
  10023. }
  10024. function deprecate(msg, fn) {
  10025. var firstTime = true;
  10026. function printMsg() {
  10027. if (moment.suppressDeprecationWarnings === false &&
  10028. typeof console !== 'undefined' && console.warn) {
  10029. console.warn("Deprecation warning: " + msg);
  10030. }
  10031. }
  10032. return extend(function () {
  10033. if (firstTime) {
  10034. printMsg();
  10035. firstTime = false;
  10036. }
  10037. return fn.apply(this, arguments);
  10038. }, fn);
  10039. }
  10040. function padToken(func, count) {
  10041. return function (a) {
  10042. return leftZeroFill(func.call(this, a), count);
  10043. };
  10044. }
  10045. function ordinalizeToken(func, period) {
  10046. return function (a) {
  10047. return this.lang().ordinal(func.call(this, a), period);
  10048. };
  10049. }
  10050. while (ordinalizeTokens.length) {
  10051. i = ordinalizeTokens.pop();
  10052. formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i);
  10053. }
  10054. while (paddedTokens.length) {
  10055. i = paddedTokens.pop();
  10056. formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2);
  10057. }
  10058. formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3);
  10059. /************************************
  10060. Constructors
  10061. ************************************/
  10062. function Language() {
  10063. }
  10064. // Moment prototype object
  10065. function Moment(config) {
  10066. checkOverflow(config);
  10067. extend(this, config);
  10068. }
  10069. // Duration Constructor
  10070. function Duration(duration) {
  10071. var normalizedInput = normalizeObjectUnits(duration),
  10072. years = normalizedInput.year || 0,
  10073. quarters = normalizedInput.quarter || 0,
  10074. months = normalizedInput.month || 0,
  10075. weeks = normalizedInput.week || 0,
  10076. days = normalizedInput.day || 0,
  10077. hours = normalizedInput.hour || 0,
  10078. minutes = normalizedInput.minute || 0,
  10079. seconds = normalizedInput.second || 0,
  10080. milliseconds = normalizedInput.millisecond || 0;
  10081. // representation for dateAddRemove
  10082. this._milliseconds = +milliseconds +
  10083. seconds * 1e3 + // 1000
  10084. minutes * 6e4 + // 1000 * 60
  10085. hours * 36e5; // 1000 * 60 * 60
  10086. // Because of dateAddRemove treats 24 hours as different from a
  10087. // day when working around DST, we need to store them separately
  10088. this._days = +days +
  10089. weeks * 7;
  10090. // It is impossible translate months into days without knowing
  10091. // which months you are are talking about, so we have to store
  10092. // it separately.
  10093. this._months = +months +
  10094. quarters * 3 +
  10095. years * 12;
  10096. this._data = {};
  10097. this._bubble();
  10098. }
  10099. /************************************
  10100. Helpers
  10101. ************************************/
  10102. function extend(a, b) {
  10103. for (var i in b) {
  10104. if (b.hasOwnProperty(i)) {
  10105. a[i] = b[i];
  10106. }
  10107. }
  10108. if (b.hasOwnProperty("toString")) {
  10109. a.toString = b.toString;
  10110. }
  10111. if (b.hasOwnProperty("valueOf")) {
  10112. a.valueOf = b.valueOf;
  10113. }
  10114. return a;
  10115. }
  10116. function cloneMoment(m) {
  10117. var result = {}, i;
  10118. for (i in m) {
  10119. if (m.hasOwnProperty(i) && momentProperties.hasOwnProperty(i)) {
  10120. result[i] = m[i];
  10121. }
  10122. }
  10123. return result;
  10124. }
  10125. function absRound(number) {
  10126. if (number < 0) {
  10127. return Math.ceil(number);
  10128. } else {
  10129. return Math.floor(number);
  10130. }
  10131. }
  10132. // left zero fill a number
  10133. // see http://jsperf.com/left-zero-filling for performance comparison
  10134. function leftZeroFill(number, targetLength, forceSign) {
  10135. var output = '' + Math.abs(number),
  10136. sign = number >= 0;
  10137. while (output.length < targetLength) {
  10138. output = '0' + output;
  10139. }
  10140. return (sign ? (forceSign ? '+' : '') : '-') + output;
  10141. }
  10142. // helper function for _.addTime and _.subtractTime
  10143. function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) {
  10144. var milliseconds = duration._milliseconds,
  10145. days = duration._days,
  10146. months = duration._months;
  10147. updateOffset = updateOffset == null ? true : updateOffset;
  10148. if (milliseconds) {
  10149. mom._d.setTime(+mom._d + milliseconds * isAdding);
  10150. }
  10151. if (days) {
  10152. rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding);
  10153. }
  10154. if (months) {
  10155. rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding);
  10156. }
  10157. if (updateOffset) {
  10158. moment.updateOffset(mom, days || months);
  10159. }
  10160. }
  10161. // check if is an array
  10162. function isArray(input) {
  10163. return Object.prototype.toString.call(input) === '[object Array]';
  10164. }
  10165. function isDate(input) {
  10166. return Object.prototype.toString.call(input) === '[object Date]' ||
  10167. input instanceof Date;
  10168. }
  10169. // compare two arrays, return the number of differences
  10170. function compareArrays(array1, array2, dontConvert) {
  10171. var len = Math.min(array1.length, array2.length),
  10172. lengthDiff = Math.abs(array1.length - array2.length),
  10173. diffs = 0,
  10174. i;
  10175. for (i = 0; i < len; i++) {
  10176. if ((dontConvert && array1[i] !== array2[i]) ||
  10177. (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
  10178. diffs++;
  10179. }
  10180. }
  10181. return diffs + lengthDiff;
  10182. }
  10183. function normalizeUnits(units) {
  10184. if (units) {
  10185. var lowered = units.toLowerCase().replace(/(.)s$/, '$1');
  10186. units = unitAliases[units] || camelFunctions[lowered] || lowered;
  10187. }
  10188. return units;
  10189. }
  10190. function normalizeObjectUnits(inputObject) {
  10191. var normalizedInput = {},
  10192. normalizedProp,
  10193. prop;
  10194. for (prop in inputObject) {
  10195. if (inputObject.hasOwnProperty(prop)) {
  10196. normalizedProp = normalizeUnits(prop);
  10197. if (normalizedProp) {
  10198. normalizedInput[normalizedProp] = inputObject[prop];
  10199. }
  10200. }
  10201. }
  10202. return normalizedInput;
  10203. }
  10204. function makeList(field) {
  10205. var count, setter;
  10206. if (field.indexOf('week') === 0) {
  10207. count = 7;
  10208. setter = 'day';
  10209. }
  10210. else if (field.indexOf('month') === 0) {
  10211. count = 12;
  10212. setter = 'month';
  10213. }
  10214. else {
  10215. return;
  10216. }
  10217. moment[field] = function (format, index) {
  10218. var i, getter,
  10219. method = moment.fn._lang[field],
  10220. results = [];
  10221. if (typeof format === 'number') {
  10222. index = format;
  10223. format = undefined;
  10224. }
  10225. getter = function (i) {
  10226. var m = moment().utc().set(setter, i);
  10227. return method.call(moment.fn._lang, m, format || '');
  10228. };
  10229. if (index != null) {
  10230. return getter(index);
  10231. }
  10232. else {
  10233. for (i = 0; i < count; i++) {
  10234. results.push(getter(i));
  10235. }
  10236. return results;
  10237. }
  10238. };
  10239. }
  10240. function toInt(argumentForCoercion) {
  10241. var coercedNumber = +argumentForCoercion,
  10242. value = 0;
  10243. if (coercedNumber !== 0 && isFinite(coercedNumber)) {
  10244. if (coercedNumber >= 0) {
  10245. value = Math.floor(coercedNumber);
  10246. } else {
  10247. value = Math.ceil(coercedNumber);
  10248. }
  10249. }
  10250. return value;
  10251. }
  10252. function daysInMonth(year, month) {
  10253. return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
  10254. }
  10255. function weeksInYear(year, dow, doy) {
  10256. return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week;
  10257. }
  10258. function daysInYear(year) {
  10259. return isLeapYear(year) ? 366 : 365;
  10260. }
  10261. function isLeapYear(year) {
  10262. return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
  10263. }
  10264. function checkOverflow(m) {
  10265. var overflow;
  10266. if (m._a && m._pf.overflow === -2) {
  10267. overflow =
  10268. m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH :
  10269. m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE :
  10270. m._a[HOUR] < 0 || m._a[HOUR] > 23 ? HOUR :
  10271. m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE :
  10272. m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND :
  10273. m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND :
  10274. -1;
  10275. if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
  10276. overflow = DATE;
  10277. }
  10278. m._pf.overflow = overflow;
  10279. }
  10280. }
  10281. function isValid(m) {
  10282. if (m._isValid == null) {
  10283. m._isValid = !isNaN(m._d.getTime()) &&
  10284. m._pf.overflow < 0 &&
  10285. !m._pf.empty &&
  10286. !m._pf.invalidMonth &&
  10287. !m._pf.nullInput &&
  10288. !m._pf.invalidFormat &&
  10289. !m._pf.userInvalidated;
  10290. if (m._strict) {
  10291. m._isValid = m._isValid &&
  10292. m._pf.charsLeftOver === 0 &&
  10293. m._pf.unusedTokens.length === 0;
  10294. }
  10295. }
  10296. return m._isValid;
  10297. }
  10298. function normalizeLanguage(key) {
  10299. return key ? key.toLowerCase().replace('_', '-') : key;
  10300. }
  10301. // Return a moment from input, that is local/utc/zone equivalent to model.
  10302. function makeAs(input, model) {
  10303. return model._isUTC ? moment(input).zone(model._offset || 0) :
  10304. moment(input).local();
  10305. }
  10306. /************************************
  10307. Languages
  10308. ************************************/
  10309. extend(Language.prototype, {
  10310. set : function (config) {
  10311. var prop, i;
  10312. for (i in config) {
  10313. prop = config[i];
  10314. if (typeof prop === 'function') {
  10315. this[i] = prop;
  10316. } else {
  10317. this['_' + i] = prop;
  10318. }
  10319. }
  10320. },
  10321. _months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
  10322. months : function (m) {
  10323. return this._months[m.month()];
  10324. },
  10325. _monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
  10326. monthsShort : function (m) {
  10327. return this._monthsShort[m.month()];
  10328. },
  10329. monthsParse : function (monthName) {
  10330. var i, mom, regex;
  10331. if (!this._monthsParse) {
  10332. this._monthsParse = [];
  10333. }
  10334. for (i = 0; i < 12; i++) {
  10335. // make the regex if we don't have it already
  10336. if (!this._monthsParse[i]) {
  10337. mom = moment.utc([2000, i]);
  10338. regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
  10339. this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
  10340. }
  10341. // test the regex
  10342. if (this._monthsParse[i].test(monthName)) {
  10343. return i;
  10344. }
  10345. }
  10346. },
  10347. _weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
  10348. weekdays : function (m) {
  10349. return this._weekdays[m.day()];
  10350. },
  10351. _weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
  10352. weekdaysShort : function (m) {
  10353. return this._weekdaysShort[m.day()];
  10354. },
  10355. _weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
  10356. weekdaysMin : function (m) {
  10357. return this._weekdaysMin[m.day()];
  10358. },
  10359. weekdaysParse : function (weekdayName) {
  10360. var i, mom, regex;
  10361. if (!this._weekdaysParse) {
  10362. this._weekdaysParse = [];
  10363. }
  10364. for (i = 0; i < 7; i++) {
  10365. // make the regex if we don't have it already
  10366. if (!this._weekdaysParse[i]) {
  10367. mom = moment([2000, 1]).day(i);
  10368. regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
  10369. this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
  10370. }
  10371. // test the regex
  10372. if (this._weekdaysParse[i].test(weekdayName)) {
  10373. return i;
  10374. }
  10375. }
  10376. },
  10377. _longDateFormat : {
  10378. LT : "h:mm A",
  10379. L : "MM/DD/YYYY",
  10380. LL : "MMMM D YYYY",
  10381. LLL : "MMMM D YYYY LT",
  10382. LLLL : "dddd, MMMM D YYYY LT"
  10383. },
  10384. longDateFormat : function (key) {
  10385. var output = this._longDateFormat[key];
  10386. if (!output && this._longDateFormat[key.toUpperCase()]) {
  10387. output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) {
  10388. return val.slice(1);
  10389. });
  10390. this._longDateFormat[key] = output;
  10391. }
  10392. return output;
  10393. },
  10394. isPM : function (input) {
  10395. // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
  10396. // Using charAt should be more compatible.
  10397. return ((input + '').toLowerCase().charAt(0) === 'p');
  10398. },
  10399. _meridiemParse : /[ap]\.?m?\.?/i,
  10400. meridiem : function (hours, minutes, isLower) {
  10401. if (hours > 11) {
  10402. return isLower ? 'pm' : 'PM';
  10403. } else {
  10404. return isLower ? 'am' : 'AM';
  10405. }
  10406. },
  10407. _calendar : {
  10408. sameDay : '[Today at] LT',
  10409. nextDay : '[Tomorrow at] LT',
  10410. nextWeek : 'dddd [at] LT',
  10411. lastDay : '[Yesterday at] LT',
  10412. lastWeek : '[Last] dddd [at] LT',
  10413. sameElse : 'L'
  10414. },
  10415. calendar : function (key, mom) {
  10416. var output = this._calendar[key];
  10417. return typeof output === 'function' ? output.apply(mom) : output;
  10418. },
  10419. _relativeTime : {
  10420. future : "in %s",
  10421. past : "%s ago",
  10422. s : "a few seconds",
  10423. m : "a minute",
  10424. mm : "%d minutes",
  10425. h : "an hour",
  10426. hh : "%d hours",
  10427. d : "a day",
  10428. dd : "%d days",
  10429. M : "a month",
  10430. MM : "%d months",
  10431. y : "a year",
  10432. yy : "%d years"
  10433. },
  10434. relativeTime : function (number, withoutSuffix, string, isFuture) {
  10435. var output = this._relativeTime[string];
  10436. return (typeof output === 'function') ?
  10437. output(number, withoutSuffix, string, isFuture) :
  10438. output.replace(/%d/i, number);
  10439. },
  10440. pastFuture : function (diff, output) {
  10441. var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
  10442. return typeof format === 'function' ? format(output) : format.replace(/%s/i, output);
  10443. },
  10444. ordinal : function (number) {
  10445. return this._ordinal.replace("%d", number);
  10446. },
  10447. _ordinal : "%d",
  10448. preparse : function (string) {
  10449. return string;
  10450. },
  10451. postformat : function (string) {
  10452. return string;
  10453. },
  10454. week : function (mom) {
  10455. return weekOfYear(mom, this._week.dow, this._week.doy).week;
  10456. },
  10457. _week : {
  10458. dow : 0, // Sunday is the first day of the week.
  10459. doy : 6 // The week that contains Jan 1st is the first week of the year.
  10460. },
  10461. _invalidDate: 'Invalid date',
  10462. invalidDate: function () {
  10463. return this._invalidDate;
  10464. }
  10465. });
  10466. // Loads a language definition into the `languages` cache. The function
  10467. // takes a key and optionally values. If not in the browser and no values
  10468. // are provided, it will load the language file module. As a convenience,
  10469. // this function also returns the language values.
  10470. function loadLang(key, values) {
  10471. values.abbr = key;
  10472. if (!languages[key]) {
  10473. languages[key] = new Language();
  10474. }
  10475. languages[key].set(values);
  10476. return languages[key];
  10477. }
  10478. // Remove a language from the `languages` cache. Mostly useful in tests.
  10479. function unloadLang(key) {
  10480. delete languages[key];
  10481. }
  10482. // Determines which language definition to use and returns it.
  10483. //
  10484. // With no parameters, it will return the global language. If you
  10485. // pass in a language key, such as 'en', it will return the
  10486. // definition for 'en', so long as 'en' has already been loaded using
  10487. // moment.lang.
  10488. function getLangDefinition(key) {
  10489. var i = 0, j, lang, next, split,
  10490. get = function (k) {
  10491. if (!languages[k] && hasModule) {
  10492. try {
  10493. require('./lang/' + k);
  10494. } catch (e) { }
  10495. }
  10496. return languages[k];
  10497. };
  10498. if (!key) {
  10499. return moment.fn._lang;
  10500. }
  10501. if (!isArray(key)) {
  10502. //short-circuit everything else
  10503. lang = get(key);
  10504. if (lang) {
  10505. return lang;
  10506. }
  10507. key = [key];
  10508. }
  10509. //pick the language from the array
  10510. //try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
  10511. //substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
  10512. while (i < key.length) {
  10513. split = normalizeLanguage(key[i]).split('-');
  10514. j = split.length;
  10515. next = normalizeLanguage(key[i + 1]);
  10516. next = next ? next.split('-') : null;
  10517. while (j > 0) {
  10518. lang = get(split.slice(0, j).join('-'));
  10519. if (lang) {
  10520. return lang;
  10521. }
  10522. if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
  10523. //the next array item is better than a shallower substring of this one
  10524. break;
  10525. }
  10526. j--;
  10527. }
  10528. i++;
  10529. }
  10530. return moment.fn._lang;
  10531. }
  10532. /************************************
  10533. Formatting
  10534. ************************************/
  10535. function removeFormattingTokens(input) {
  10536. if (input.match(/\[[\s\S]/)) {
  10537. return input.replace(/^\[|\]$/g, "");
  10538. }
  10539. return input.replace(/\\/g, "");
  10540. }
  10541. function makeFormatFunction(format) {
  10542. var array = format.match(formattingTokens), i, length;
  10543. for (i = 0, length = array.length; i < length; i++) {
  10544. if (formatTokenFunctions[array[i]]) {
  10545. array[i] = formatTokenFunctions[array[i]];
  10546. } else {
  10547. array[i] = removeFormattingTokens(array[i]);
  10548. }
  10549. }
  10550. return function (mom) {
  10551. var output = "";
  10552. for (i = 0; i < length; i++) {
  10553. output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
  10554. }
  10555. return output;
  10556. };
  10557. }
  10558. // format date using native date object
  10559. function formatMoment(m, format) {
  10560. if (!m.isValid()) {
  10561. return m.lang().invalidDate();
  10562. }
  10563. format = expandFormat(format, m.lang());
  10564. if (!formatFunctions[format]) {
  10565. formatFunctions[format] = makeFormatFunction(format);
  10566. }
  10567. return formatFunctions[format](m);
  10568. }
  10569. function expandFormat(format, lang) {
  10570. var i = 5;
  10571. function replaceLongDateFormatTokens(input) {
  10572. return lang.longDateFormat(input) || input;
  10573. }
  10574. localFormattingTokens.lastIndex = 0;
  10575. while (i >= 0 && localFormattingTokens.test(format)) {
  10576. format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
  10577. localFormattingTokens.lastIndex = 0;
  10578. i -= 1;
  10579. }
  10580. return format;
  10581. }
  10582. /************************************
  10583. Parsing
  10584. ************************************/
  10585. // get the regex to find the next token
  10586. function getParseRegexForToken(token, config) {
  10587. var a, strict = config._strict;
  10588. switch (token) {
  10589. case 'Q':
  10590. return parseTokenOneDigit;
  10591. case 'DDDD':
  10592. return parseTokenThreeDigits;
  10593. case 'YYYY':
  10594. case 'GGGG':
  10595. case 'gggg':
  10596. return strict ? parseTokenFourDigits : parseTokenOneToFourDigits;
  10597. case 'Y':
  10598. case 'G':
  10599. case 'g':
  10600. return parseTokenSignedNumber;
  10601. case 'YYYYYY':
  10602. case 'YYYYY':
  10603. case 'GGGGG':
  10604. case 'ggggg':
  10605. return strict ? parseTokenSixDigits : parseTokenOneToSixDigits;
  10606. case 'S':
  10607. if (strict) { return parseTokenOneDigit; }
  10608. /* falls through */
  10609. case 'SS':
  10610. if (strict) { return parseTokenTwoDigits; }
  10611. /* falls through */
  10612. case 'SSS':
  10613. if (strict) { return parseTokenThreeDigits; }
  10614. /* falls through */
  10615. case 'DDD':
  10616. return parseTokenOneToThreeDigits;
  10617. case 'MMM':
  10618. case 'MMMM':
  10619. case 'dd':
  10620. case 'ddd':
  10621. case 'dddd':
  10622. return parseTokenWord;
  10623. case 'a':
  10624. case 'A':
  10625. return getLangDefinition(config._l)._meridiemParse;
  10626. case 'X':
  10627. return parseTokenTimestampMs;
  10628. case 'Z':
  10629. case 'ZZ':
  10630. return parseTokenTimezone;
  10631. case 'T':
  10632. return parseTokenT;
  10633. case 'SSSS':
  10634. return parseTokenDigits;
  10635. case 'MM':
  10636. case 'DD':
  10637. case 'YY':
  10638. case 'GG':
  10639. case 'gg':
  10640. case 'HH':
  10641. case 'hh':
  10642. case 'mm':
  10643. case 'ss':
  10644. case 'ww':
  10645. case 'WW':
  10646. return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits;
  10647. case 'M':
  10648. case 'D':
  10649. case 'd':
  10650. case 'H':
  10651. case 'h':
  10652. case 'm':
  10653. case 's':
  10654. case 'w':
  10655. case 'W':
  10656. case 'e':
  10657. case 'E':
  10658. return parseTokenOneOrTwoDigits;
  10659. case 'Do':
  10660. return parseTokenOrdinal;
  10661. default :
  10662. a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), "i"));
  10663. return a;
  10664. }
  10665. }
  10666. function timezoneMinutesFromString(string) {
  10667. string = string || "";
  10668. var possibleTzMatches = (string.match(parseTokenTimezone) || []),
  10669. tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [],
  10670. parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0],
  10671. minutes = +(parts[1] * 60) + toInt(parts[2]);
  10672. return parts[0] === '+' ? -minutes : minutes;
  10673. }
  10674. // function to convert string input to date
  10675. function addTimeToArrayFromToken(token, input, config) {
  10676. var a, datePartArray = config._a;
  10677. switch (token) {
  10678. // QUARTER
  10679. case 'Q':
  10680. if (input != null) {
  10681. datePartArray[MONTH] = (toInt(input) - 1) * 3;
  10682. }
  10683. break;
  10684. // MONTH
  10685. case 'M' : // fall through to MM
  10686. case 'MM' :
  10687. if (input != null) {
  10688. datePartArray[MONTH] = toInt(input) - 1;
  10689. }
  10690. break;
  10691. case 'MMM' : // fall through to MMMM
  10692. case 'MMMM' :
  10693. a = getLangDefinition(config._l).monthsParse(input);
  10694. // if we didn't find a month name, mark the date as invalid.
  10695. if (a != null) {
  10696. datePartArray[MONTH] = a;
  10697. } else {
  10698. config._pf.invalidMonth = input;
  10699. }
  10700. break;
  10701. // DAY OF MONTH
  10702. case 'D' : // fall through to DD
  10703. case 'DD' :
  10704. if (input != null) {
  10705. datePartArray[DATE] = toInt(input);
  10706. }
  10707. break;
  10708. case 'Do' :
  10709. if (input != null) {
  10710. datePartArray[DATE] = toInt(parseInt(input, 10));
  10711. }
  10712. break;
  10713. // DAY OF YEAR
  10714. case 'DDD' : // fall through to DDDD
  10715. case 'DDDD' :
  10716. if (input != null) {
  10717. config._dayOfYear = toInt(input);
  10718. }
  10719. break;
  10720. // YEAR
  10721. case 'YY' :
  10722. datePartArray[YEAR] = moment.parseTwoDigitYear(input);
  10723. break;
  10724. case 'YYYY' :
  10725. case 'YYYYY' :
  10726. case 'YYYYYY' :
  10727. datePartArray[YEAR] = toInt(input);
  10728. break;
  10729. // AM / PM
  10730. case 'a' : // fall through to A
  10731. case 'A' :
  10732. config._isPm = getLangDefinition(config._l).isPM(input);
  10733. break;
  10734. // 24 HOUR
  10735. case 'H' : // fall through to hh
  10736. case 'HH' : // fall through to hh
  10737. case 'h' : // fall through to hh
  10738. case 'hh' :
  10739. datePartArray[HOUR] = toInt(input);
  10740. break;
  10741. // MINUTE
  10742. case 'm' : // fall through to mm
  10743. case 'mm' :
  10744. datePartArray[MINUTE] = toInt(input);
  10745. break;
  10746. // SECOND
  10747. case 's' : // fall through to ss
  10748. case 'ss' :
  10749. datePartArray[SECOND] = toInt(input);
  10750. break;
  10751. // MILLISECOND
  10752. case 'S' :
  10753. case 'SS' :
  10754. case 'SSS' :
  10755. case 'SSSS' :
  10756. datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000);
  10757. break;
  10758. // UNIX TIMESTAMP WITH MS
  10759. case 'X':
  10760. config._d = new Date(parseFloat(input) * 1000);
  10761. break;
  10762. // TIMEZONE
  10763. case 'Z' : // fall through to ZZ
  10764. case 'ZZ' :
  10765. config._useUTC = true;
  10766. config._tzm = timezoneMinutesFromString(input);
  10767. break;
  10768. case 'w':
  10769. case 'ww':
  10770. case 'W':
  10771. case 'WW':
  10772. case 'd':
  10773. case 'dd':
  10774. case 'ddd':
  10775. case 'dddd':
  10776. case 'e':
  10777. case 'E':
  10778. token = token.substr(0, 1);
  10779. /* falls through */
  10780. case 'gg':
  10781. case 'gggg':
  10782. case 'GG':
  10783. case 'GGGG':
  10784. case 'GGGGG':
  10785. token = token.substr(0, 2);
  10786. if (input) {
  10787. config._w = config._w || {};
  10788. config._w[token] = input;
  10789. }
  10790. break;
  10791. }
  10792. }
  10793. // convert an array to a date.
  10794. // the array should mirror the parameters below
  10795. // note: all values past the year are optional and will default to the lowest possible value.
  10796. // [year, month, day , hour, minute, second, millisecond]
  10797. function dateFromConfig(config) {
  10798. var i, date, input = [], currentDate,
  10799. yearToUse, fixYear, w, temp, lang, weekday, week;
  10800. if (config._d) {
  10801. return;
  10802. }
  10803. currentDate = currentDateArray(config);
  10804. //compute day of the year from weeks and weekdays
  10805. if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
  10806. fixYear = function (val) {
  10807. var intVal = parseInt(val, 10);
  10808. return val ?
  10809. (val.length < 3 ? (intVal > 68 ? 1900 + intVal : 2000 + intVal) : intVal) :
  10810. (config._a[YEAR] == null ? moment().weekYear() : config._a[YEAR]);
  10811. };
  10812. w = config._w;
  10813. if (w.GG != null || w.W != null || w.E != null) {
  10814. temp = dayOfYearFromWeeks(fixYear(w.GG), w.W || 1, w.E, 4, 1);
  10815. }
  10816. else {
  10817. lang = getLangDefinition(config._l);
  10818. weekday = w.d != null ? parseWeekday(w.d, lang) :
  10819. (w.e != null ? parseInt(w.e, 10) + lang._week.dow : 0);
  10820. week = parseInt(w.w, 10) || 1;
  10821. //if we're parsing 'd', then the low day numbers may be next week
  10822. if (w.d != null && weekday < lang._week.dow) {
  10823. week++;
  10824. }
  10825. temp = dayOfYearFromWeeks(fixYear(w.gg), week, weekday, lang._week.doy, lang._week.dow);
  10826. }
  10827. config._a[YEAR] = temp.year;
  10828. config._dayOfYear = temp.dayOfYear;
  10829. }
  10830. //if the day of the year is set, figure out what it is
  10831. if (config._dayOfYear) {
  10832. yearToUse = config._a[YEAR] == null ? currentDate[YEAR] : config._a[YEAR];
  10833. if (config._dayOfYear > daysInYear(yearToUse)) {
  10834. config._pf._overflowDayOfYear = true;
  10835. }
  10836. date = makeUTCDate(yearToUse, 0, config._dayOfYear);
  10837. config._a[MONTH] = date.getUTCMonth();
  10838. config._a[DATE] = date.getUTCDate();
  10839. }
  10840. // Default to current date.
  10841. // * if no year, month, day of month are given, default to today
  10842. // * if day of month is given, default month and year
  10843. // * if month is given, default only year
  10844. // * if year is given, don't default anything
  10845. for (i = 0; i < 3 && config._a[i] == null; ++i) {
  10846. config._a[i] = input[i] = currentDate[i];
  10847. }
  10848. // Zero out whatever was not defaulted, including time
  10849. for (; i < 7; i++) {
  10850. config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
  10851. }
  10852. // add the offsets to the time to be parsed so that we can have a clean array for checking isValid
  10853. input[HOUR] += toInt((config._tzm || 0) / 60);
  10854. input[MINUTE] += toInt((config._tzm || 0) % 60);
  10855. config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input);
  10856. }
  10857. function dateFromObject(config) {
  10858. var normalizedInput;
  10859. if (config._d) {
  10860. return;
  10861. }
  10862. normalizedInput = normalizeObjectUnits(config._i);
  10863. config._a = [
  10864. normalizedInput.year,
  10865. normalizedInput.month,
  10866. normalizedInput.day,
  10867. normalizedInput.hour,
  10868. normalizedInput.minute,
  10869. normalizedInput.second,
  10870. normalizedInput.millisecond
  10871. ];
  10872. dateFromConfig(config);
  10873. }
  10874. function currentDateArray(config) {
  10875. var now = new Date();
  10876. if (config._useUTC) {
  10877. return [
  10878. now.getUTCFullYear(),
  10879. now.getUTCMonth(),
  10880. now.getUTCDate()
  10881. ];
  10882. } else {
  10883. return [now.getFullYear(), now.getMonth(), now.getDate()];
  10884. }
  10885. }
  10886. // date from string and format string
  10887. function makeDateFromStringAndFormat(config) {
  10888. config._a = [];
  10889. config._pf.empty = true;
  10890. // This array is used to make a Date, either with `new Date` or `Date.UTC`
  10891. var lang = getLangDefinition(config._l),
  10892. string = '' + config._i,
  10893. i, parsedInput, tokens, token, skipped,
  10894. stringLength = string.length,
  10895. totalParsedInputLength = 0;
  10896. tokens = expandFormat(config._f, lang).match(formattingTokens) || [];
  10897. for (i = 0; i < tokens.length; i++) {
  10898. token = tokens[i];
  10899. parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
  10900. if (parsedInput) {
  10901. skipped = string.substr(0, string.indexOf(parsedInput));
  10902. if (skipped.length > 0) {
  10903. config._pf.unusedInput.push(skipped);
  10904. }
  10905. string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
  10906. totalParsedInputLength += parsedInput.length;
  10907. }
  10908. // don't parse if it's not a known token
  10909. if (formatTokenFunctions[token]) {
  10910. if (parsedInput) {
  10911. config._pf.empty = false;
  10912. }
  10913. else {
  10914. config._pf.unusedTokens.push(token);
  10915. }
  10916. addTimeToArrayFromToken(token, parsedInput, config);
  10917. }
  10918. else if (config._strict && !parsedInput) {
  10919. config._pf.unusedTokens.push(token);
  10920. }
  10921. }
  10922. // add remaining unparsed input length to the string
  10923. config._pf.charsLeftOver = stringLength - totalParsedInputLength;
  10924. if (string.length > 0) {
  10925. config._pf.unusedInput.push(string);
  10926. }
  10927. // handle am pm
  10928. if (config._isPm && config._a[HOUR] < 12) {
  10929. config._a[HOUR] += 12;
  10930. }
  10931. // if is 12 am, change hours to 0
  10932. if (config._isPm === false && config._a[HOUR] === 12) {
  10933. config._a[HOUR] = 0;
  10934. }
  10935. dateFromConfig(config);
  10936. checkOverflow(config);
  10937. }
  10938. function unescapeFormat(s) {
  10939. return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
  10940. return p1 || p2 || p3 || p4;
  10941. });
  10942. }
  10943. // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
  10944. function regexpEscape(s) {
  10945. return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
  10946. }
  10947. // date from string and array of format strings
  10948. function makeDateFromStringAndArray(config) {
  10949. var tempConfig,
  10950. bestMoment,
  10951. scoreToBeat,
  10952. i,
  10953. currentScore;
  10954. if (config._f.length === 0) {
  10955. config._pf.invalidFormat = true;
  10956. config._d = new Date(NaN);
  10957. return;
  10958. }
  10959. for (i = 0; i < config._f.length; i++) {
  10960. currentScore = 0;
  10961. tempConfig = extend({}, config);
  10962. tempConfig._pf = defaultParsingFlags();
  10963. tempConfig._f = config._f[i];
  10964. makeDateFromStringAndFormat(tempConfig);
  10965. if (!isValid(tempConfig)) {
  10966. continue;
  10967. }
  10968. // if there is any input that was not parsed add a penalty for that format
  10969. currentScore += tempConfig._pf.charsLeftOver;
  10970. //or tokens
  10971. currentScore += tempConfig._pf.unusedTokens.length * 10;
  10972. tempConfig._pf.score = currentScore;
  10973. if (scoreToBeat == null || currentScore < scoreToBeat) {
  10974. scoreToBeat = currentScore;
  10975. bestMoment = tempConfig;
  10976. }
  10977. }
  10978. extend(config, bestMoment || tempConfig);
  10979. }
  10980. // date from iso format
  10981. function makeDateFromString(config) {
  10982. var i, l,
  10983. string = config._i,
  10984. match = isoRegex.exec(string);
  10985. if (match) {
  10986. config._pf.iso = true;
  10987. for (i = 0, l = isoDates.length; i < l; i++) {
  10988. if (isoDates[i][1].exec(string)) {
  10989. // match[5] should be "T" or undefined
  10990. config._f = isoDates[i][0] + (match[6] || " ");
  10991. break;
  10992. }
  10993. }
  10994. for (i = 0, l = isoTimes.length; i < l; i++) {
  10995. if (isoTimes[i][1].exec(string)) {
  10996. config._f += isoTimes[i][0];
  10997. break;
  10998. }
  10999. }
  11000. if (string.match(parseTokenTimezone)) {
  11001. config._f += "Z";
  11002. }
  11003. makeDateFromStringAndFormat(config);
  11004. }
  11005. else {
  11006. moment.createFromInputFallback(config);
  11007. }
  11008. }
  11009. function makeDateFromInput(config) {
  11010. var input = config._i,
  11011. matched = aspNetJsonRegex.exec(input);
  11012. if (input === undefined) {
  11013. config._d = new Date();
  11014. } else if (matched) {
  11015. config._d = new Date(+matched[1]);
  11016. } else if (typeof input === 'string') {
  11017. makeDateFromString(config);
  11018. } else if (isArray(input)) {
  11019. config._a = input.slice(0);
  11020. dateFromConfig(config);
  11021. } else if (isDate(input)) {
  11022. config._d = new Date(+input);
  11023. } else if (typeof(input) === 'object') {
  11024. dateFromObject(config);
  11025. } else if (typeof(input) === 'number') {
  11026. // from milliseconds
  11027. config._d = new Date(input);
  11028. } else {
  11029. moment.createFromInputFallback(config);
  11030. }
  11031. }
  11032. function makeDate(y, m, d, h, M, s, ms) {
  11033. //can't just apply() to create a date:
  11034. //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
  11035. var date = new Date(y, m, d, h, M, s, ms);
  11036. //the date constructor doesn't accept years < 1970
  11037. if (y < 1970) {
  11038. date.setFullYear(y);
  11039. }
  11040. return date;
  11041. }
  11042. function makeUTCDate(y) {
  11043. var date = new Date(Date.UTC.apply(null, arguments));
  11044. if (y < 1970) {
  11045. date.setUTCFullYear(y);
  11046. }
  11047. return date;
  11048. }
  11049. function parseWeekday(input, language) {
  11050. if (typeof input === 'string') {
  11051. if (!isNaN(input)) {
  11052. input = parseInt(input, 10);
  11053. }
  11054. else {
  11055. input = language.weekdaysParse(input);
  11056. if (typeof input !== 'number') {
  11057. return null;
  11058. }
  11059. }
  11060. }
  11061. return input;
  11062. }
  11063. /************************************
  11064. Relative Time
  11065. ************************************/
  11066. // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
  11067. function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) {
  11068. return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
  11069. }
  11070. function relativeTime(milliseconds, withoutSuffix, lang) {
  11071. var seconds = round(Math.abs(milliseconds) / 1000),
  11072. minutes = round(seconds / 60),
  11073. hours = round(minutes / 60),
  11074. days = round(hours / 24),
  11075. years = round(days / 365),
  11076. args = seconds < 45 && ['s', seconds] ||
  11077. minutes === 1 && ['m'] ||
  11078. minutes < 45 && ['mm', minutes] ||
  11079. hours === 1 && ['h'] ||
  11080. hours < 22 && ['hh', hours] ||
  11081. days === 1 && ['d'] ||
  11082. days <= 25 && ['dd', days] ||
  11083. days <= 45 && ['M'] ||
  11084. days < 345 && ['MM', round(days / 30)] ||
  11085. years === 1 && ['y'] || ['yy', years];
  11086. args[2] = withoutSuffix;
  11087. args[3] = milliseconds > 0;
  11088. args[4] = lang;
  11089. return substituteTimeAgo.apply({}, args);
  11090. }
  11091. /************************************
  11092. Week of Year
  11093. ************************************/
  11094. // firstDayOfWeek 0 = sun, 6 = sat
  11095. // the day of the week that starts the week
  11096. // (usually sunday or monday)
  11097. // firstDayOfWeekOfYear 0 = sun, 6 = sat
  11098. // the first week is the week that contains the first
  11099. // of this day of the week
  11100. // (eg. ISO weeks use thursday (4))
  11101. function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) {
  11102. var end = firstDayOfWeekOfYear - firstDayOfWeek,
  11103. daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(),
  11104. adjustedMoment;
  11105. if (daysToDayOfWeek > end) {
  11106. daysToDayOfWeek -= 7;
  11107. }
  11108. if (daysToDayOfWeek < end - 7) {
  11109. daysToDayOfWeek += 7;
  11110. }
  11111. adjustedMoment = moment(mom).add('d', daysToDayOfWeek);
  11112. return {
  11113. week: Math.ceil(adjustedMoment.dayOfYear() / 7),
  11114. year: adjustedMoment.year()
  11115. };
  11116. }
  11117. //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
  11118. function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) {
  11119. var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear;
  11120. weekday = weekday != null ? weekday : firstDayOfWeek;
  11121. daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0);
  11122. dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1;
  11123. return {
  11124. year: dayOfYear > 0 ? year : year - 1,
  11125. dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear
  11126. };
  11127. }
  11128. /************************************
  11129. Top Level Functions
  11130. ************************************/
  11131. function makeMoment(config) {
  11132. var input = config._i,
  11133. format = config._f;
  11134. if (input === null || (format === undefined && input === '')) {
  11135. return moment.invalid({nullInput: true});
  11136. }
  11137. if (typeof input === 'string') {
  11138. config._i = input = getLangDefinition().preparse(input);
  11139. }
  11140. if (moment.isMoment(input)) {
  11141. config = cloneMoment(input);
  11142. config._d = new Date(+input._d);
  11143. } else if (format) {
  11144. if (isArray(format)) {
  11145. makeDateFromStringAndArray(config);
  11146. } else {
  11147. makeDateFromStringAndFormat(config);
  11148. }
  11149. } else {
  11150. makeDateFromInput(config);
  11151. }
  11152. return new Moment(config);
  11153. }
  11154. moment = function (input, format, lang, strict) {
  11155. var c;
  11156. if (typeof(lang) === "boolean") {
  11157. strict = lang;
  11158. lang = undefined;
  11159. }
  11160. // object construction must be done this way.
  11161. // https://github.com/moment/moment/issues/1423
  11162. c = {};
  11163. c._isAMomentObject = true;
  11164. c._i = input;
  11165. c._f = format;
  11166. c._l = lang;
  11167. c._strict = strict;
  11168. c._isUTC = false;
  11169. c._pf = defaultParsingFlags();
  11170. return makeMoment(c);
  11171. };
  11172. moment.suppressDeprecationWarnings = false;
  11173. moment.createFromInputFallback = deprecate(
  11174. "moment construction falls back to js Date. This is " +
  11175. "discouraged and will be removed in upcoming major " +
  11176. "release. Please refer to " +
  11177. "https://github.com/moment/moment/issues/1407 for more info.",
  11178. function (config) {
  11179. config._d = new Date(config._i);
  11180. });
  11181. // creating with utc
  11182. moment.utc = function (input, format, lang, strict) {
  11183. var c;
  11184. if (typeof(lang) === "boolean") {
  11185. strict = lang;
  11186. lang = undefined;
  11187. }
  11188. // object construction must be done this way.
  11189. // https://github.com/moment/moment/issues/1423
  11190. c = {};
  11191. c._isAMomentObject = true;
  11192. c._useUTC = true;
  11193. c._isUTC = true;
  11194. c._l = lang;
  11195. c._i = input;
  11196. c._f = format;
  11197. c._strict = strict;
  11198. c._pf = defaultParsingFlags();
  11199. return makeMoment(c).utc();
  11200. };
  11201. // creating with unix timestamp (in seconds)
  11202. moment.unix = function (input) {
  11203. return moment(input * 1000);
  11204. };
  11205. // duration
  11206. moment.duration = function (input, key) {
  11207. var duration = input,
  11208. // matching against regexp is expensive, do it on demand
  11209. match = null,
  11210. sign,
  11211. ret,
  11212. parseIso;
  11213. if (moment.isDuration(input)) {
  11214. duration = {
  11215. ms: input._milliseconds,
  11216. d: input._days,
  11217. M: input._months
  11218. };
  11219. } else if (typeof input === 'number') {
  11220. duration = {};
  11221. if (key) {
  11222. duration[key] = input;
  11223. } else {
  11224. duration.milliseconds = input;
  11225. }
  11226. } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) {
  11227. sign = (match[1] === "-") ? -1 : 1;
  11228. duration = {
  11229. y: 0,
  11230. d: toInt(match[DATE]) * sign,
  11231. h: toInt(match[HOUR]) * sign,
  11232. m: toInt(match[MINUTE]) * sign,
  11233. s: toInt(match[SECOND]) * sign,
  11234. ms: toInt(match[MILLISECOND]) * sign
  11235. };
  11236. } else if (!!(match = isoDurationRegex.exec(input))) {
  11237. sign = (match[1] === "-") ? -1 : 1;
  11238. parseIso = function (inp) {
  11239. // We'd normally use ~~inp for this, but unfortunately it also
  11240. // converts floats to ints.
  11241. // inp may be undefined, so careful calling replace on it.
  11242. var res = inp && parseFloat(inp.replace(',', '.'));
  11243. // apply sign while we're at it
  11244. return (isNaN(res) ? 0 : res) * sign;
  11245. };
  11246. duration = {
  11247. y: parseIso(match[2]),
  11248. M: parseIso(match[3]),
  11249. d: parseIso(match[4]),
  11250. h: parseIso(match[5]),
  11251. m: parseIso(match[6]),
  11252. s: parseIso(match[7]),
  11253. w: parseIso(match[8])
  11254. };
  11255. }
  11256. ret = new Duration(duration);
  11257. if (moment.isDuration(input) && input.hasOwnProperty('_lang')) {
  11258. ret._lang = input._lang;
  11259. }
  11260. return ret;
  11261. };
  11262. // version number
  11263. moment.version = VERSION;
  11264. // default format
  11265. moment.defaultFormat = isoFormat;
  11266. // Plugins that add properties should also add the key here (null value),
  11267. // so we can properly clone ourselves.
  11268. moment.momentProperties = momentProperties;
  11269. // This function will be called whenever a moment is mutated.
  11270. // It is intended to keep the offset in sync with the timezone.
  11271. moment.updateOffset = function () {};
  11272. // This function will load languages and then set the global language. If
  11273. // no arguments are passed in, it will simply return the current global
  11274. // language key.
  11275. moment.lang = function (key, values) {
  11276. var r;
  11277. if (!key) {
  11278. return moment.fn._lang._abbr;
  11279. }
  11280. if (values) {
  11281. loadLang(normalizeLanguage(key), values);
  11282. } else if (values === null) {
  11283. unloadLang(key);
  11284. key = 'en';
  11285. } else if (!languages[key]) {
  11286. getLangDefinition(key);
  11287. }
  11288. r = moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key);
  11289. return r._abbr;
  11290. };
  11291. // returns language data
  11292. moment.langData = function (key) {
  11293. if (key && key._lang && key._lang._abbr) {
  11294. key = key._lang._abbr;
  11295. }
  11296. return getLangDefinition(key);
  11297. };
  11298. // compare moment object
  11299. moment.isMoment = function (obj) {
  11300. return obj instanceof Moment ||
  11301. (obj != null && obj.hasOwnProperty('_isAMomentObject'));
  11302. };
  11303. // for typechecking Duration objects
  11304. moment.isDuration = function (obj) {
  11305. return obj instanceof Duration;
  11306. };
  11307. for (i = lists.length - 1; i >= 0; --i) {
  11308. makeList(lists[i]);
  11309. }
  11310. moment.normalizeUnits = function (units) {
  11311. return normalizeUnits(units);
  11312. };
  11313. moment.invalid = function (flags) {
  11314. var m = moment.utc(NaN);
  11315. if (flags != null) {
  11316. extend(m._pf, flags);
  11317. }
  11318. else {
  11319. m._pf.userInvalidated = true;
  11320. }
  11321. return m;
  11322. };
  11323. moment.parseZone = function () {
  11324. return moment.apply(null, arguments).parseZone();
  11325. };
  11326. moment.parseTwoDigitYear = function (input) {
  11327. return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
  11328. };
  11329. /************************************
  11330. Moment Prototype
  11331. ************************************/
  11332. extend(moment.fn = Moment.prototype, {
  11333. clone : function () {
  11334. return moment(this);
  11335. },
  11336. valueOf : function () {
  11337. return +this._d + ((this._offset || 0) * 60000);
  11338. },
  11339. unix : function () {
  11340. return Math.floor(+this / 1000);
  11341. },
  11342. toString : function () {
  11343. return this.clone().lang('en').format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ");
  11344. },
  11345. toDate : function () {
  11346. return this._offset ? new Date(+this) : this._d;
  11347. },
  11348. toISOString : function () {
  11349. var m = moment(this).utc();
  11350. if (0 < m.year() && m.year() <= 9999) {
  11351. return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
  11352. } else {
  11353. return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
  11354. }
  11355. },
  11356. toArray : function () {
  11357. var m = this;
  11358. return [
  11359. m.year(),
  11360. m.month(),
  11361. m.date(),
  11362. m.hours(),
  11363. m.minutes(),
  11364. m.seconds(),
  11365. m.milliseconds()
  11366. ];
  11367. },
  11368. isValid : function () {
  11369. return isValid(this);
  11370. },
  11371. isDSTShifted : function () {
  11372. if (this._a) {
  11373. return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0;
  11374. }
  11375. return false;
  11376. },
  11377. parsingFlags : function () {
  11378. return extend({}, this._pf);
  11379. },
  11380. invalidAt: function () {
  11381. return this._pf.overflow;
  11382. },
  11383. utc : function () {
  11384. return this.zone(0);
  11385. },
  11386. local : function () {
  11387. this.zone(0);
  11388. this._isUTC = false;
  11389. return this;
  11390. },
  11391. format : function (inputString) {
  11392. var output = formatMoment(this, inputString || moment.defaultFormat);
  11393. return this.lang().postformat(output);
  11394. },
  11395. add : function (input, val) {
  11396. var dur;
  11397. // switch args to support add('s', 1) and add(1, 's')
  11398. if (typeof input === 'string') {
  11399. dur = moment.duration(+val, input);
  11400. } else {
  11401. dur = moment.duration(input, val);
  11402. }
  11403. addOrSubtractDurationFromMoment(this, dur, 1);
  11404. return this;
  11405. },
  11406. subtract : function (input, val) {
  11407. var dur;
  11408. // switch args to support subtract('s', 1) and subtract(1, 's')
  11409. if (typeof input === 'string') {
  11410. dur = moment.duration(+val, input);
  11411. } else {
  11412. dur = moment.duration(input, val);
  11413. }
  11414. addOrSubtractDurationFromMoment(this, dur, -1);
  11415. return this;
  11416. },
  11417. diff : function (input, units, asFloat) {
  11418. var that = makeAs(input, this),
  11419. zoneDiff = (this.zone() - that.zone()) * 6e4,
  11420. diff, output;
  11421. units = normalizeUnits(units);
  11422. if (units === 'year' || units === 'month') {
  11423. // average number of days in the months in the given dates
  11424. diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2
  11425. // difference in months
  11426. output = ((this.year() - that.year()) * 12) + (this.month() - that.month());
  11427. // adjust by taking difference in days, average number of days
  11428. // and dst in the given months.
  11429. output += ((this - moment(this).startOf('month')) -
  11430. (that - moment(that).startOf('month'))) / diff;
  11431. // same as above but with zones, to negate all dst
  11432. output -= ((this.zone() - moment(this).startOf('month').zone()) -
  11433. (that.zone() - moment(that).startOf('month').zone())) * 6e4 / diff;
  11434. if (units === 'year') {
  11435. output = output / 12;
  11436. }
  11437. } else {
  11438. diff = (this - that);
  11439. output = units === 'second' ? diff / 1e3 : // 1000
  11440. units === 'minute' ? diff / 6e4 : // 1000 * 60
  11441. units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60
  11442. units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
  11443. units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
  11444. diff;
  11445. }
  11446. return asFloat ? output : absRound(output);
  11447. },
  11448. from : function (time, withoutSuffix) {
  11449. return moment.duration(this.diff(time)).lang(this.lang()._abbr).humanize(!withoutSuffix);
  11450. },
  11451. fromNow : function (withoutSuffix) {
  11452. return this.from(moment(), withoutSuffix);
  11453. },
  11454. calendar : function () {
  11455. // We want to compare the start of today, vs this.
  11456. // Getting start-of-today depends on whether we're zone'd or not.
  11457. var sod = makeAs(moment(), this).startOf('day'),
  11458. diff = this.diff(sod, 'days', true),
  11459. format = diff < -6 ? 'sameElse' :
  11460. diff < -1 ? 'lastWeek' :
  11461. diff < 0 ? 'lastDay' :
  11462. diff < 1 ? 'sameDay' :
  11463. diff < 2 ? 'nextDay' :
  11464. diff < 7 ? 'nextWeek' : 'sameElse';
  11465. return this.format(this.lang().calendar(format, this));
  11466. },
  11467. isLeapYear : function () {
  11468. return isLeapYear(this.year());
  11469. },
  11470. isDST : function () {
  11471. return (this.zone() < this.clone().month(0).zone() ||
  11472. this.zone() < this.clone().month(5).zone());
  11473. },
  11474. day : function (input) {
  11475. var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
  11476. if (input != null) {
  11477. input = parseWeekday(input, this.lang());
  11478. return this.add({ d : input - day });
  11479. } else {
  11480. return day;
  11481. }
  11482. },
  11483. month : makeAccessor('Month', true),
  11484. startOf: function (units) {
  11485. units = normalizeUnits(units);
  11486. // the following switch intentionally omits break keywords
  11487. // to utilize falling through the cases.
  11488. switch (units) {
  11489. case 'year':
  11490. this.month(0);
  11491. /* falls through */
  11492. case 'quarter':
  11493. case 'month':
  11494. this.date(1);
  11495. /* falls through */
  11496. case 'week':
  11497. case 'isoWeek':
  11498. case 'day':
  11499. this.hours(0);
  11500. /* falls through */
  11501. case 'hour':
  11502. this.minutes(0);
  11503. /* falls through */
  11504. case 'minute':
  11505. this.seconds(0);
  11506. /* falls through */
  11507. case 'second':
  11508. this.milliseconds(0);
  11509. /* falls through */
  11510. }
  11511. // weeks are a special case
  11512. if (units === 'week') {
  11513. this.weekday(0);
  11514. } else if (units === 'isoWeek') {
  11515. this.isoWeekday(1);
  11516. }
  11517. // quarters are also special
  11518. if (units === 'quarter') {
  11519. this.month(Math.floor(this.month() / 3) * 3);
  11520. }
  11521. return this;
  11522. },
  11523. endOf: function (units) {
  11524. units = normalizeUnits(units);
  11525. return this.startOf(units).add((units === 'isoWeek' ? 'week' : units), 1).subtract('ms', 1);
  11526. },
  11527. isAfter: function (input, units) {
  11528. units = typeof units !== 'undefined' ? units : 'millisecond';
  11529. return +this.clone().startOf(units) > +moment(input).startOf(units);
  11530. },
  11531. isBefore: function (input, units) {
  11532. units = typeof units !== 'undefined' ? units : 'millisecond';
  11533. return +this.clone().startOf(units) < +moment(input).startOf(units);
  11534. },
  11535. isSame: function (input, units) {
  11536. units = units || 'ms';
  11537. return +this.clone().startOf(units) === +makeAs(input, this).startOf(units);
  11538. },
  11539. min: function (other) {
  11540. other = moment.apply(null, arguments);
  11541. return other < this ? this : other;
  11542. },
  11543. max: function (other) {
  11544. other = moment.apply(null, arguments);
  11545. return other > this ? this : other;
  11546. },
  11547. // keepTime = true means only change the timezone, without affecting
  11548. // the local hour. So 5:31:26 +0300 --[zone(2, true)]--> 5:31:26 +0200
  11549. // It is possible that 5:31:26 doesn't exist int zone +0200, so we
  11550. // adjust the time as needed, to be valid.
  11551. //
  11552. // Keeping the time actually adds/subtracts (one hour)
  11553. // from the actual represented time. That is why we call updateOffset
  11554. // a second time. In case it wants us to change the offset again
  11555. // _changeInProgress == true case, then we have to adjust, because
  11556. // there is no such time in the given timezone.
  11557. zone : function (input, keepTime) {
  11558. var offset = this._offset || 0;
  11559. if (input != null) {
  11560. if (typeof input === "string") {
  11561. input = timezoneMinutesFromString(input);
  11562. }
  11563. if (Math.abs(input) < 16) {
  11564. input = input * 60;
  11565. }
  11566. this._offset = input;
  11567. this._isUTC = true;
  11568. if (offset !== input) {
  11569. if (!keepTime || this._changeInProgress) {
  11570. addOrSubtractDurationFromMoment(this,
  11571. moment.duration(offset - input, 'm'), 1, false);
  11572. } else if (!this._changeInProgress) {
  11573. this._changeInProgress = true;
  11574. moment.updateOffset(this, true);
  11575. this._changeInProgress = null;
  11576. }
  11577. }
  11578. } else {
  11579. return this._isUTC ? offset : this._d.getTimezoneOffset();
  11580. }
  11581. return this;
  11582. },
  11583. zoneAbbr : function () {
  11584. return this._isUTC ? "UTC" : "";
  11585. },
  11586. zoneName : function () {
  11587. return this._isUTC ? "Coordinated Universal Time" : "";
  11588. },
  11589. parseZone : function () {
  11590. if (this._tzm) {
  11591. this.zone(this._tzm);
  11592. } else if (typeof this._i === 'string') {
  11593. this.zone(this._i);
  11594. }
  11595. return this;
  11596. },
  11597. hasAlignedHourOffset : function (input) {
  11598. if (!input) {
  11599. input = 0;
  11600. }
  11601. else {
  11602. input = moment(input).zone();
  11603. }
  11604. return (this.zone() - input) % 60 === 0;
  11605. },
  11606. daysInMonth : function () {
  11607. return daysInMonth(this.year(), this.month());
  11608. },
  11609. dayOfYear : function (input) {
  11610. var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1;
  11611. return input == null ? dayOfYear : this.add("d", (input - dayOfYear));
  11612. },
  11613. quarter : function (input) {
  11614. return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
  11615. },
  11616. weekYear : function (input) {
  11617. var year = weekOfYear(this, this.lang()._week.dow, this.lang()._week.doy).year;
  11618. return input == null ? year : this.add("y", (input - year));
  11619. },
  11620. isoWeekYear : function (input) {
  11621. var year = weekOfYear(this, 1, 4).year;
  11622. return input == null ? year : this.add("y", (input - year));
  11623. },
  11624. week : function (input) {
  11625. var week = this.lang().week(this);
  11626. return input == null ? week : this.add("d", (input - week) * 7);
  11627. },
  11628. isoWeek : function (input) {
  11629. var week = weekOfYear(this, 1, 4).week;
  11630. return input == null ? week : this.add("d", (input - week) * 7);
  11631. },
  11632. weekday : function (input) {
  11633. var weekday = (this.day() + 7 - this.lang()._week.dow) % 7;
  11634. return input == null ? weekday : this.add("d", input - weekday);
  11635. },
  11636. isoWeekday : function (input) {
  11637. // behaves the same as moment#day except
  11638. // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
  11639. // as a setter, sunday should belong to the previous week.
  11640. return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7);
  11641. },
  11642. isoWeeksInYear : function () {
  11643. return weeksInYear(this.year(), 1, 4);
  11644. },
  11645. weeksInYear : function () {
  11646. var weekInfo = this._lang._week;
  11647. return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
  11648. },
  11649. get : function (units) {
  11650. units = normalizeUnits(units);
  11651. return this[units]();
  11652. },
  11653. set : function (units, value) {
  11654. units = normalizeUnits(units);
  11655. if (typeof this[units] === 'function') {
  11656. this[units](value);
  11657. }
  11658. return this;
  11659. },
  11660. // If passed a language key, it will set the language for this
  11661. // instance. Otherwise, it will return the language configuration
  11662. // variables for this instance.
  11663. lang : function (key) {
  11664. if (key === undefined) {
  11665. return this._lang;
  11666. } else {
  11667. this._lang = getLangDefinition(key);
  11668. return this;
  11669. }
  11670. }
  11671. });
  11672. function rawMonthSetter(mom, value) {
  11673. var dayOfMonth;
  11674. // TODO: Move this out of here!
  11675. if (typeof value === 'string') {
  11676. value = mom.lang().monthsParse(value);
  11677. // TODO: Another silent failure?
  11678. if (typeof value !== 'number') {
  11679. return mom;
  11680. }
  11681. }
  11682. dayOfMonth = Math.min(mom.date(),
  11683. daysInMonth(mom.year(), value));
  11684. mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
  11685. return mom;
  11686. }
  11687. function rawGetter(mom, unit) {
  11688. return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]();
  11689. }
  11690. function rawSetter(mom, unit, value) {
  11691. if (unit === 'Month') {
  11692. return rawMonthSetter(mom, value);
  11693. } else {
  11694. return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
  11695. }
  11696. }
  11697. function makeAccessor(unit, keepTime) {
  11698. return function (value) {
  11699. if (value != null) {
  11700. rawSetter(this, unit, value);
  11701. moment.updateOffset(this, keepTime);
  11702. return this;
  11703. } else {
  11704. return rawGetter(this, unit);
  11705. }
  11706. };
  11707. }
  11708. moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false);
  11709. moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false);
  11710. moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false);
  11711. // Setting the hour should keep the time, because the user explicitly
  11712. // specified which hour he wants. So trying to maintain the same hour (in
  11713. // a new timezone) makes sense. Adding/subtracting hours does not follow
  11714. // this rule.
  11715. moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true);
  11716. // moment.fn.month is defined separately
  11717. moment.fn.date = makeAccessor('Date', true);
  11718. moment.fn.dates = deprecate("dates accessor is deprecated. Use date instead.", makeAccessor('Date', true));
  11719. moment.fn.year = makeAccessor('FullYear', true);
  11720. moment.fn.years = deprecate("years accessor is deprecated. Use year instead.", makeAccessor('FullYear', true));
  11721. // add plural methods
  11722. moment.fn.days = moment.fn.day;
  11723. moment.fn.months = moment.fn.month;
  11724. moment.fn.weeks = moment.fn.week;
  11725. moment.fn.isoWeeks = moment.fn.isoWeek;
  11726. moment.fn.quarters = moment.fn.quarter;
  11727. // add aliased format methods
  11728. moment.fn.toJSON = moment.fn.toISOString;
  11729. /************************************
  11730. Duration Prototype
  11731. ************************************/
  11732. extend(moment.duration.fn = Duration.prototype, {
  11733. _bubble : function () {
  11734. var milliseconds = this._milliseconds,
  11735. days = this._days,
  11736. months = this._months,
  11737. data = this._data,
  11738. seconds, minutes, hours, years;
  11739. // The following code bubbles up values, see the tests for
  11740. // examples of what that means.
  11741. data.milliseconds = milliseconds % 1000;
  11742. seconds = absRound(milliseconds / 1000);
  11743. data.seconds = seconds % 60;
  11744. minutes = absRound(seconds / 60);
  11745. data.minutes = minutes % 60;
  11746. hours = absRound(minutes / 60);
  11747. data.hours = hours % 24;
  11748. days += absRound(hours / 24);
  11749. data.days = days % 30;
  11750. months += absRound(days / 30);
  11751. data.months = months % 12;
  11752. years = absRound(months / 12);
  11753. data.years = years;
  11754. },
  11755. weeks : function () {
  11756. return absRound(this.days() / 7);
  11757. },
  11758. valueOf : function () {
  11759. return this._milliseconds +
  11760. this._days * 864e5 +
  11761. (this._months % 12) * 2592e6 +
  11762. toInt(this._months / 12) * 31536e6;
  11763. },
  11764. humanize : function (withSuffix) {
  11765. var difference = +this,
  11766. output = relativeTime(difference, !withSuffix, this.lang());
  11767. if (withSuffix) {
  11768. output = this.lang().pastFuture(difference, output);
  11769. }
  11770. return this.lang().postformat(output);
  11771. },
  11772. add : function (input, val) {
  11773. // supports only 2.0-style add(1, 's') or add(moment)
  11774. var dur = moment.duration(input, val);
  11775. this._milliseconds += dur._milliseconds;
  11776. this._days += dur._days;
  11777. this._months += dur._months;
  11778. this._bubble();
  11779. return this;
  11780. },
  11781. subtract : function (input, val) {
  11782. var dur = moment.duration(input, val);
  11783. this._milliseconds -= dur._milliseconds;
  11784. this._days -= dur._days;
  11785. this._months -= dur._months;
  11786. this._bubble();
  11787. return this;
  11788. },
  11789. get : function (units) {
  11790. units = normalizeUnits(units);
  11791. return this[units.toLowerCase() + 's']();
  11792. },
  11793. as : function (units) {
  11794. units = normalizeUnits(units);
  11795. return this['as' + units.charAt(0).toUpperCase() + units.slice(1) + 's']();
  11796. },
  11797. lang : moment.fn.lang,
  11798. toIsoString : function () {
  11799. // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
  11800. var years = Math.abs(this.years()),
  11801. months = Math.abs(this.months()),
  11802. days = Math.abs(this.days()),
  11803. hours = Math.abs(this.hours()),
  11804. minutes = Math.abs(this.minutes()),
  11805. seconds = Math.abs(this.seconds() + this.milliseconds() / 1000);
  11806. if (!this.asSeconds()) {
  11807. // this is the same as C#'s (Noda) and python (isodate)...
  11808. // but not other JS (goog.date)
  11809. return 'P0D';
  11810. }
  11811. return (this.asSeconds() < 0 ? '-' : '') +
  11812. 'P' +
  11813. (years ? years + 'Y' : '') +
  11814. (months ? months + 'M' : '') +
  11815. (days ? days + 'D' : '') +
  11816. ((hours || minutes || seconds) ? 'T' : '') +
  11817. (hours ? hours + 'H' : '') +
  11818. (minutes ? minutes + 'M' : '') +
  11819. (seconds ? seconds + 'S' : '');
  11820. }
  11821. });
  11822. function makeDurationGetter(name) {
  11823. moment.duration.fn[name] = function () {
  11824. return this._data[name];
  11825. };
  11826. }
  11827. function makeDurationAsGetter(name, factor) {
  11828. moment.duration.fn['as' + name] = function () {
  11829. return +this / factor;
  11830. };
  11831. }
  11832. for (i in unitMillisecondFactors) {
  11833. if (unitMillisecondFactors.hasOwnProperty(i)) {
  11834. makeDurationAsGetter(i, unitMillisecondFactors[i]);
  11835. makeDurationGetter(i.toLowerCase());
  11836. }
  11837. }
  11838. makeDurationAsGetter('Weeks', 6048e5);
  11839. moment.duration.fn.asMonths = function () {
  11840. return (+this - this.years() * 31536e6) / 2592e6 + this.years() * 12;
  11841. };
  11842. /************************************
  11843. Default Lang
  11844. ************************************/
  11845. // Set default language, other languages will inherit from English.
  11846. moment.lang('en', {
  11847. ordinal : function (number) {
  11848. var b = number % 10,
  11849. output = (toInt(number % 100 / 10) === 1) ? 'th' :
  11850. (b === 1) ? 'st' :
  11851. (b === 2) ? 'nd' :
  11852. (b === 3) ? 'rd' : 'th';
  11853. return number + output;
  11854. }
  11855. });
  11856. /* EMBED_LANGUAGES */
  11857. /************************************
  11858. Exposing Moment
  11859. ************************************/
  11860. function makeGlobal(shouldDeprecate) {
  11861. /*global ender:false */
  11862. if (typeof ender !== 'undefined') {
  11863. return;
  11864. }
  11865. oldGlobalMoment = globalScope.moment;
  11866. if (shouldDeprecate) {
  11867. globalScope.moment = deprecate(
  11868. "Accessing Moment through the global scope is " +
  11869. "deprecated, and will be removed in an upcoming " +
  11870. "release.",
  11871. moment);
  11872. } else {
  11873. globalScope.moment = moment;
  11874. }
  11875. }
  11876. // CommonJS module is defined
  11877. if (hasModule) {
  11878. module.exports = moment;
  11879. } else if (typeof define === "function" && define.amd) {
  11880. define("moment", ['require','exports','module'],function (require, exports, module) {
  11881. if (module.config && module.config() && module.config().noGlobal === true) {
  11882. // release the global variable
  11883. globalScope.moment = oldGlobalMoment;
  11884. }
  11885. return moment;
  11886. });
  11887. makeGlobal(true);
  11888. } else {
  11889. makeGlobal();
  11890. }
  11891. }).call(this);
  11892. /*
  11893. jed.js
  11894. v0.5.0beta
  11895. https://github.com/SlexAxton/Jed
  11896. -----------
  11897. A gettext compatible i18n library for modern JavaScript Applications
  11898. by Alex Sexton - AlexSexton [at] gmail - @SlexAxton
  11899. WTFPL license for use
  11900. Dojo CLA for contributions
  11901. Jed offers the entire applicable GNU gettext spec'd set of
  11902. functions, but also offers some nicer wrappers around them.
  11903. The api for gettext was written for a language with no function
  11904. overloading, so Jed allows a little more of that.
  11905. Many thanks to Joshua I. Miller - unrtst@cpan.org - who wrote
  11906. gettext.js back in 2008. I was able to vet a lot of my ideas
  11907. against his. I also made sure Jed passed against his tests
  11908. in order to offer easy upgrades -- jsgettext.berlios.de
  11909. */
  11910. (function (root, undef) {
  11911. // Set up some underscore-style functions, if you already have
  11912. // underscore, feel free to delete this section, and use it
  11913. // directly, however, the amount of functions used doesn't
  11914. // warrant having underscore as a full dependency.
  11915. // Underscore 1.3.0 was used to port and is licensed
  11916. // under the MIT License by Jeremy Ashkenas.
  11917. var ArrayProto = Array.prototype,
  11918. ObjProto = Object.prototype,
  11919. slice = ArrayProto.slice,
  11920. hasOwnProp = ObjProto.hasOwnProperty,
  11921. nativeForEach = ArrayProto.forEach,
  11922. breaker = {};
  11923. // We're not using the OOP style _ so we don't need the
  11924. // extra level of indirection. This still means that you
  11925. // sub out for real `_` though.
  11926. var _ = {
  11927. forEach : function( obj, iterator, context ) {
  11928. var i, l, key;
  11929. if ( obj === null ) {
  11930. return;
  11931. }
  11932. if ( nativeForEach && obj.forEach === nativeForEach ) {
  11933. obj.forEach( iterator, context );
  11934. }
  11935. else if ( obj.length === +obj.length ) {
  11936. for ( i = 0, l = obj.length; i < l; i++ ) {
  11937. if ( i in obj && iterator.call( context, obj[i], i, obj ) === breaker ) {
  11938. return;
  11939. }
  11940. }
  11941. }
  11942. else {
  11943. for ( key in obj) {
  11944. if ( hasOwnProp.call( obj, key ) ) {
  11945. if ( iterator.call (context, obj[key], key, obj ) === breaker ) {
  11946. return;
  11947. }
  11948. }
  11949. }
  11950. }
  11951. },
  11952. extend : function( obj ) {
  11953. this.forEach( slice.call( arguments, 1 ), function ( source ) {
  11954. for ( var prop in source ) {
  11955. obj[prop] = source[prop];
  11956. }
  11957. });
  11958. return obj;
  11959. }
  11960. };
  11961. // END Miniature underscore impl
  11962. // Jed is a constructor function
  11963. var Jed = function ( options ) {
  11964. // Some minimal defaults
  11965. this.defaults = {
  11966. "locale_data" : {
  11967. "messages" : {
  11968. "" : {
  11969. "domain" : "messages",
  11970. "lang" : "en",
  11971. "plural_forms" : "nplurals=2; plural=(n != 1);"
  11972. }
  11973. // There are no default keys, though
  11974. }
  11975. },
  11976. // The default domain if one is missing
  11977. "domain" : "messages"
  11978. };
  11979. // Mix in the sent options with the default options
  11980. this.options = _.extend( {}, this.defaults, options );
  11981. this.textdomain( this.options.domain );
  11982. if ( options.domain && ! this.options.locale_data[ this.options.domain ] ) {
  11983. throw new Error('Text domain set to non-existent domain: `' + options.domain + '`');
  11984. }
  11985. };
  11986. // The gettext spec sets this character as the default
  11987. // delimiter for context lookups.
  11988. // e.g.: context\u0004key
  11989. // If your translation company uses something different,
  11990. // just change this at any time and it will use that instead.
  11991. Jed.context_delimiter = String.fromCharCode( 4 );
  11992. function getPluralFormFunc ( plural_form_string ) {
  11993. return Jed.PF.compile( plural_form_string || "nplurals=2; plural=(n != 1);");
  11994. }
  11995. function Chain( key, i18n ){
  11996. this._key = key;
  11997. this._i18n = i18n;
  11998. }
  11999. // Create a chainable api for adding args prettily
  12000. _.extend( Chain.prototype, {
  12001. onDomain : function ( domain ) {
  12002. this._domain = domain;
  12003. return this;
  12004. },
  12005. withContext : function ( context ) {
  12006. this._context = context;
  12007. return this;
  12008. },
  12009. ifPlural : function ( num, pkey ) {
  12010. this._val = num;
  12011. this._pkey = pkey;
  12012. return this;
  12013. },
  12014. fetch : function ( sArr ) {
  12015. if ( {}.toString.call( sArr ) != '[object Array]' ) {
  12016. sArr = [].slice.call(arguments);
  12017. }
  12018. return ( sArr && sArr.length ? Jed.sprintf : function(x){ return x; } )(
  12019. this._i18n.dcnpgettext(this._domain, this._context, this._key, this._pkey, this._val),
  12020. sArr
  12021. );
  12022. }
  12023. });
  12024. // Add functions to the Jed prototype.
  12025. // These will be the functions on the object that's returned
  12026. // from creating a `new Jed()`
  12027. // These seem redundant, but they gzip pretty well.
  12028. _.extend( Jed.prototype, {
  12029. // The sexier api start point
  12030. translate : function ( key ) {
  12031. return new Chain( key, this );
  12032. },
  12033. textdomain : function ( domain ) {
  12034. if ( ! domain ) {
  12035. return this._textdomain;
  12036. }
  12037. this._textdomain = domain;
  12038. },
  12039. gettext : function ( key ) {
  12040. return this.dcnpgettext.call( this, undef, undef, key );
  12041. },
  12042. dgettext : function ( domain, key ) {
  12043. return this.dcnpgettext.call( this, domain, undef, key );
  12044. },
  12045. dcgettext : function ( domain , key /*, category */ ) {
  12046. // Ignores the category anyways
  12047. return this.dcnpgettext.call( this, domain, undef, key );
  12048. },
  12049. ngettext : function ( skey, pkey, val ) {
  12050. return this.dcnpgettext.call( this, undef, undef, skey, pkey, val );
  12051. },
  12052. dngettext : function ( domain, skey, pkey, val ) {
  12053. return this.dcnpgettext.call( this, domain, undef, skey, pkey, val );
  12054. },
  12055. dcngettext : function ( domain, skey, pkey, val/*, category */) {
  12056. return this.dcnpgettext.call( this, domain, undef, skey, pkey, val );
  12057. },
  12058. pgettext : function ( context, key ) {
  12059. return this.dcnpgettext.call( this, undef, context, key );
  12060. },
  12061. dpgettext : function ( domain, context, key ) {
  12062. return this.dcnpgettext.call( this, domain, context, key );
  12063. },
  12064. dcpgettext : function ( domain, context, key/*, category */) {
  12065. return this.dcnpgettext.call( this, domain, context, key );
  12066. },
  12067. npgettext : function ( context, skey, pkey, val ) {
  12068. return this.dcnpgettext.call( this, undef, context, skey, pkey, val );
  12069. },
  12070. dnpgettext : function ( domain, context, skey, pkey, val ) {
  12071. return this.dcnpgettext.call( this, domain, context, skey, pkey, val );
  12072. },
  12073. // The most fully qualified gettext function. It has every option.
  12074. // Since it has every option, we can use it from every other method.
  12075. // This is the bread and butter.
  12076. // Technically there should be one more argument in this function for 'Category',
  12077. // but since we never use it, we might as well not waste the bytes to define it.
  12078. dcnpgettext : function ( domain, context, singular_key, plural_key, val ) {
  12079. // Set some defaults
  12080. plural_key = plural_key || singular_key;
  12081. // Use the global domain default if one
  12082. // isn't explicitly passed in
  12083. domain = domain || this._textdomain;
  12084. // Default the value to the singular case
  12085. val = typeof val == 'undefined' ? 1 : val;
  12086. var fallback;
  12087. // Handle special cases
  12088. // No options found
  12089. if ( ! this.options ) {
  12090. // There's likely something wrong, but we'll return the correct key for english
  12091. // We do this by instantiating a brand new Jed instance with the default set
  12092. // for everything that could be broken.
  12093. fallback = new Jed();
  12094. return fallback.dcnpgettext.call( fallback, undefined, undefined, singular_key, plural_key, val );
  12095. }
  12096. // No translation data provided
  12097. if ( ! this.options.locale_data ) {
  12098. throw new Error('No locale data provided.');
  12099. }
  12100. if ( ! this.options.locale_data[ domain ] ) {
  12101. throw new Error('Domain `' + domain + '` was not found.');
  12102. }
  12103. if ( ! this.options.locale_data[ domain ][ "" ] ) {
  12104. throw new Error('No locale meta information provided.');
  12105. }
  12106. // Make sure we have a truthy key. Otherwise we might start looking
  12107. // into the empty string key, which is the options for the locale
  12108. // data.
  12109. if ( ! singular_key ) {
  12110. throw new Error('No translation key found.');
  12111. }
  12112. // Handle invalid numbers, but try casting strings for good measure
  12113. if ( typeof val != 'number' ) {
  12114. val = parseInt( val, 10 );
  12115. if ( isNaN( val ) ) {
  12116. throw new Error('The number that was passed in is not a number.');
  12117. }
  12118. }
  12119. var key = context ? context + Jed.context_delimiter + singular_key : singular_key,
  12120. locale_data = this.options.locale_data,
  12121. dict = locale_data[ domain ],
  12122. pluralForms = dict[""].plural_forms || (locale_data.messages || this.defaults.locale_data.messages)[""].plural_forms,
  12123. val_idx = getPluralFormFunc(pluralForms)(val) + 1,
  12124. val_list,
  12125. res;
  12126. // Throw an error if a domain isn't found
  12127. if ( ! dict ) {
  12128. throw new Error('No domain named `' + domain + '` could be found.');
  12129. }
  12130. val_list = dict[ key ];
  12131. // If there is no match, then revert back to
  12132. // english style singular/plural with the keys passed in.
  12133. if ( ! val_list || val_idx >= val_list.length ) {
  12134. if (this.options.missing_key_callback) {
  12135. this.options.missing_key_callback(key);
  12136. }
  12137. res = [ null, singular_key, plural_key ];
  12138. return res[ getPluralFormFunc(pluralForms)( val ) + 1 ];
  12139. }
  12140. res = val_list[ val_idx ];
  12141. // This includes empty strings on purpose
  12142. if ( ! res ) {
  12143. res = [ null, singular_key, plural_key ];
  12144. return res[ getPluralFormFunc(pluralForms)( val ) + 1 ];
  12145. }
  12146. return res;
  12147. }
  12148. });
  12149. // We add in sprintf capabilities for post translation value interolation
  12150. // This is not internally used, so you can remove it if you have this
  12151. // available somewhere else, or want to use a different system.
  12152. // We _slightly_ modify the normal sprintf behavior to more gracefully handle
  12153. // undefined values.
  12154. /**
  12155. sprintf() for JavaScript 0.7-beta1
  12156. http://www.diveintojavascript.com/projects/javascript-sprintf
  12157. Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
  12158. All rights reserved.
  12159. Redistribution and use in source and binary forms, with or without
  12160. modification, are permitted provided that the following conditions are met:
  12161. * Redistributions of source code must retain the above copyright
  12162. notice, this list of conditions and the following disclaimer.
  12163. * Redistributions in binary form must reproduce the above copyright
  12164. notice, this list of conditions and the following disclaimer in the
  12165. documentation and/or other materials provided with the distribution.
  12166. * Neither the name of sprintf() for JavaScript nor the
  12167. names of its contributors may be used to endorse or promote products
  12168. derived from this software without specific prior written permission.
  12169. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  12170. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  12171. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  12172. DISCLAIMED. IN NO EVENT SHALL Alexandru Marasteanu BE LIABLE FOR ANY
  12173. DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  12174. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  12175. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  12176. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12177. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  12178. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  12179. */
  12180. var sprintf = (function() {
  12181. function get_type(variable) {
  12182. return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
  12183. }
  12184. function str_repeat(input, multiplier) {
  12185. for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
  12186. return output.join('');
  12187. }
  12188. var str_format = function() {
  12189. if (!str_format.cache.hasOwnProperty(arguments[0])) {
  12190. str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
  12191. }
  12192. return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
  12193. };
  12194. str_format.format = function(parse_tree, argv) {
  12195. var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
  12196. for (i = 0; i < tree_length; i++) {
  12197. node_type = get_type(parse_tree[i]);
  12198. if (node_type === 'string') {
  12199. output.push(parse_tree[i]);
  12200. }
  12201. else if (node_type === 'array') {
  12202. match = parse_tree[i]; // convenience purposes only
  12203. if (match[2]) { // keyword argument
  12204. arg = argv[cursor];
  12205. for (k = 0; k < match[2].length; k++) {
  12206. if (!arg.hasOwnProperty(match[2][k])) {
  12207. throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
  12208. }
  12209. arg = arg[match[2][k]];
  12210. }
  12211. }
  12212. else if (match[1]) { // positional argument (explicit)
  12213. arg = argv[match[1]];
  12214. }
  12215. else { // positional argument (implicit)
  12216. arg = argv[cursor++];
  12217. }
  12218. if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
  12219. throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
  12220. }
  12221. // Jed EDIT
  12222. if ( typeof arg == 'undefined' || arg === null ) {
  12223. arg = '';
  12224. }
  12225. // Jed EDIT
  12226. switch (match[8]) {
  12227. case 'b': arg = arg.toString(2); break;
  12228. case 'c': arg = String.fromCharCode(arg); break;
  12229. case 'd': arg = parseInt(arg, 10); break;
  12230. case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
  12231. case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
  12232. case 'o': arg = arg.toString(8); break;
  12233. case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
  12234. case 'u': arg = Math.abs(arg); break;
  12235. case 'x': arg = arg.toString(16); break;
  12236. case 'X': arg = arg.toString(16).toUpperCase(); break;
  12237. }
  12238. arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
  12239. pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
  12240. pad_length = match[6] - String(arg).length;
  12241. pad = match[6] ? str_repeat(pad_character, pad_length) : '';
  12242. output.push(match[5] ? arg + pad : pad + arg);
  12243. }
  12244. }
  12245. return output.join('');
  12246. };
  12247. str_format.cache = {};
  12248. str_format.parse = function(fmt) {
  12249. var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
  12250. while (_fmt) {
  12251. if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
  12252. parse_tree.push(match[0]);
  12253. }
  12254. else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
  12255. parse_tree.push('%');
  12256. }
  12257. else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
  12258. if (match[2]) {
  12259. arg_names |= 1;
  12260. var field_list = [], replacement_field = match[2], field_match = [];
  12261. if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
  12262. field_list.push(field_match[1]);
  12263. while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
  12264. if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
  12265. field_list.push(field_match[1]);
  12266. }
  12267. else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
  12268. field_list.push(field_match[1]);
  12269. }
  12270. else {
  12271. throw('[sprintf] huh?');
  12272. }
  12273. }
  12274. }
  12275. else {
  12276. throw('[sprintf] huh?');
  12277. }
  12278. match[2] = field_list;
  12279. }
  12280. else {
  12281. arg_names |= 2;
  12282. }
  12283. if (arg_names === 3) {
  12284. throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
  12285. }
  12286. parse_tree.push(match);
  12287. }
  12288. else {
  12289. throw('[sprintf] huh?');
  12290. }
  12291. _fmt = _fmt.substring(match[0].length);
  12292. }
  12293. return parse_tree;
  12294. };
  12295. return str_format;
  12296. })();
  12297. var vsprintf = function(fmt, argv) {
  12298. argv.unshift(fmt);
  12299. return sprintf.apply(null, argv);
  12300. };
  12301. Jed.parse_plural = function ( plural_forms, n ) {
  12302. plural_forms = plural_forms.replace(/n/g, n);
  12303. return Jed.parse_expression(plural_forms);
  12304. };
  12305. Jed.sprintf = function ( fmt, args ) {
  12306. if ( {}.toString.call( args ) == '[object Array]' ) {
  12307. return vsprintf( fmt, [].slice.call(args) );
  12308. }
  12309. return sprintf.apply(this, [].slice.call(arguments) );
  12310. };
  12311. Jed.prototype.sprintf = function () {
  12312. return Jed.sprintf.apply(this, arguments);
  12313. };
  12314. // END sprintf Implementation
  12315. // Start the Plural forms section
  12316. // This is a full plural form expression parser. It is used to avoid
  12317. // running 'eval' or 'new Function' directly against the plural
  12318. // forms.
  12319. //
  12320. // This can be important if you get translations done through a 3rd
  12321. // party vendor. I encourage you to use this instead, however, I
  12322. // also will provide a 'precompiler' that you can use at build time
  12323. // to output valid/safe function representations of the plural form
  12324. // expressions. This means you can build this code out for the most
  12325. // part.
  12326. Jed.PF = {};
  12327. Jed.PF.parse = function ( p ) {
  12328. var plural_str = Jed.PF.extractPluralExpr( p );
  12329. return Jed.PF.parser.parse.call(Jed.PF.parser, plural_str);
  12330. };
  12331. Jed.PF.compile = function ( p ) {
  12332. // Handle trues and falses as 0 and 1
  12333. function imply( val ) {
  12334. return (val === true ? 1 : val ? val : 0);
  12335. }
  12336. var ast = Jed.PF.parse( p );
  12337. return function ( n ) {
  12338. return imply( Jed.PF.interpreter( ast )( n ) );
  12339. };
  12340. };
  12341. Jed.PF.interpreter = function ( ast ) {
  12342. return function ( n ) {
  12343. var res;
  12344. switch ( ast.type ) {
  12345. case 'GROUP':
  12346. return Jed.PF.interpreter( ast.expr )( n );
  12347. case 'TERNARY':
  12348. if ( Jed.PF.interpreter( ast.expr )( n ) ) {
  12349. return Jed.PF.interpreter( ast.truthy )( n );
  12350. }
  12351. return Jed.PF.interpreter( ast.falsey )( n );
  12352. case 'OR':
  12353. return Jed.PF.interpreter( ast.left )( n ) || Jed.PF.interpreter( ast.right )( n );
  12354. case 'AND':
  12355. return Jed.PF.interpreter( ast.left )( n ) && Jed.PF.interpreter( ast.right )( n );
  12356. case 'LT':
  12357. return Jed.PF.interpreter( ast.left )( n ) < Jed.PF.interpreter( ast.right )( n );
  12358. case 'GT':
  12359. return Jed.PF.interpreter( ast.left )( n ) > Jed.PF.interpreter( ast.right )( n );
  12360. case 'LTE':
  12361. return Jed.PF.interpreter( ast.left )( n ) <= Jed.PF.interpreter( ast.right )( n );
  12362. case 'GTE':
  12363. return Jed.PF.interpreter( ast.left )( n ) >= Jed.PF.interpreter( ast.right )( n );
  12364. case 'EQ':
  12365. return Jed.PF.interpreter( ast.left )( n ) == Jed.PF.interpreter( ast.right )( n );
  12366. case 'NEQ':
  12367. return Jed.PF.interpreter( ast.left )( n ) != Jed.PF.interpreter( ast.right )( n );
  12368. case 'MOD':
  12369. return Jed.PF.interpreter( ast.left )( n ) % Jed.PF.interpreter( ast.right )( n );
  12370. case 'VAR':
  12371. return n;
  12372. case 'NUM':
  12373. return ast.val;
  12374. default:
  12375. throw new Error("Invalid Token found.");
  12376. }
  12377. };
  12378. };
  12379. Jed.PF.extractPluralExpr = function ( p ) {
  12380. // trim first
  12381. p = p.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
  12382. if (! /;\s*$/.test(p)) {
  12383. p = p.concat(';');
  12384. }
  12385. var nplurals_re = /nplurals\=(\d+);/,
  12386. plural_re = /plural\=(.*);/,
  12387. nplurals_matches = p.match( nplurals_re ),
  12388. res = {},
  12389. plural_matches;
  12390. // Find the nplurals number
  12391. if ( nplurals_matches.length > 1 ) {
  12392. res.nplurals = nplurals_matches[1];
  12393. }
  12394. else {
  12395. throw new Error('nplurals not found in plural_forms string: ' + p );
  12396. }
  12397. // remove that data to get to the formula
  12398. p = p.replace( nplurals_re, "" );
  12399. plural_matches = p.match( plural_re );
  12400. if (!( plural_matches && plural_matches.length > 1 ) ) {
  12401. throw new Error('`plural` expression not found: ' + p);
  12402. }
  12403. return plural_matches[ 1 ];
  12404. };
  12405. /* Jison generated parser */
  12406. Jed.PF.parser = (function(){
  12407. var parser = {trace: function trace() { },
  12408. yy: {},
  12409. symbols_: {"error":2,"expressions":3,"e":4,"EOF":5,"?":6,":":7,"||":8,"&&":9,"<":10,"<=":11,">":12,">=":13,"!=":14,"==":15,"%":16,"(":17,")":18,"n":19,"NUMBER":20,"$accept":0,"$end":1},
  12410. terminals_: {2:"error",5:"EOF",6:"?",7:":",8:"||",9:"&&",10:"<",11:"<=",12:">",13:">=",14:"!=",15:"==",16:"%",17:"(",18:")",19:"n",20:"NUMBER"},
  12411. productions_: [0,[3,2],[4,5],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,1],[4,1]],
  12412. performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
  12413. var $0 = $$.length - 1;
  12414. switch (yystate) {
  12415. case 1: return { type : 'GROUP', expr: $$[$0-1] };
  12416. break;
  12417. case 2:this.$ = { type: 'TERNARY', expr: $$[$0-4], truthy : $$[$0-2], falsey: $$[$0] };
  12418. break;
  12419. case 3:this.$ = { type: "OR", left: $$[$0-2], right: $$[$0] };
  12420. break;
  12421. case 4:this.$ = { type: "AND", left: $$[$0-2], right: $$[$0] };
  12422. break;
  12423. case 5:this.$ = { type: 'LT', left: $$[$0-2], right: $$[$0] };
  12424. break;
  12425. case 6:this.$ = { type: 'LTE', left: $$[$0-2], right: $$[$0] };
  12426. break;
  12427. case 7:this.$ = { type: 'GT', left: $$[$0-2], right: $$[$0] };
  12428. break;
  12429. case 8:this.$ = { type: 'GTE', left: $$[$0-2], right: $$[$0] };
  12430. break;
  12431. case 9:this.$ = { type: 'NEQ', left: $$[$0-2], right: $$[$0] };
  12432. break;
  12433. case 10:this.$ = { type: 'EQ', left: $$[$0-2], right: $$[$0] };
  12434. break;
  12435. case 11:this.$ = { type: 'MOD', left: $$[$0-2], right: $$[$0] };
  12436. break;
  12437. case 12:this.$ = { type: 'GROUP', expr: $$[$0-1] };
  12438. break;
  12439. case 13:this.$ = { type: 'VAR' };
  12440. break;
  12441. case 14:this.$ = { type: 'NUM', val: Number(yytext) };
  12442. break;
  12443. }
  12444. },
  12445. table: [{3:1,4:2,17:[1,3],19:[1,4],20:[1,5]},{1:[3]},{5:[1,6],6:[1,7],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16]},{4:17,17:[1,3],19:[1,4],20:[1,5]},{5:[2,13],6:[2,13],7:[2,13],8:[2,13],9:[2,13],10:[2,13],11:[2,13],12:[2,13],13:[2,13],14:[2,13],15:[2,13],16:[2,13],18:[2,13]},{5:[2,14],6:[2,14],7:[2,14],8:[2,14],9:[2,14],10:[2,14],11:[2,14],12:[2,14],13:[2,14],14:[2,14],15:[2,14],16:[2,14],18:[2,14]},{1:[2,1]},{4:18,17:[1,3],19:[1,4],20:[1,5]},{4:19,17:[1,3],19:[1,4],20:[1,5]},{4:20,17:[1,3],19:[1,4],20:[1,5]},{4:21,17:[1,3],19:[1,4],20:[1,5]},{4:22,17:[1,3],19:[1,4],20:[1,5]},{4:23,17:[1,3],19:[1,4],20:[1,5]},{4:24,17:[1,3],19:[1,4],20:[1,5]},{4:25,17:[1,3],19:[1,4],20:[1,5]},{4:26,17:[1,3],19:[1,4],20:[1,5]},{4:27,17:[1,3],19:[1,4],20:[1,5]},{6:[1,7],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[1,28]},{6:[1,7],7:[1,29],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16]},{5:[2,3],6:[2,3],7:[2,3],8:[2,3],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[2,3]},{5:[2,4],6:[2,4],7:[2,4],8:[2,4],9:[2,4],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[2,4]},{5:[2,5],6:[2,5],7:[2,5],8:[2,5],9:[2,5],10:[2,5],11:[2,5],12:[2,5],13:[2,5],14:[2,5],15:[2,5],16:[1,16],18:[2,5]},{5:[2,6],6:[2,6],7:[2,6],8:[2,6],9:[2,6],10:[2,6],11:[2,6],12:[2,6],13:[2,6],14:[2,6],15:[2,6],16:[1,16],18:[2,6]},{5:[2,7],6:[2,7],7:[2,7],8:[2,7],9:[2,7],10:[2,7],11:[2,7],12:[2,7],13:[2,7],14:[2,7],15:[2,7],16:[1,16],18:[2,7]},{5:[2,8],6:[2,8],7:[2,8],8:[2,8],9:[2,8],10:[2,8],11:[2,8],12:[2,8],13:[2,8],14:[2,8],15:[2,8],16:[1,16],18:[2,8]},{5:[2,9],6:[2,9],7:[2,9],8:[2,9],9:[2,9],10:[2,9],11:[2,9],12:[2,9],13:[2,9],14:[2,9],15:[2,9],16:[1,16],18:[2,9]},{5:[2,10],6:[2,10],7:[2,10],8:[2,10],9:[2,10],10:[2,10],11:[2,10],12:[2,10],13:[2,10],14:[2,10],15:[2,10],16:[1,16],18:[2,10]},{5:[2,11],6:[2,11],7:[2,11],8:[2,11],9:[2,11],10:[2,11],11:[2,11],12:[2,11],13:[2,11],14:[2,11],15:[2,11],16:[2,11],18:[2,11]},{5:[2,12],6:[2,12],7:[2,12],8:[2,12],9:[2,12],10:[2,12],11:[2,12],12:[2,12],13:[2,12],14:[2,12],15:[2,12],16:[2,12],18:[2,12]},{4:30,17:[1,3],19:[1,4],20:[1,5]},{5:[2,2],6:[1,7],7:[2,2],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[2,2]}],
  12446. defaultActions: {6:[2,1]},
  12447. parseError: function parseError(str, hash) {
  12448. throw new Error(str);
  12449. },
  12450. parse: function parse(input) {
  12451. var self = this,
  12452. stack = [0],
  12453. vstack = [null], // semantic value stack
  12454. lstack = [], // location stack
  12455. table = this.table,
  12456. yytext = '',
  12457. yylineno = 0,
  12458. yyleng = 0,
  12459. recovering = 0,
  12460. TERROR = 2,
  12461. EOF = 1;
  12462. //this.reductionCount = this.shiftCount = 0;
  12463. this.lexer.setInput(input);
  12464. this.lexer.yy = this.yy;
  12465. this.yy.lexer = this.lexer;
  12466. if (typeof this.lexer.yylloc == 'undefined')
  12467. this.lexer.yylloc = {};
  12468. var yyloc = this.lexer.yylloc;
  12469. lstack.push(yyloc);
  12470. if (typeof this.yy.parseError === 'function')
  12471. this.parseError = this.yy.parseError;
  12472. function popStack (n) {
  12473. stack.length = stack.length - 2*n;
  12474. vstack.length = vstack.length - n;
  12475. lstack.length = lstack.length - n;
  12476. }
  12477. function lex() {
  12478. var token;
  12479. token = self.lexer.lex() || 1; // $end = 1
  12480. // if token isn't its numeric value, convert
  12481. if (typeof token !== 'number') {
  12482. token = self.symbols_[token] || token;
  12483. }
  12484. return token;
  12485. }
  12486. var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected;
  12487. while (true) {
  12488. // retreive state number from top of stack
  12489. state = stack[stack.length-1];
  12490. // use default actions if available
  12491. if (this.defaultActions[state]) {
  12492. action = this.defaultActions[state];
  12493. } else {
  12494. if (symbol == null)
  12495. symbol = lex();
  12496. // read action for current state and first input
  12497. action = table[state] && table[state][symbol];
  12498. }
  12499. // handle parse error
  12500. _handle_error:
  12501. if (typeof action === 'undefined' || !action.length || !action[0]) {
  12502. if (!recovering) {
  12503. // Report error
  12504. expected = [];
  12505. for (p in table[state]) if (this.terminals_[p] && p > 2) {
  12506. expected.push("'"+this.terminals_[p]+"'");
  12507. }
  12508. var errStr = '';
  12509. if (this.lexer.showPosition) {
  12510. errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'";
  12511. } else {
  12512. errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " +
  12513. (symbol == 1 /*EOF*/ ? "end of input" :
  12514. ("'"+(this.terminals_[symbol] || symbol)+"'"));
  12515. }
  12516. this.parseError(errStr,
  12517. {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
  12518. }
  12519. // just recovered from another error
  12520. if (recovering == 3) {
  12521. if (symbol == EOF) {
  12522. throw new Error(errStr || 'Parsing halted.');
  12523. }
  12524. // discard current lookahead and grab another
  12525. yyleng = this.lexer.yyleng;
  12526. yytext = this.lexer.yytext;
  12527. yylineno = this.lexer.yylineno;
  12528. yyloc = this.lexer.yylloc;
  12529. symbol = lex();
  12530. }
  12531. // try to recover from error
  12532. while (1) {
  12533. // check for error recovery rule in this state
  12534. if ((TERROR.toString()) in table[state]) {
  12535. break;
  12536. }
  12537. if (state == 0) {
  12538. throw new Error(errStr || 'Parsing halted.');
  12539. }
  12540. popStack(1);
  12541. state = stack[stack.length-1];
  12542. }
  12543. preErrorSymbol = symbol; // save the lookahead token
  12544. symbol = TERROR; // insert generic error symbol as new lookahead
  12545. state = stack[stack.length-1];
  12546. action = table[state] && table[state][TERROR];
  12547. recovering = 3; // allow 3 real symbols to be shifted before reporting a new error
  12548. }
  12549. // this shouldn't happen, unless resolve defaults are off
  12550. if (action[0] instanceof Array && action.length > 1) {
  12551. throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol);
  12552. }
  12553. switch (action[0]) {
  12554. case 1: // shift
  12555. //this.shiftCount++;
  12556. stack.push(symbol);
  12557. vstack.push(this.lexer.yytext);
  12558. lstack.push(this.lexer.yylloc);
  12559. stack.push(action[1]); // push state
  12560. symbol = null;
  12561. if (!preErrorSymbol) { // normal execution/no error
  12562. yyleng = this.lexer.yyleng;
  12563. yytext = this.lexer.yytext;
  12564. yylineno = this.lexer.yylineno;
  12565. yyloc = this.lexer.yylloc;
  12566. if (recovering > 0)
  12567. recovering--;
  12568. } else { // error just occurred, resume old lookahead f/ before error
  12569. symbol = preErrorSymbol;
  12570. preErrorSymbol = null;
  12571. }
  12572. break;
  12573. case 2: // reduce
  12574. //this.reductionCount++;
  12575. len = this.productions_[action[1]][1];
  12576. // perform semantic action
  12577. yyval.$ = vstack[vstack.length-len]; // default to $$ = $1
  12578. // default location, uses first token for firsts, last for lasts
  12579. yyval._$ = {
  12580. first_line: lstack[lstack.length-(len||1)].first_line,
  12581. last_line: lstack[lstack.length-1].last_line,
  12582. first_column: lstack[lstack.length-(len||1)].first_column,
  12583. last_column: lstack[lstack.length-1].last_column
  12584. };
  12585. r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
  12586. if (typeof r !== 'undefined') {
  12587. return r;
  12588. }
  12589. // pop off stack
  12590. if (len) {
  12591. stack = stack.slice(0,-1*len*2);
  12592. vstack = vstack.slice(0, -1*len);
  12593. lstack = lstack.slice(0, -1*len);
  12594. }
  12595. stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce)
  12596. vstack.push(yyval.$);
  12597. lstack.push(yyval._$);
  12598. // goto new state = table[STATE][NONTERMINAL]
  12599. newState = table[stack[stack.length-2]][stack[stack.length-1]];
  12600. stack.push(newState);
  12601. break;
  12602. case 3: // accept
  12603. return true;
  12604. }
  12605. }
  12606. return true;
  12607. }};/* Jison generated lexer */
  12608. var lexer = (function(){
  12609. var lexer = ({EOF:1,
  12610. parseError:function parseError(str, hash) {
  12611. if (this.yy.parseError) {
  12612. this.yy.parseError(str, hash);
  12613. } else {
  12614. throw new Error(str);
  12615. }
  12616. },
  12617. setInput:function (input) {
  12618. this._input = input;
  12619. this._more = this._less = this.done = false;
  12620. this.yylineno = this.yyleng = 0;
  12621. this.yytext = this.matched = this.match = '';
  12622. this.conditionStack = ['INITIAL'];
  12623. this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
  12624. return this;
  12625. },
  12626. input:function () {
  12627. var ch = this._input[0];
  12628. this.yytext+=ch;
  12629. this.yyleng++;
  12630. this.match+=ch;
  12631. this.matched+=ch;
  12632. var lines = ch.match(/\n/);
  12633. if (lines) this.yylineno++;
  12634. this._input = this._input.slice(1);
  12635. return ch;
  12636. },
  12637. unput:function (ch) {
  12638. this._input = ch + this._input;
  12639. return this;
  12640. },
  12641. more:function () {
  12642. this._more = true;
  12643. return this;
  12644. },
  12645. pastInput:function () {
  12646. var past = this.matched.substr(0, this.matched.length - this.match.length);
  12647. return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
  12648. },
  12649. upcomingInput:function () {
  12650. var next = this.match;
  12651. if (next.length < 20) {
  12652. next += this._input.substr(0, 20-next.length);
  12653. }
  12654. return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
  12655. },
  12656. showPosition:function () {
  12657. var pre = this.pastInput();
  12658. var c = new Array(pre.length + 1).join("-");
  12659. return pre + this.upcomingInput() + "\n" + c+"^";
  12660. },
  12661. next:function () {
  12662. if (this.done) {
  12663. return this.EOF;
  12664. }
  12665. if (!this._input) this.done = true;
  12666. var token,
  12667. match,
  12668. col,
  12669. lines;
  12670. if (!this._more) {
  12671. this.yytext = '';
  12672. this.match = '';
  12673. }
  12674. var rules = this._currentRules();
  12675. for (var i=0;i < rules.length; i++) {
  12676. match = this._input.match(this.rules[rules[i]]);
  12677. if (match) {
  12678. lines = match[0].match(/\n.*/g);
  12679. if (lines) this.yylineno += lines.length;
  12680. this.yylloc = {first_line: this.yylloc.last_line,
  12681. last_line: this.yylineno+1,
  12682. first_column: this.yylloc.last_column,
  12683. last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
  12684. this.yytext += match[0];
  12685. this.match += match[0];
  12686. this.matches = match;
  12687. this.yyleng = this.yytext.length;
  12688. this._more = false;
  12689. this._input = this._input.slice(match[0].length);
  12690. this.matched += match[0];
  12691. token = this.performAction.call(this, this.yy, this, rules[i],this.conditionStack[this.conditionStack.length-1]);
  12692. if (token) return token;
  12693. else return;
  12694. }
  12695. }
  12696. if (this._input === "") {
  12697. return this.EOF;
  12698. } else {
  12699. this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
  12700. {text: "", token: null, line: this.yylineno});
  12701. }
  12702. },
  12703. lex:function lex() {
  12704. var r = this.next();
  12705. if (typeof r !== 'undefined') {
  12706. return r;
  12707. } else {
  12708. return this.lex();
  12709. }
  12710. },
  12711. begin:function begin(condition) {
  12712. this.conditionStack.push(condition);
  12713. },
  12714. popState:function popState() {
  12715. return this.conditionStack.pop();
  12716. },
  12717. _currentRules:function _currentRules() {
  12718. return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
  12719. },
  12720. topState:function () {
  12721. return this.conditionStack[this.conditionStack.length-2];
  12722. },
  12723. pushState:function begin(condition) {
  12724. this.begin(condition);
  12725. }});
  12726. lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
  12727. var YYSTATE=YY_START;
  12728. switch($avoiding_name_collisions) {
  12729. case 0:/* skip whitespace */
  12730. break;
  12731. case 1:return 20
  12732. break;
  12733. case 2:return 19
  12734. break;
  12735. case 3:return 8
  12736. break;
  12737. case 4:return 9
  12738. break;
  12739. case 5:return 6
  12740. break;
  12741. case 6:return 7
  12742. break;
  12743. case 7:return 11
  12744. break;
  12745. case 8:return 13
  12746. break;
  12747. case 9:return 10
  12748. break;
  12749. case 10:return 12
  12750. break;
  12751. case 11:return 14
  12752. break;
  12753. case 12:return 15
  12754. break;
  12755. case 13:return 16
  12756. break;
  12757. case 14:return 17
  12758. break;
  12759. case 15:return 18
  12760. break;
  12761. case 16:return 5
  12762. break;
  12763. case 17:return 'INVALID'
  12764. break;
  12765. }
  12766. };
  12767. lexer.rules = [/^\s+/,/^[0-9]+(\.[0-9]+)?\b/,/^n\b/,/^\|\|/,/^&&/,/^\?/,/^:/,/^<=/,/^>=/,/^</,/^>/,/^!=/,/^==/,/^%/,/^\(/,/^\)/,/^$/,/^./];
  12768. lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],"inclusive":true}};return lexer;})()
  12769. parser.lexer = lexer;
  12770. return parser;
  12771. })();
  12772. // End parser
  12773. // Handle node, amd, and global systems
  12774. if (typeof exports !== 'undefined') {
  12775. if (typeof module !== 'undefined' && module.exports) {
  12776. exports = module.exports = Jed;
  12777. }
  12778. exports.Jed = Jed;
  12779. }
  12780. else {
  12781. if (typeof define === 'function' && define.amd) {
  12782. define('jed', [],function() {
  12783. return Jed;
  12784. });
  12785. }
  12786. // Leak a global regardless of module system
  12787. root['Jed'] = Jed;
  12788. }
  12789. })(this);
  12790. (function (root, factory) {
  12791. var translations = {
  12792. "domain": "converse",
  12793. "locale_data": {
  12794. "converse": {
  12795. "": {
  12796. "Project-Id-Version": "Converse.js 0.4",
  12797. "Report-Msgid-Bugs-To": "",
  12798. "POT-Creation-Date": "2013-09-15 21:55+0200",
  12799. "PO-Revision-Date": "2013-09-15 21:56+0200",
  12800. "Last-Translator": "JC Brand <jc@opkode.com>",
  12801. "Language-Team": "Afrikaans",
  12802. "Language": "af",
  12803. "MIME-Version": "1.0",
  12804. "Content-Type": "text/plain; charset=UTF-8",
  12805. "Content-Transfer-Encoding": "8bit",
  12806. "domain": "converse",
  12807. "lang": "af",
  12808. "plural_forms": "nplurals=2; plural=(n != 1);"
  12809. },
  12810. "unencrypted": [
  12811. null,
  12812. "nie-privaat"
  12813. ],
  12814. "unverified": [
  12815. null,
  12816. "ongeverifieer"
  12817. ],
  12818. "verified": [
  12819. null,
  12820. "privaat"
  12821. ],
  12822. "finished": [
  12823. null,
  12824. "afgesluit"
  12825. ],
  12826. "Disconnected": [
  12827. null,
  12828. "Verbindung onderbreek"
  12829. ],
  12830. "Error": [
  12831. null,
  12832. "Fout"
  12833. ],
  12834. "Connecting": [
  12835. null,
  12836. "Verbind tans"
  12837. ],
  12838. "Connection Failed": [
  12839. null,
  12840. "Verbinding het gefaal"
  12841. ],
  12842. "Authenticating": [
  12843. null,
  12844. "Besig om te bekragtig"
  12845. ],
  12846. "Authentication Failed": [
  12847. null,
  12848. "Bekragtiging het gefaal"
  12849. ],
  12850. "Disconnecting": [
  12851. null,
  12852. "Onderbreek verbinding"
  12853. ],
  12854. "Re-establishing encrypted session": [
  12855. null,
  12856. "Herstel versleutelde sessie"
  12857. ],
  12858. "Your browser needs to generate a private key, which will be used in your encrypted chat session. This can take up to 30 seconds during which your browser might freeze and become unresponsive.": [
  12859. null,
  12860. "Die webblaaier moet 'n private sleutel vir die versleutelde klets-sessie genereer. Dit kan tot 30 sekondes duur, waartydenѕ die webblaaier mag vries en nie reageer nie."
  12861. ],
  12862. "Private key generated.": [
  12863. null,
  12864. "Private sleutel"
  12865. ],
  12866. "Authentication request from %1$s\n\nYour buddy is attempting to verify your identity, by asking you the question below.\n\n%2$s": [
  12867. null,
  12868. "Verifikasie versoek van %1$s\n\nU gespreksmaat probeer om u identiteit te verifieer, deur die volgende vraag te vra \n\n%2$s"
  12869. ],
  12870. "Could not verify this user's identify.": [
  12871. null,
  12872. "Kon nie hierdie gebruiker se identitied verifieer nie."
  12873. ],
  12874. "Personal message": [
  12875. null,
  12876. "Persoonlike boodskap"
  12877. ],
  12878. "Start encrypted conversation": [
  12879. null,
  12880. "Begin versleutelde gesprek"
  12881. ],
  12882. "Refresh encrypted conversation": [
  12883. null,
  12884. "Verfris versleutelde gesprek"
  12885. ],
  12886. "End encrypted conversation": [
  12887. null,
  12888. "Beëindig versleutelde gesprek"
  12889. ],
  12890. "Verify with SMP": [
  12891. null,
  12892. "Verifieer met SMP"
  12893. ],
  12894. "Verify with fingerprints": [
  12895. null,
  12896. "Verifieer met vingerafdrukke"
  12897. ],
  12898. "What's this?": [
  12899. null,
  12900. "Wat is hierdie?"
  12901. ],
  12902. "me": [
  12903. null,
  12904. "ek"
  12905. ],
  12906. "Show this menu": [
  12907. null,
  12908. "Vertoon hierdie keuselys"
  12909. ],
  12910. "Write in the third person": [
  12911. null,
  12912. "Skryf in die derde persoon"
  12913. ],
  12914. "Remove messages": [
  12915. null,
  12916. "Verwyder boodskappe"
  12917. ],
  12918. "Your message could not be sent": [
  12919. null,
  12920. "U boodskap kon nie gestuur word nie"
  12921. ],
  12922. "We received an unencrypted message": [
  12923. null,
  12924. "Ons het 'n onversleutelde boodskap ontvang"
  12925. ],
  12926. "We received an unreadable encrypted message": [
  12927. null,
  12928. "Ons het 'n onleesbare versleutelde boodskap ontvang"
  12929. ],
  12930. "This user has requested an encrypted session.": [
  12931. null,
  12932. "Hierdie gebruiker versoek 'n versleutelde sessie"
  12933. ],
  12934. "Here are the fingerprints, please confirm them with %1$s, outside of this chat.\n\nFingerprint for you, %2$s: %3$s\n\nFingerprint for %1$s: %4$s\n\nIf you have confirmed that the fingerprints match, click OK, otherwise click Cancel.": [
  12935. null,
  12936. "Hier is die vingerafdrukke, bevestig hulle met %1$s, buite hierdie kletskanaal \n\nU vingerafdruk, %2$s: %3$s\n\nVingerafdruk vir %1$s: %4$s\n\nIndien u die vingerafdrukke bevestig het, klik OK, andersinds klik Kanselleer"
  12937. ],
  12938. "You will be prompted to provide a security question and then an answer to that question.\n\nYour buddy will then be prompted the same question and if they type the exact same answer (case sensitive), their identity will have been verified.": [
  12939. null,
  12940. "Daar sal van u verwag word om 'n sekuriteitsvraag te stel, en dan ook die antwoord tot daardie vraag te verskaf.\n\nU gespreksmaat sal dan daardie vraag gestel word, en indien hulle presies dieselfde antwoord (hoofletters tel) verskaf, sal hul identiteit geverifieer wees."
  12941. ],
  12942. "What is your security question?": [
  12943. null,
  12944. "Wat is u sekuriteitsvraag?"
  12945. ],
  12946. "What is the answer to the security question?": [
  12947. null,
  12948. "Wat is die antwoord tot die sekuriteitsvraag?"
  12949. ],
  12950. "Invalid authentication scheme provided": [
  12951. null,
  12952. "Ongeldige verifikasiemetode verskaf"
  12953. ],
  12954. "Your messages are not encrypted anymore": [
  12955. null,
  12956. "U boodskappe is nie meer versleutel nie"
  12957. ],
  12958. "Your messages are now encrypted but your buddy's identity has not been verified.": [
  12959. null,
  12960. "U boodskappe is now versleutel maar u gespreksmaat se identiteit is nog onseker."
  12961. ],
  12962. "Your buddy's identify has been verified.": [
  12963. null,
  12964. "U gespreksmaat se identiteit is geverifieer."
  12965. ],
  12966. "Your buddy has ended encryption on their end, you should do the same.": [
  12967. null,
  12968. "U gespreksmaat het versleuteling gestaak, u behoort nou dieselfde te doen."
  12969. ],
  12970. "Your messages are not encrypted. Click here to enable OTR encryption.": [
  12971. null,
  12972. "U boodskappe is nie versleutel nie. Klik hier om OTR versleuteling te aktiveer."
  12973. ],
  12974. "Your messages are encrypted, but your buddy has not been verified.": [
  12975. null,
  12976. "U boodskappe is versleutel, maar u gespreksmaat se identiteit is not onseker."
  12977. ],
  12978. "Your messages are encrypted and your buddy verified.": [
  12979. null,
  12980. "U boodskappe is versleutel en u gespreksmaat se identiteit geverifieer."
  12981. ],
  12982. "Your buddy has closed their end of the private session, you should do the same": [
  12983. null,
  12984. "U gespreksmaat het die private sessie gestaak. U behoort dieselfde te doen"
  12985. ],
  12986. "Contacts": [
  12987. null,
  12988. "Kontakte"
  12989. ],
  12990. "Online": [
  12991. null,
  12992. "Aangemeld"
  12993. ],
  12994. "Busy": [
  12995. null,
  12996. "Besig"
  12997. ],
  12998. "Away": [
  12999. null,
  13000. "Afwesig"
  13001. ],
  13002. "Offline": [
  13003. null,
  13004. "Afgemeld"
  13005. ],
  13006. "Click to add new chat contacts": [
  13007. null,
  13008. "Kliek om nuwe kletskontakte by te voeg"
  13009. ],
  13010. "Add a contact": [
  13011. null,
  13012. "Voeg 'n kontak by"
  13013. ],
  13014. "Contact username": [
  13015. null,
  13016. "Konak gebruikersnaam"
  13017. ],
  13018. "Add": [
  13019. null,
  13020. "Voeg by"
  13021. ],
  13022. "Contact name": [
  13023. null,
  13024. "Kontaknaam"
  13025. ],
  13026. "Search": [
  13027. null,
  13028. "Soek"
  13029. ],
  13030. "No users found": [
  13031. null,
  13032. "Geen gebruikers gevind"
  13033. ],
  13034. "Click to add as a chat contact": [
  13035. null,
  13036. "Kliek om as kletskontak by te voeg"
  13037. ],
  13038. "Click to open this room": [
  13039. null,
  13040. "Kliek om hierdie kletskamer te open"
  13041. ],
  13042. "Show more information on this room": [
  13043. null,
  13044. "Wys meer inligting aangaande hierdie kletskamer"
  13045. ],
  13046. "Description:": [
  13047. null,
  13048. "Beskrywing:"
  13049. ],
  13050. "Occupants:": [
  13051. null,
  13052. "Deelnemers:"
  13053. ],
  13054. "Features:": [
  13055. null,
  13056. "Eienskappe:"
  13057. ],
  13058. "Requires authentication": [
  13059. null,
  13060. "Benodig magtiging"
  13061. ],
  13062. "Hidden": [
  13063. null,
  13064. "Verskuil"
  13065. ],
  13066. "Requires an invitation": [
  13067. null,
  13068. "Benodig 'n uitnodiging"
  13069. ],
  13070. "Moderated": [
  13071. null,
  13072. "Gemodereer"
  13073. ],
  13074. "Non-anonymous": [
  13075. null,
  13076. "Nie-anoniem"
  13077. ],
  13078. "Open room": [
  13079. null,
  13080. "Oop kletskamer"
  13081. ],
  13082. "Permanent room": [
  13083. null,
  13084. "Permanente kamer"
  13085. ],
  13086. "Public": [
  13087. null,
  13088. "Publiek"
  13089. ],
  13090. "Semi-anonymous": [
  13091. null,
  13092. "Deels anoniem"
  13093. ],
  13094. "Temporary room": [
  13095. null,
  13096. "Tydelike kamer"
  13097. ],
  13098. "Unmoderated": [
  13099. null,
  13100. "Ongemodereer"
  13101. ],
  13102. "Rooms": [
  13103. null,
  13104. "Kamers"
  13105. ],
  13106. "Room name": [
  13107. null,
  13108. "Kamer naam"
  13109. ],
  13110. "Nickname": [
  13111. null,
  13112. "Bynaam"
  13113. ],
  13114. "Server": [
  13115. null,
  13116. "Bediener"
  13117. ],
  13118. "Join": [
  13119. null,
  13120. "Sluit aan"
  13121. ],
  13122. "Show rooms": [
  13123. null,
  13124. "Wys kamers"
  13125. ],
  13126. "No rooms on %1$s": [
  13127. null,
  13128. "Geen kamers op %1$s"
  13129. ],
  13130. "Rooms on %1$s": [
  13131. null,
  13132. "Kamers op %1$s"
  13133. ],
  13134. "Set chatroom topic": [
  13135. null,
  13136. "Stel kletskamer onderwerp"
  13137. ],
  13138. "Kick user from chatroom": [
  13139. null,
  13140. "Skop gebruiker uit die kletskamer"
  13141. ],
  13142. "Ban user from chatroom": [
  13143. null,
  13144. "Verban gebruiker vanuit die kletskamer"
  13145. ],
  13146. "Message": [
  13147. null,
  13148. "Boodskap"
  13149. ],
  13150. "Save": [
  13151. null,
  13152. "Stoor"
  13153. ],
  13154. "Cancel": [
  13155. null,
  13156. "Kanseleer"
  13157. ],
  13158. "An error occurred while trying to save the form.": [
  13159. null,
  13160. "A fout het voorgekom terwyl probeer is om die vorm te stoor."
  13161. ],
  13162. "This chatroom requires a password": [
  13163. null,
  13164. "Hiedie kletskamer benodig 'n wagwoord"
  13165. ],
  13166. "Password: ": [
  13167. null,
  13168. "Wagwoord:"
  13169. ],
  13170. "Submit": [
  13171. null,
  13172. "Dien in"
  13173. ],
  13174. "This room is not anonymous": [
  13175. null,
  13176. "Hierdie vertrek is nie anoniem nie"
  13177. ],
  13178. "This room now shows unavailable members": [
  13179. null,
  13180. "Hierdie vertrek wys nou onbeskikbare lede"
  13181. ],
  13182. "This room does not show unavailable members": [
  13183. null,
  13184. "Hierdie vertrek wys nie onbeskikbare lede nie"
  13185. ],
  13186. "Non-privacy-related room configuration has changed": [
  13187. null,
  13188. "Nie-privaatheidverwante kamer instellings het verander"
  13189. ],
  13190. "Room logging is now enabled": [
  13191. null,
  13192. "Kamer log is nou aangeskakel"
  13193. ],
  13194. "Room logging is now disabled": [
  13195. null,
  13196. "Kamer log is nou afgeskakel"
  13197. ],
  13198. "This room is now non-anonymous": [
  13199. null,
  13200. "Hiedie kamer is nou nie anoniem nie"
  13201. ],
  13202. "This room is now semi-anonymous": [
  13203. null,
  13204. "Hierdie kamer is nou gedeeltelik anoniem"
  13205. ],
  13206. "This room is now fully-anonymous": [
  13207. null,
  13208. "Hierdie kamer is nou ten volle anoniem"
  13209. ],
  13210. "A new room has been created": [
  13211. null,
  13212. "'n Nuwe kamer is geskep"
  13213. ],
  13214. "Your nickname has been changed": [
  13215. null,
  13216. "Jou bynaam is verander"
  13217. ],
  13218. "<strong>%1$s</strong> has been banned": [
  13219. null,
  13220. "<strong>%1$s</strong> is verban"
  13221. ],
  13222. "<strong>%1$s</strong> has been kicked out": [
  13223. null,
  13224. "<strong>%1$s</strong> is uitgeskop"
  13225. ],
  13226. "<strong>%1$s</strong> has been removed because of an affiliation change": [
  13227. null,
  13228. "<strong>%1$s</strong> is verwyder a.g.v 'n verandering van affiliasie"
  13229. ],
  13230. "<strong>%1$s</strong> has been removed for not being a member": [
  13231. null,
  13232. "<strong>%1$s</strong> is nie 'n lid nie, en dus verwyder"
  13233. ],
  13234. "You have been banned from this room": [
  13235. null,
  13236. "Jy is uit die kamer verban"
  13237. ],
  13238. "You have been kicked from this room": [
  13239. null,
  13240. "Jy is uit die kamer geskop"
  13241. ],
  13242. "You have been removed from this room because of an affiliation change": [
  13243. null,
  13244. "Jy is vanuit die kamer verwyder a.g.v 'n verandering van affiliasie"
  13245. ],
  13246. "You have been removed from this room because the room has changed to members-only and you're not a member": [
  13247. null,
  13248. "Jy is vanuit die kamer verwyder omdat die kamer nou slegs tot lede beperk word en jy nie 'n lid is nie."
  13249. ],
  13250. "You have been removed from this room because the MUC (Multi-user chat) service is being shut down.": [
  13251. null,
  13252. "Jy is van hierdie kamer verwyder aangesien die MUC (Multi-user chat) diens nou afgeskakel word."
  13253. ],
  13254. "You are not on the member list of this room": [
  13255. null,
  13256. "Jy is nie op die ledelys van hierdie kamer nie"
  13257. ],
  13258. "No nickname was specified": [
  13259. null,
  13260. "Geen bynaam verskaf nie"
  13261. ],
  13262. "You are not allowed to create new rooms": [
  13263. null,
  13264. "Jy word nie toegelaat om nog kamers te skep nie"
  13265. ],
  13266. "Your nickname doesn't conform to this room's policies": [
  13267. null,
  13268. "Jou bynaam voldoen nie aan die kamer se beleid nie"
  13269. ],
  13270. "Your nickname is already taken": [
  13271. null,
  13272. "Jou bynaam is reeds geneem"
  13273. ],
  13274. "This room does not (yet) exist": [
  13275. null,
  13276. "Hierdie kamer bestaan tans (nog) nie"
  13277. ],
  13278. "This room has reached it's maximum number of occupants": [
  13279. null,
  13280. "Hierdie kamer het sy maksimum aantal deelnemers bereik"
  13281. ],
  13282. "Topic set by %1$s to: %2$s": [
  13283. null,
  13284. "Onderwerp deur %1$s bygewerk na: %2$s"
  13285. ],
  13286. "This user is a moderator": [
  13287. null,
  13288. "Hierdie gebruiker is 'n moderator"
  13289. ],
  13290. "This user can send messages in this room": [
  13291. null,
  13292. "Hierdie gebruiker kan boodskappe na die kamer stuur"
  13293. ],
  13294. "This user can NOT send messages in this room": [
  13295. null,
  13296. "Hierdie gebruiker kan NIE boodskappe na die kamer stuur nie"
  13297. ],
  13298. "Click to chat with this contact": [
  13299. null,
  13300. "Kliek om met hierdie kontak te klets"
  13301. ],
  13302. "Click to remove this contact": [
  13303. null,
  13304. "Kliek om hierdie kontak te verwyder"
  13305. ],
  13306. "This contact is busy": [
  13307. null,
  13308. "Hierdie persoon is besig"
  13309. ],
  13310. "This contact is online": [
  13311. null,
  13312. "Hierdie persoon is aanlyn"
  13313. ],
  13314. "This contact is offline": [
  13315. null,
  13316. "Hierdie persoon is aflyn"
  13317. ],
  13318. "This contact is unavailable": [
  13319. null,
  13320. "Hierdie persoon is onbeskikbaar"
  13321. ],
  13322. "This contact is away for an extended period": [
  13323. null,
  13324. "Hierdie persoon is vir lank afwesig"
  13325. ],
  13326. "This contact is away": [
  13327. null,
  13328. "Hierdie persoon is afwesig"
  13329. ],
  13330. "Contact requests": [
  13331. null,
  13332. "Kontak versoeke"
  13333. ],
  13334. "My contacts": [
  13335. null,
  13336. "My kontakte"
  13337. ],
  13338. "Pending contacts": [
  13339. null,
  13340. "Hangende kontakte"
  13341. ],
  13342. "Custom status": [
  13343. null,
  13344. "Doelgemaakte status"
  13345. ],
  13346. "Click to change your chat status": [
  13347. null,
  13348. "Kliek om jou klets-status te verander"
  13349. ],
  13350. "Click here to write a custom status message": [
  13351. null,
  13352. "Kliek hier om jou eie statusboodskap te skryf"
  13353. ],
  13354. "online": [
  13355. null,
  13356. "aangemeld"
  13357. ],
  13358. "busy": [
  13359. null,
  13360. "besig"
  13361. ],
  13362. "away for long": [
  13363. null,
  13364. "vir lank afwesig"
  13365. ],
  13366. "away": [
  13367. null,
  13368. "afwesig"
  13369. ],
  13370. "I am %1$s": [
  13371. null,
  13372. "Ek is %1$s"
  13373. ],
  13374. "Sign in": [
  13375. null,
  13376. "Teken in"
  13377. ],
  13378. "XMPP/Jabber Username:": [
  13379. null,
  13380. "XMPP/Jabber Gebruikersnaam:"
  13381. ],
  13382. "Password:": [
  13383. null,
  13384. "Wagwoord"
  13385. ],
  13386. "Log In": [
  13387. null,
  13388. "Meld aan"
  13389. ],
  13390. "BOSH Service URL:": [
  13391. null,
  13392. "BOSH bediener URL"
  13393. ],
  13394. "Online Contacts": [
  13395. null,
  13396. "Kontakte aangemeld"
  13397. ],
  13398. "Connected": [
  13399. null,
  13400. "Verbind"
  13401. ],
  13402. "Attached": [
  13403. null,
  13404. "Geheg"
  13405. ]
  13406. }
  13407. }
  13408. };
  13409. if (typeof define === 'function' && define.amd) {
  13410. define("af", ['jed'], function () {
  13411. return factory(new Jed(translations));
  13412. });
  13413. } else {
  13414. if (!window.locales) {
  13415. window.locales = {};
  13416. }
  13417. window.locales.af = factory(new Jed(translations));
  13418. }
  13419. }(this, function (af) {
  13420. return af;
  13421. }));
  13422. (function (root, factory) {
  13423. var translations = {
  13424. "domain": "converse",
  13425. "locale_data": {
  13426. "converse": {
  13427. "": {
  13428. "Project-Id-Version": "Converse.js 0.4",
  13429. "Report-Msgid-Bugs-To": "",
  13430. "POT-Creation-Date": "2013-09-15 21:55+0200",
  13431. "PO-Revision-Date": "2013-09-15 22:03+0200",
  13432. "Last-Translator": "JC Brand <jc@opkode.com>",
  13433. "Language-Team": "German",
  13434. "Language": "de",
  13435. "MIME-Version": "1.0",
  13436. "Content-Type": "text/plain; charset=UTF-8",
  13437. "Content-Transfer-Encoding": "8bit",
  13438. "Plural-Forms": "nplurals=2; plural=(n != 1);",
  13439. "domain": "converse",
  13440. "lang": "de",
  13441. "plural_forms": "nplurals=2; plural=(n != 1);"
  13442. },
  13443. "unencrypted": [
  13444. null,
  13445. ""
  13446. ],
  13447. "unverified": [
  13448. null,
  13449. ""
  13450. ],
  13451. "verified": [
  13452. null,
  13453. ""
  13454. ],
  13455. "finished": [
  13456. null,
  13457. ""
  13458. ],
  13459. "Disconnected": [
  13460. null,
  13461. "Verbindung unterbrochen."
  13462. ],
  13463. "Error": [
  13464. null,
  13465. "Fehler"
  13466. ],
  13467. "Connecting": [
  13468. null,
  13469. "Verbindungsaufbau …"
  13470. ],
  13471. "Connection Failed": [
  13472. null,
  13473. "Entfernte Verbindung fehlgeschlagen"
  13474. ],
  13475. "Authenticating": [
  13476. null,
  13477. "Authentifizierung"
  13478. ],
  13479. "Authentication Failed": [
  13480. null,
  13481. "Authentifizierung gescheitert"
  13482. ],
  13483. "Disconnecting": [
  13484. null,
  13485. "Trenne Verbindung"
  13486. ],
  13487. "Re-establishing encrypted session": [
  13488. null,
  13489. ""
  13490. ],
  13491. "Your browser needs to generate a private key, which will be used in your encrypted chat session. This can take up to 30 seconds during which your browser might freeze and become unresponsive.": [
  13492. null,
  13493. ""
  13494. ],
  13495. "Private key generated.": [
  13496. null,
  13497. ""
  13498. ],
  13499. "Authentication request from %1$s\n\nYour buddy is attempting to verify your identity, by asking you the question below.\n\n%2$s": [
  13500. null,
  13501. ""
  13502. ],
  13503. "Could not verify this user's identify.": [
  13504. null,
  13505. ""
  13506. ],
  13507. "Personal message": [
  13508. null,
  13509. "Persönliche Nachricht"
  13510. ],
  13511. "Start encrypted conversation": [
  13512. null,
  13513. ""
  13514. ],
  13515. "Refresh encrypted conversation": [
  13516. null,
  13517. ""
  13518. ],
  13519. "End encrypted conversation": [
  13520. null,
  13521. ""
  13522. ],
  13523. "Verify with SMP": [
  13524. null,
  13525. ""
  13526. ],
  13527. "Verify with fingerprints": [
  13528. null,
  13529. ""
  13530. ],
  13531. "What's this?": [
  13532. null,
  13533. ""
  13534. ],
  13535. "me": [
  13536. null,
  13537. "Ich"
  13538. ],
  13539. "Show this menu": [
  13540. null,
  13541. "Dieses Menü anzeigen"
  13542. ],
  13543. "Write in the third person": [
  13544. null,
  13545. "In der dritten Person schreiben"
  13546. ],
  13547. "Remove messages": [
  13548. null,
  13549. "Nachrichten entfernen"
  13550. ],
  13551. "Your message could not be sent": [
  13552. null,
  13553. ""
  13554. ],
  13555. "We received an unencrypted message": [
  13556. null,
  13557. ""
  13558. ],
  13559. "We received an unreadable encrypted message": [
  13560. null,
  13561. ""
  13562. ],
  13563. "This user has requested an encrypted session.": [
  13564. null,
  13565. ""
  13566. ],
  13567. "Here are the fingerprints, please confirm them with %1$s, outside of this chat.\n\nFingerprint for you, %2$s: %3$s\n\nFingerprint for %1$s: %4$s\n\nIf you have confirmed that the fingerprints match, click OK, otherwise click Cancel.": [
  13568. null,
  13569. ""
  13570. ],
  13571. "You will be prompted to provide a security question and then an answer to that question.\n\nYour buddy will then be prompted the same question and if they type the exact same answer (case sensitive), their identity will have been verified.": [
  13572. null,
  13573. ""
  13574. ],
  13575. "What is your security question?": [
  13576. null,
  13577. ""
  13578. ],
  13579. "What is the answer to the security question?": [
  13580. null,
  13581. ""
  13582. ],
  13583. "Invalid authentication scheme provided": [
  13584. null,
  13585. ""
  13586. ],
  13587. "Your messages are not encrypted anymore": [
  13588. null,
  13589. ""
  13590. ],
  13591. "Your messages are now encrypted but your buddy's identity has not been verified.": [
  13592. null,
  13593. ""
  13594. ],
  13595. "Your buddy's identify has been verified.": [
  13596. null,
  13597. ""
  13598. ],
  13599. "Your buddy has ended encryption on their end, you should do the same.": [
  13600. null,
  13601. ""
  13602. ],
  13603. "Your messages are not encrypted. Click here to enable OTR encryption.": [
  13604. null,
  13605. ""
  13606. ],
  13607. "Your messages are encrypted, but your buddy has not been verified.": [
  13608. null,
  13609. ""
  13610. ],
  13611. "Your messages are encrypted and your buddy verified.": [
  13612. null,
  13613. ""
  13614. ],
  13615. "Your buddy has closed their end of the private session, you should do the same": [
  13616. null,
  13617. ""
  13618. ],
  13619. "Contacts": [
  13620. null,
  13621. "Kontakte"
  13622. ],
  13623. "Online": [
  13624. null,
  13625. "Online"
  13626. ],
  13627. "Busy": [
  13628. null,
  13629. "Beschäfticht"
  13630. ],
  13631. "Away": [
  13632. null,
  13633. "Abwesend"
  13634. ],
  13635. "Offline": [
  13636. null,
  13637. "Abgemeldet"
  13638. ],
  13639. "Click to add new chat contacts": [
  13640. null,
  13641. "Klicken Sie, um einen neuen Kontakt hinzuzufügen"
  13642. ],
  13643. "Add a contact": [
  13644. null,
  13645. "Kontakte hinzufügen"
  13646. ],
  13647. "Contact username": [
  13648. null,
  13649. "Benutzername"
  13650. ],
  13651. "Add": [
  13652. null,
  13653. "Hinzufügen"
  13654. ],
  13655. "Contact name": [
  13656. null,
  13657. "Name des Kontakts"
  13658. ],
  13659. "Search": [
  13660. null,
  13661. "Suche"
  13662. ],
  13663. "No users found": [
  13664. null,
  13665. "Keine Benutzer gefunden"
  13666. ],
  13667. "Click to add as a chat contact": [
  13668. null,
  13669. "Hier klicken um als Kontakt hinzuzufügen"
  13670. ],
  13671. "Click to open this room": [
  13672. null,
  13673. "Hier klicken um diesen Raum zu öffnen"
  13674. ],
  13675. "Show more information on this room": [
  13676. null,
  13677. "Mehr Information über diesen Raum zeigen"
  13678. ],
  13679. "Description:": [
  13680. null,
  13681. "Beschreibung"
  13682. ],
  13683. "Occupants:": [
  13684. null,
  13685. "Teilnehmer"
  13686. ],
  13687. "Features:": [
  13688. null,
  13689. "Funktionen:"
  13690. ],
  13691. "Requires authentication": [
  13692. null,
  13693. "Authentifizierung erforderlich"
  13694. ],
  13695. "Hidden": [
  13696. null,
  13697. "Versteckt"
  13698. ],
  13699. "Requires an invitation": [
  13700. null,
  13701. "Einladung erforderlich"
  13702. ],
  13703. "Moderated": [
  13704. null,
  13705. "Moderiert"
  13706. ],
  13707. "Non-anonymous": [
  13708. null,
  13709. "Nicht anonym"
  13710. ],
  13711. "Open room": [
  13712. null,
  13713. "Offener Raum"
  13714. ],
  13715. "Permanent room": [
  13716. null,
  13717. "Dauerhafter Raum"
  13718. ],
  13719. "Public": [
  13720. null,
  13721. "Öffentlich"
  13722. ],
  13723. "Semi-anonymous": [
  13724. null,
  13725. "Teils anonym"
  13726. ],
  13727. "Temporary room": [
  13728. null,
  13729. "Vorübergehender Raum"
  13730. ],
  13731. "Unmoderated": [
  13732. null,
  13733. "Unmoderiert"
  13734. ],
  13735. "Rooms": [
  13736. null,
  13737. "Räume"
  13738. ],
  13739. "Room name": [
  13740. null,
  13741. "Raumname"
  13742. ],
  13743. "Nickname": [
  13744. null,
  13745. "Spitzname"
  13746. ],
  13747. "Server": [
  13748. null,
  13749. "Server"
  13750. ],
  13751. "Join": [
  13752. null,
  13753. "Beitreten"
  13754. ],
  13755. "Show rooms": [
  13756. null,
  13757. "Räume anzeigen"
  13758. ],
  13759. "No rooms on %1$s": [
  13760. null,
  13761. "Keine Räume auf %1$s"
  13762. ],
  13763. "Rooms on %1$s": [
  13764. null,
  13765. "Räume auf %1$s"
  13766. ],
  13767. "Set chatroom topic": [
  13768. null,
  13769. "Chatraum Thema festlegen"
  13770. ],
  13771. "Kick user from chatroom": [
  13772. null,
  13773. "Werfe einen Benutzer aus dem Raum."
  13774. ],
  13775. "Ban user from chatroom": [
  13776. null,
  13777. "Verbanne einen Benutzer aus dem Raum."
  13778. ],
  13779. "Message": [
  13780. null,
  13781. "Nachricht"
  13782. ],
  13783. "Save": [
  13784. null,
  13785. "Speichern"
  13786. ],
  13787. "Cancel": [
  13788. null,
  13789. "Abbrechen"
  13790. ],
  13791. "An error occurred while trying to save the form.": [
  13792. null,
  13793. "Beim Speichern der Formular is ein Fehler aufgetreten."
  13794. ],
  13795. "This chatroom requires a password": [
  13796. null,
  13797. "Passwort wird für die Anmeldung benötigt."
  13798. ],
  13799. "Password: ": [
  13800. null,
  13801. "Passwort: "
  13802. ],
  13803. "Submit": [
  13804. null,
  13805. "Einreichen"
  13806. ],
  13807. "This room is not anonymous": [
  13808. null,
  13809. "Dieser Raum ist nicht anonym"
  13810. ],
  13811. "This room now shows unavailable members": [
  13812. null,
  13813. "Dieser Raum zeigt jetzt unferfügbare Mitglieder"
  13814. ],
  13815. "This room does not show unavailable members": [
  13816. null,
  13817. "Dieser Raum zeigt nicht unverfügbare Mitglieder"
  13818. ],
  13819. "Non-privacy-related room configuration has changed": [
  13820. null,
  13821. "Die Konfiguration, die nicht auf die Privatsphäre bezogen ist, hat sich geändert"
  13822. ],
  13823. "Room logging is now enabled": [
  13824. null,
  13825. "Zukünftige Nachrichten dieses Raums werden protokolliert."
  13826. ],
  13827. "Room logging is now disabled": [
  13828. null,
  13829. "Zukünftige Nachrichten dieses Raums werden nicht protokolliert."
  13830. ],
  13831. "This room is now non-anonymous": [
  13832. null,
  13833. "Dieser Raum ist jetzt nicht anonym"
  13834. ],
  13835. "This room is now semi-anonymous": [
  13836. null,
  13837. "Dieser Raum ist jetzt teils anonym"
  13838. ],
  13839. "This room is now fully-anonymous": [
  13840. null,
  13841. "Dieser Raum ist jetzt anonym"
  13842. ],
  13843. "A new room has been created": [
  13844. null,
  13845. "Einen neuen Raum ist erstellen"
  13846. ],
  13847. "Your nickname has been changed": [
  13848. null,
  13849. "Spitzname festgelegen"
  13850. ],
  13851. "<strong>%1$s</strong> has been banned": [
  13852. null,
  13853. "<strong>%1$s</strong> ist verbannt"
  13854. ],
  13855. "<strong>%1$s</strong> has been kicked out": [
  13856. null,
  13857. "<strong>%1$s</strong> ist hinausgeworfen"
  13858. ],
  13859. "<strong>%1$s</strong> has been removed because of an affiliation change": [
  13860. null,
  13861. "<strong>%1$s</strong> wurde wegen einer Zugehörigkeitsänderung entfernt"
  13862. ],
  13863. "<strong>%1$s</strong> has been removed for not being a member": [
  13864. null,
  13865. "<strong>%1$s</strong> ist kein Mitglied und wurde daher entfernt"
  13866. ],
  13867. "You have been banned from this room": [
  13868. null,
  13869. "Sie sind aus diesem Raum verbannt worden"
  13870. ],
  13871. "You have been kicked from this room": [
  13872. null,
  13873. "Sie wurden aus diesem Raum hinausgeworfen"
  13874. ],
  13875. "You have been removed from this room because of an affiliation change": [
  13876. null,
  13877. "Sie wurden wegen einer Zugehörigkeitsänderung entfernt"
  13878. ],
  13879. "You have been removed from this room because the room has changed to members-only and you're not a member": [
  13880. null,
  13881. "Sie wurden aus diesem Raum entfernt da Sie kein Mitglied sind."
  13882. ],
  13883. "You have been removed from this room because the MUC (Multi-user chat) service is being shut down.": [
  13884. null,
  13885. "Sie werden aus diesem Raum entfernt da der MUC (Muli-user chat) Dienst gerade abgeschalten wird."
  13886. ],
  13887. "You are not on the member list of this room": [
  13888. null,
  13889. "Sie sind nicht auf der Mitgliederliste dieses Raums"
  13890. ],
  13891. "No nickname was specified": [
  13892. null,
  13893. "Kein Spitzname festgelegt"
  13894. ],
  13895. "You are not allowed to create new rooms": [
  13896. null,
  13897. "Es ist Ihnen nicht erlaubt, neue Räume anzulegen"
  13898. ],
  13899. "Your nickname doesn't conform to this room's policies": [
  13900. null,
  13901. "Ungültiger Spitzname"
  13902. ],
  13903. "Your nickname is already taken": [
  13904. null,
  13905. "Ihre Spitzname existiert bereits."
  13906. ],
  13907. "This room does not (yet) exist": [
  13908. null,
  13909. "Dieser Raum existiert (noch) nicht"
  13910. ],
  13911. "This room has reached it's maximum number of occupants": [
  13912. null,
  13913. "Dieser Raum hat die maximale Mitgliederanzahl erreicht"
  13914. ],
  13915. "Topic set by %1$s to: %2$s": [
  13916. null,
  13917. "%1$s hat das Thema zu \"%2$s\" abgeändert"
  13918. ],
  13919. "This user is a moderator": [
  13920. null,
  13921. "Dieser Benutzer ist ein Moderator"
  13922. ],
  13923. "This user can send messages in this room": [
  13924. null,
  13925. "Dieser Benutzer kann Nachrichten in diesem Raum verschicken"
  13926. ],
  13927. "This user can NOT send messages in this room": [
  13928. null,
  13929. "Dieser Benutzer kann keine Nachrichten in diesem Raum verschicken"
  13930. ],
  13931. "Click to chat with this contact": [
  13932. null,
  13933. "Hier klicken um mit diesem Kontakt zu chatten"
  13934. ],
  13935. "Click to remove this contact": [
  13936. null,
  13937. "Hier klicken um diesen Kontakt zu entfernen"
  13938. ],
  13939. "This contact is busy": [
  13940. null,
  13941. "Dieser Kontakt ist beschäfticht"
  13942. ],
  13943. "This contact is online": [
  13944. null,
  13945. "Dieser Kontakt ist online"
  13946. ],
  13947. "This contact is offline": [
  13948. null,
  13949. "Dieser Kontakt ist offline"
  13950. ],
  13951. "This contact is unavailable": [
  13952. null,
  13953. "Dieser Kontakt ist nicht verfügbar"
  13954. ],
  13955. "This contact is away for an extended period": [
  13956. null,
  13957. "Dieser Kontakt is für längere Zeit abwesend"
  13958. ],
  13959. "This contact is away": [
  13960. null,
  13961. "Dieser Kontakt ist abwesend"
  13962. ],
  13963. "Contact requests": [
  13964. null,
  13965. "Kontaktanfragen"
  13966. ],
  13967. "My contacts": [
  13968. null,
  13969. "Meine Kontakte"
  13970. ],
  13971. "Pending contacts": [
  13972. null,
  13973. "Unbestätigte Kontakte"
  13974. ],
  13975. "Custom status": [
  13976. null,
  13977. "Status-Nachricht"
  13978. ],
  13979. "Click to change your chat status": [
  13980. null,
  13981. "Klicken Sie, um ihrer Status to ändern"
  13982. ],
  13983. "Click here to write a custom status message": [
  13984. null,
  13985. "Klicken Sie hier, um ihrer Status-Nachricht to ändern"
  13986. ],
  13987. "online": [
  13988. null,
  13989. "online"
  13990. ],
  13991. "busy": [
  13992. null,
  13993. "beschäfticht"
  13994. ],
  13995. "away for long": [
  13996. null,
  13997. "länger abwesend"
  13998. ],
  13999. "away": [
  14000. null,
  14001. "abwesend"
  14002. ],
  14003. "I am %1$s": [
  14004. null,
  14005. "Ich bin %1$s"
  14006. ],
  14007. "Sign in": [
  14008. null,
  14009. "Anmelden"
  14010. ],
  14011. "XMPP/Jabber Username:": [
  14012. null,
  14013. "XMPP/Jabber Benutzername"
  14014. ],
  14015. "Password:": [
  14016. null,
  14017. "Passwort:"
  14018. ],
  14019. "Log In": [
  14020. null,
  14021. "Anmelden"
  14022. ],
  14023. "BOSH Service URL:": [
  14024. null,
  14025. "BOSH "
  14026. ],
  14027. "Online Contacts": [
  14028. null,
  14029. "Online-Kontakte"
  14030. ],
  14031. "%1$s is typing": [
  14032. null,
  14033. "%1$s tippt"
  14034. ],
  14035. "Connected": [
  14036. null,
  14037. "Verbunden"
  14038. ],
  14039. "Attached": [
  14040. null,
  14041. "Angehängt"
  14042. ]
  14043. }
  14044. }
  14045. };
  14046. if (typeof define === 'function' && define.amd) {
  14047. define("de", ['jed'], function () {
  14048. return factory(new Jed(translations));
  14049. });
  14050. } else {
  14051. if (!window.locales) {
  14052. window.locales = {};
  14053. }
  14054. window.locales.de = factory(new Jed(translations));
  14055. }
  14056. }(this, function (de) {
  14057. return de;
  14058. }));
  14059. (function (root, factory) {
  14060. var translations = {
  14061. "domain": "converse",
  14062. "locale_data": {
  14063. "converse": {
  14064. "": {
  14065. "domain": "converse",
  14066. "lang": "en",
  14067. "plural_forms": "nplurals=2; plural=(n != 1);"
  14068. }
  14069. }
  14070. }
  14071. };
  14072. if (typeof define === 'function' && define.amd) {
  14073. define("en", ['jed'], function () {
  14074. return factory(new Jed(translations));
  14075. });
  14076. } else {
  14077. if (!window.locales) {
  14078. window.locales = {};
  14079. }
  14080. window.locales.en = factory(new Jed(translations));
  14081. }
  14082. }(this, function (en) {
  14083. return en;
  14084. }));
  14085. (function (root, factory) {
  14086. var translations = {
  14087. "domain": "converse",
  14088. "locale_data": {
  14089. "converse": {
  14090. "": {
  14091. "Project-Id-Version": "Converse.js 0.4",
  14092. "Report-Msgid-Bugs-To": "",
  14093. "POT-Creation-Date": "2013-09-15 21:55+0200",
  14094. "PO-Revision-Date": "2013-09-15 21:59+0200",
  14095. "Last-Translator": "Javier Lopez <m@javier.io>",
  14096. "Language-Team": "ES <LL@li.org>",
  14097. "Language": "es",
  14098. "MIME-Version": "1.0",
  14099. "Content-Type": "text/plain; charset=UTF-8",
  14100. "Content-Transfer-Encoding": "8bit",
  14101. "Plural-Forms": "nplurals=2; plural=(n != 1);",
  14102. "plural_forms": "nplurals=2; plural=(n != 1);",
  14103. "lang": "es",
  14104. "Language-Code": "es",
  14105. "Language-Name": "Español",
  14106. "Preferred-Encodings": "utf-8 latin1",
  14107. "Domain": "converse",
  14108. "domain": "converse",
  14109. "X-Is-Fallback-For": "es-ar es-bo es-cl es-co es-cr es-do es-ec es-es es-sv es-gt es-hn es-mx es-ni es-pa es-py es-pe es-pr es-us es-uy es-ve"
  14110. },
  14111. "unencrypted": [
  14112. null,
  14113. "texto plano"
  14114. ],
  14115. "unverified": [
  14116. null,
  14117. "sin verificar"
  14118. ],
  14119. "verified": [
  14120. null,
  14121. "verificado"
  14122. ],
  14123. "finished": [
  14124. null,
  14125. "finalizado"
  14126. ],
  14127. "Disconnected": [
  14128. null,
  14129. "Desconectado"
  14130. ],
  14131. "Error": [
  14132. null,
  14133. "Error"
  14134. ],
  14135. "Connecting": [
  14136. null,
  14137. "Conectando"
  14138. ],
  14139. "Connection Failed": [
  14140. null,
  14141. "La conexión falló"
  14142. ],
  14143. "Authenticating": [
  14144. null,
  14145. "Autenticando"
  14146. ],
  14147. "Authentication Failed": [
  14148. null,
  14149. "La autenticación falló"
  14150. ],
  14151. "Disconnecting": [
  14152. null,
  14153. "Desconectando"
  14154. ],
  14155. "Re-establishing encrypted session": [
  14156. null,
  14157. "Re-estableciendo sesión cifrada"
  14158. ],
  14159. "Your browser needs to generate a private key, which will be used in your encrypted chat session. This can take up to 30 seconds during which your browser might freeze and become unresponsive.": [
  14160. null,
  14161. "Su navegador generará una llave privada para usarse en la sesión cifrada. Esto puede tomar hasta 30 segundo, durante este tiempo su navegador puede dejar de responder."
  14162. ],
  14163. "Private key generated.": [
  14164. null,
  14165. "Llave privada generada"
  14166. ],
  14167. "Authentication request from %1$s\n\nYour buddy is attempting to verify your identity, by asking you the question below.\n\n%2$s": [
  14168. null,
  14169. "Petición de autenticación de %1$s\n\nSu contacto intenta verificar su identidad haciendo la siguiente pregunta.\n\n%2$s"
  14170. ],
  14171. "Could not verify this user's identify.": [
  14172. null,
  14173. "No se pudo verificar la identidad de este usuario"
  14174. ],
  14175. "Personal message": [
  14176. null,
  14177. "Mensaje personal"
  14178. ],
  14179. "Start encrypted conversation": [
  14180. null,
  14181. "Iniciar sesión cifrada"
  14182. ],
  14183. "Refresh encrypted conversation": [
  14184. null,
  14185. "Actualizar sesión cifrada"
  14186. ],
  14187. "End encrypted conversation": [
  14188. null,
  14189. "Finalizar sesión cifrada"
  14190. ],
  14191. "Verify with SMP": [
  14192. null,
  14193. "Verificar con SMP"
  14194. ],
  14195. "Verify with fingerprints": [
  14196. null,
  14197. "Verificar con identificadores"
  14198. ],
  14199. "What's this?": [
  14200. null,
  14201. "¿Qué es esto?"
  14202. ],
  14203. "me": [
  14204. null,
  14205. "yo"
  14206. ],
  14207. "Show this menu": [
  14208. null,
  14209. "Mostrar este menú"
  14210. ],
  14211. "Write in the third person": [
  14212. null,
  14213. "Escribir en tercera persona"
  14214. ],
  14215. "Remove messages": [
  14216. null,
  14217. "Eliminar mensajes"
  14218. ],
  14219. "Your message could not be sent": [
  14220. null,
  14221. "Su mensaje no se pudo enviar"
  14222. ],
  14223. "We received an unencrypted message": [
  14224. null,
  14225. "Se recibío un mensaje sin cifrar"
  14226. ],
  14227. "We received an unreadable encrypted message": [
  14228. null,
  14229. "Se recibío un mensaje cifrado corrupto"
  14230. ],
  14231. "This user has requested an encrypted session.": [
  14232. null,
  14233. "El usuario ha solicitado una sesión cifrada"
  14234. ],
  14235. "Here are the fingerprints, please confirm them with %1$s, outside of this chat.\n\nFingerprint for you, %2$s: %3$s\n\nFingerprint for %1$s: %4$s\n\nIf you have confirmed that the fingerprints match, click OK, otherwise click Cancel.": [
  14236. null,
  14237. "Por favor confirme los identificadores de %1$s fuera de este chat\n\n. Su identificador es, %2$s: %3$s\n\n. El identificador de %1$s es: %4$s\n\n. Después de confirmar los identificadores haga click en OK, cancele si no concuerdan."
  14238. ],
  14239. "You will be prompted to provide a security question and then an answer to that question.\n\nYour buddy will then be prompted the same question and if they type the exact same answer (case sensitive), their identity will have been verified.": [
  14240. null,
  14241. "Se le solicitará una pregunta de seguridad.\n\n. La pregunta que responda se le hará a su contacto, si las respuestas concuerdan (cuidando mayúsculas/minúsculas) su identidad quedará verificada."
  14242. ],
  14243. "What is your security question?": [
  14244. null,
  14245. "Introduzca su pregunta de seguridad"
  14246. ],
  14247. "What is the answer to the security question?": [
  14248. null,
  14249. "Introduzca la respuesta a su pregunta de seguridad"
  14250. ],
  14251. "Invalid authentication scheme provided": [
  14252. null,
  14253. "Esquema de autenticación inválido"
  14254. ],
  14255. "Your messages are not encrypted anymore": [
  14256. null,
  14257. "Sus mensajes han dejado de cifrarse"
  14258. ],
  14259. "Your messages are now encrypted but your buddy's identity has not been verified.": [
  14260. null,
  14261. "Sus mensajes están ahora cifrados pero la identidad de su contacto no ha sido verificada"
  14262. ],
  14263. "Your buddy's identify has been verified.": [
  14264. null,
  14265. "La identidad de su contacto ha sido confirmada"
  14266. ],
  14267. "Your buddy has ended encryption on their end, you should do the same.": [
  14268. null,
  14269. "Su contacto finalizó la sesión cifrada, debería hacer lo mismo"
  14270. ],
  14271. "Your messages are not encrypted. Click here to enable OTR encryption.": [
  14272. null,
  14273. "Sus mensajes no están cifrados. Haga click aquí para habilitar el cifrado OTR"
  14274. ],
  14275. "Your messages are encrypted, but your buddy has not been verified.": [
  14276. null,
  14277. "Sus mensajes están cifrados pero la identidad de su contacto no ha sido verificada"
  14278. ],
  14279. "Your messages are encrypted and your buddy verified.": [
  14280. null,
  14281. "Sus mensajes están cifrados y su contacto ha sido verificado"
  14282. ],
  14283. "Your buddy has closed their end of the private session, you should do the same": [
  14284. null,
  14285. "Su contacto finalizó la sesión cifrada, debería hacer lo mismo"
  14286. ],
  14287. "Contacts": [
  14288. null,
  14289. "Contactos"
  14290. ],
  14291. "Online": [
  14292. null,
  14293. "En linea"
  14294. ],
  14295. "Busy": [
  14296. null,
  14297. "Ocupado"
  14298. ],
  14299. "Away": [
  14300. null,
  14301. "Ausente"
  14302. ],
  14303. "Offline": [
  14304. null,
  14305. "Desconectado"
  14306. ],
  14307. "Click to add new chat contacts": [
  14308. null,
  14309. "Haga click para agregar nuevos contactos al chat"
  14310. ],
  14311. "Add a contact": [
  14312. null,
  14313. "Agregar un contacto"
  14314. ],
  14315. "Contact username": [
  14316. null,
  14317. "Nombre de usuario de contacto"
  14318. ],
  14319. "Add": [
  14320. null,
  14321. "Agregar"
  14322. ],
  14323. "Contact name": [
  14324. null,
  14325. "Nombre de contacto"
  14326. ],
  14327. "Search": [
  14328. null,
  14329. "Búsqueda"
  14330. ],
  14331. "No users found": [
  14332. null,
  14333. "Sin usuarios encontrados"
  14334. ],
  14335. "Click to add as a chat contact": [
  14336. null,
  14337. "Haga click para agregar como contacto de chat"
  14338. ],
  14339. "Click to open this room": [
  14340. null,
  14341. "Haga click para abrir esta sala"
  14342. ],
  14343. "Show more information on this room": [
  14344. null,
  14345. "Mostrar más información en esta sala"
  14346. ],
  14347. "Description:": [
  14348. null,
  14349. "Descripción"
  14350. ],
  14351. "Occupants:": [
  14352. null,
  14353. "Ocupantes:"
  14354. ],
  14355. "Features:": [
  14356. null,
  14357. "Características:"
  14358. ],
  14359. "Requires authentication": [
  14360. null,
  14361. "Autenticación requerida"
  14362. ],
  14363. "Hidden": [
  14364. null,
  14365. "Oculto"
  14366. ],
  14367. "Requires an invitation": [
  14368. null,
  14369. "Requiere una invitación"
  14370. ],
  14371. "Moderated": [
  14372. null,
  14373. "Moderado"
  14374. ],
  14375. "Non-anonymous": [
  14376. null,
  14377. "No anónimo"
  14378. ],
  14379. "Open room": [
  14380. null,
  14381. "Abrir sala"
  14382. ],
  14383. "Permanent room": [
  14384. null,
  14385. "Sala permanente"
  14386. ],
  14387. "Public": [
  14388. null,
  14389. "Publico"
  14390. ],
  14391. "Semi-anonymous": [
  14392. null,
  14393. "Semi anónimo"
  14394. ],
  14395. "Temporary room": [
  14396. null,
  14397. "Sala temporal"
  14398. ],
  14399. "Unmoderated": [
  14400. null,
  14401. "Sin moderar"
  14402. ],
  14403. "Rooms": [
  14404. null,
  14405. "Salas"
  14406. ],
  14407. "Room name": [
  14408. null,
  14409. "Nombre de sala"
  14410. ],
  14411. "Nickname": [
  14412. null,
  14413. "Apodo"
  14414. ],
  14415. "Server": [
  14416. null,
  14417. "Servidor"
  14418. ],
  14419. "Join": [
  14420. null,
  14421. "Unirse"
  14422. ],
  14423. "Show rooms": [
  14424. null,
  14425. "Mostrar salas"
  14426. ],
  14427. "No rooms on %1$s": [
  14428. null,
  14429. "Sin salas en %1$s"
  14430. ],
  14431. "Rooms on %1$s": [
  14432. null,
  14433. "Salas en %1$s"
  14434. ],
  14435. "Set chatroom topic": [
  14436. null,
  14437. "Definir tema de sala de chat"
  14438. ],
  14439. "Kick user from chatroom": [
  14440. null,
  14441. "Expulsar usuario de sala de chat."
  14442. ],
  14443. "Ban user from chatroom": [
  14444. null,
  14445. "Bloquear usuario de sala de chat."
  14446. ],
  14447. "Message": [
  14448. null,
  14449. "Mensaje"
  14450. ],
  14451. "Save": [
  14452. null,
  14453. "Guardar"
  14454. ],
  14455. "Cancel": [
  14456. null,
  14457. "Cancelar"
  14458. ],
  14459. "An error occurred while trying to save the form.": [
  14460. null,
  14461. "Un error ocurrío mientras se guardaba el formulario."
  14462. ],
  14463. "This chatroom requires a password": [
  14464. null,
  14465. "Esta sala de chat requiere una contraseña."
  14466. ],
  14467. "Password: ": [
  14468. null,
  14469. "Contraseña: "
  14470. ],
  14471. "Submit": [
  14472. null,
  14473. "Enviar"
  14474. ],
  14475. "This room is not anonymous": [
  14476. null,
  14477. "Esta sala no es para usuarios anónimos"
  14478. ],
  14479. "This room now shows unavailable members": [
  14480. null,
  14481. "Esta sala ahora muestra los miembros no disponibles"
  14482. ],
  14483. "This room does not show unavailable members": [
  14484. null,
  14485. "Esta sala no muestra los miembros no disponibles"
  14486. ],
  14487. "Non-privacy-related room configuration has changed": [
  14488. null,
  14489. "Una configuración de la sala no relacionada con la privacidad ha sido cambiada"
  14490. ],
  14491. "Room logging is now enabled": [
  14492. null,
  14493. "El registro de la sala ahora está habilitado"
  14494. ],
  14495. "Room logging is now disabled": [
  14496. null,
  14497. "El registro de la sala ahora está deshabilitado"
  14498. ],
  14499. "This room is now non-anonymous": [
  14500. null,
  14501. "Esta sala ahora es pública"
  14502. ],
  14503. "This room is now semi-anonymous": [
  14504. null,
  14505. "Esta sala ahora es semi-anónima"
  14506. ],
  14507. "This room is now fully-anonymous": [
  14508. null,
  14509. "Esta sala ahora es completamente anónima"
  14510. ],
  14511. "A new room has been created": [
  14512. null,
  14513. "Una nueva sala ha sido creada"
  14514. ],
  14515. "Your nickname has been changed": [
  14516. null,
  14517. "Su apodo ha sido cambiado"
  14518. ],
  14519. "<strong>%1$s</strong> has been banned": [
  14520. null,
  14521. "<strong>%1$s</strong> ha sido bloqueado"
  14522. ],
  14523. "<strong>%1$s</strong> has been kicked out": [
  14524. null,
  14525. "<strong>%1$s</strong> ha sido expulsado"
  14526. ],
  14527. "<strong>%1$s</strong> has been removed because of an affiliation change": [
  14528. null,
  14529. "<strong>%1$s</strong> ha sido eliminado debido a un cambio de afiliación"
  14530. ],
  14531. "<strong>%1$s</strong> has been removed for not being a member": [
  14532. null,
  14533. "<strong>%1$s</strong> ha sido eliminado debido a que no es miembro"
  14534. ],
  14535. "You have been banned from this room": [
  14536. null,
  14537. "Usted ha sido bloqueado de esta sala"
  14538. ],
  14539. "You have been kicked from this room": [
  14540. null,
  14541. "Usted ha sido expulsado de esta sala"
  14542. ],
  14543. "You have been removed from this room because of an affiliation change": [
  14544. null,
  14545. "Usted ha sido eliminado de esta sala debido a un cambio de afiliación"
  14546. ],
  14547. "You have been removed from this room because the room has changed to members-only and you're not a member": [
  14548. null,
  14549. "Usted ha sido eliminado de esta sala debido a que la sala cambio su configuración a solo-miembros y usted no es un miembro"
  14550. ],
  14551. "You have been removed from this room because the MUC (Multi-user chat) service is being shut down.": [
  14552. null,
  14553. "Usted ha sido eliminado de esta sala debido a que el servicio MUC (Multi-user chat) está deshabilitado."
  14554. ],
  14555. "You are not on the member list of this room": [
  14556. null,
  14557. "Usted no está en la lista de miembros de esta sala"
  14558. ],
  14559. "No nickname was specified": [
  14560. null,
  14561. "Sin apodo especificado"
  14562. ],
  14563. "You are not allowed to create new rooms": [
  14564. null,
  14565. "Usted no esta autorizado para crear nuevas salas"
  14566. ],
  14567. "Your nickname doesn't conform to this room's policies": [
  14568. null,
  14569. "Su apodo no se ajusta a la política de esta sala"
  14570. ],
  14571. "Your nickname is already taken": [
  14572. null,
  14573. "Su apodo ya ha sido tomando por otro usuario"
  14574. ],
  14575. "This room does not (yet) exist": [
  14576. null,
  14577. "Esta sala (aún) no existe"
  14578. ],
  14579. "This room has reached it's maximum number of occupants": [
  14580. null,
  14581. "Esta sala ha alcanzado su número máximo de ocupantes"
  14582. ],
  14583. "Topic set by %1$s to: %2$s": [
  14584. null,
  14585. "Tema fijado por %1$s a: %2$s"
  14586. ],
  14587. "This user is a moderator": [
  14588. null,
  14589. "Este usuario es un moderador"
  14590. ],
  14591. "This user can send messages in this room": [
  14592. null,
  14593. "Este usuario puede enviar mensajes en esta sala"
  14594. ],
  14595. "This user can NOT send messages in this room": [
  14596. null,
  14597. "Este usuario NO puede enviar mensajes en esta"
  14598. ],
  14599. "Click to chat with this contact": [
  14600. null,
  14601. "Haga click para conversar con este contacto"
  14602. ],
  14603. "Click to remove this contact": [
  14604. null,
  14605. "Haga click para eliminar este contacto"
  14606. ],
  14607. "This contact is busy": [
  14608. null,
  14609. "Este contacto está ocupado"
  14610. ],
  14611. "This contact is online": [
  14612. null,
  14613. "Este contacto está en línea"
  14614. ],
  14615. "This contact is offline": [
  14616. null,
  14617. "Este contacto está desconectado"
  14618. ],
  14619. "This contact is unavailable": [
  14620. null,
  14621. "Este contacto no está disponible"
  14622. ],
  14623. "This contact is away for an extended period": [
  14624. null,
  14625. "Este contacto está ausente por un largo periodo de tiempo"
  14626. ],
  14627. "This contact is away": [
  14628. null,
  14629. "Este contacto está ausente"
  14630. ],
  14631. "Contact requests": [
  14632. null,
  14633. "Solicitudes de contacto"
  14634. ],
  14635. "My contacts": [
  14636. null,
  14637. "Mis contactos"
  14638. ],
  14639. "Pending contacts": [
  14640. null,
  14641. "Contactos pendientes"
  14642. ],
  14643. "Custom status": [
  14644. null,
  14645. "Personalizar estatus"
  14646. ],
  14647. "Click to change your chat status": [
  14648. null,
  14649. "Haga click para cambiar su estatus de chat"
  14650. ],
  14651. "Click here to write a custom status message": [
  14652. null,
  14653. "Haga click para escribir un mensaje de estatus personalizado"
  14654. ],
  14655. "online": [
  14656. null,
  14657. "en línea"
  14658. ],
  14659. "busy": [
  14660. null,
  14661. "ocupado"
  14662. ],
  14663. "away for long": [
  14664. null,
  14665. "ausente por mucho tiempo"
  14666. ],
  14667. "away": [
  14668. null,
  14669. "ausente"
  14670. ],
  14671. "I am %1$s": [
  14672. null,
  14673. "Estoy %1$s"
  14674. ],
  14675. "Sign in": [
  14676. null,
  14677. "Registrar"
  14678. ],
  14679. "XMPP/Jabber Username:": [
  14680. null,
  14681. "Nombre de usuario XMPP/Jabber"
  14682. ],
  14683. "Password:": [
  14684. null,
  14685. "Contraseña:"
  14686. ],
  14687. "Log In": [
  14688. null,
  14689. "Iniciar sesión"
  14690. ],
  14691. "BOSH Service URL:": [
  14692. null,
  14693. "URL del servicio BOSH:"
  14694. ],
  14695. "Online Contacts": [
  14696. null,
  14697. "En línea"
  14698. ],
  14699. "Connected": [
  14700. null,
  14701. "Conectado"
  14702. ],
  14703. "Attached": [
  14704. null,
  14705. "Adjuntado"
  14706. ]
  14707. }
  14708. }
  14709. };
  14710. if (typeof define === 'function' && define.amd) {
  14711. define("es", ['jed'], function () {
  14712. return factory(new Jed(translations));
  14713. });
  14714. } else {
  14715. if (!window.locales) {
  14716. window.locales = {};
  14717. }
  14718. window.locales.es = factory(new Jed(translations));
  14719. }
  14720. }(this, function (es) {
  14721. return es;
  14722. }));
  14723. (function (root, factory) {
  14724. var translations = {
  14725. "domain": "converse",
  14726. "locale_data": {
  14727. "converse": {
  14728. "": {
  14729. "Project-Id-Version": "Converse.js 0.4",
  14730. "Report-Msgid-Bugs-To": "",
  14731. "POT-Creation-Date": "2013-09-15 21:55+0200",
  14732. "PO-Revision-Date": "2013-09-15 21:58+0200",
  14733. "Language-Team": "FR <LL@li.org>",
  14734. "Language": "fr",
  14735. "MIME-Version": "1.0",
  14736. "Content-Type": "text/plain; charset=UTF-8",
  14737. "Content-Transfer-Encoding": "8bit",
  14738. "Plural-Forms": "nplurals=2; plural=(n != 1);",
  14739. "plural_forms": "nplurals=2; plural=(n != 1);",
  14740. "lang": "fr",
  14741. "Language-Code": "fr",
  14742. "Preferred-Encodings": "utf-8 latin1",
  14743. "Domain": "converse",
  14744. "domain": "converse"
  14745. },
  14746. "unencrypted": [
  14747. null,
  14748. ""
  14749. ],
  14750. "unverified": [
  14751. null,
  14752. ""
  14753. ],
  14754. "verified": [
  14755. null,
  14756. ""
  14757. ],
  14758. "finished": [
  14759. null,
  14760. ""
  14761. ],
  14762. "Disconnected": [
  14763. null,
  14764. "Déconnecté"
  14765. ],
  14766. "Error": [
  14767. null,
  14768. "Erreur"
  14769. ],
  14770. "Connecting": [
  14771. null,
  14772. "Connection"
  14773. ],
  14774. "Connection Failed": [
  14775. null,
  14776. "La connection a échoué"
  14777. ],
  14778. "Authenticating": [
  14779. null,
  14780. "Authentification"
  14781. ],
  14782. "Authentication Failed": [
  14783. null,
  14784. "L'authentification a échoué"
  14785. ],
  14786. "Disconnecting": [
  14787. null,
  14788. "Déconnection"
  14789. ],
  14790. "Re-establishing encrypted session": [
  14791. null,
  14792. ""
  14793. ],
  14794. "Your browser needs to generate a private key, which will be used in your encrypted chat session. This can take up to 30 seconds during which your browser might freeze and become unresponsive.": [
  14795. null,
  14796. ""
  14797. ],
  14798. "Private key generated.": [
  14799. null,
  14800. ""
  14801. ],
  14802. "Authentication request from %1$s\n\nYour buddy is attempting to verify your identity, by asking you the question below.\n\n%2$s": [
  14803. null,
  14804. ""
  14805. ],
  14806. "Could not verify this user's identify.": [
  14807. null,
  14808. ""
  14809. ],
  14810. "Personal message": [
  14811. null,
  14812. "Message personnel"
  14813. ],
  14814. "Start encrypted conversation": [
  14815. null,
  14816. ""
  14817. ],
  14818. "Refresh encrypted conversation": [
  14819. null,
  14820. ""
  14821. ],
  14822. "End encrypted conversation": [
  14823. null,
  14824. ""
  14825. ],
  14826. "Verify with SMP": [
  14827. null,
  14828. ""
  14829. ],
  14830. "Verify with fingerprints": [
  14831. null,
  14832. ""
  14833. ],
  14834. "What's this?": [
  14835. null,
  14836. ""
  14837. ],
  14838. "me": [
  14839. null,
  14840. ""
  14841. ],
  14842. "Show this menu": [
  14843. null,
  14844. "Afficher ce menu"
  14845. ],
  14846. "Write in the third person": [
  14847. null,
  14848. "Écrire à la troisième personne"
  14849. ],
  14850. "Remove messages": [
  14851. null,
  14852. "Effacer les messages"
  14853. ],
  14854. "Your message could not be sent": [
  14855. null,
  14856. ""
  14857. ],
  14858. "We received an unencrypted message": [
  14859. null,
  14860. ""
  14861. ],
  14862. "We received an unreadable encrypted message": [
  14863. null,
  14864. ""
  14865. ],
  14866. "This user has requested an encrypted session.": [
  14867. null,
  14868. ""
  14869. ],
  14870. "Here are the fingerprints, please confirm them with %1$s, outside of this chat.\n\nFingerprint for you, %2$s: %3$s\n\nFingerprint for %1$s: %4$s\n\nIf you have confirmed that the fingerprints match, click OK, otherwise click Cancel.": [
  14871. null,
  14872. ""
  14873. ],
  14874. "You will be prompted to provide a security question and then an answer to that question.\n\nYour buddy will then be prompted the same question and if they type the exact same answer (case sensitive), their identity will have been verified.": [
  14875. null,
  14876. ""
  14877. ],
  14878. "What is your security question?": [
  14879. null,
  14880. ""
  14881. ],
  14882. "What is the answer to the security question?": [
  14883. null,
  14884. ""
  14885. ],
  14886. "Invalid authentication scheme provided": [
  14887. null,
  14888. ""
  14889. ],
  14890. "Your messages are not encrypted anymore": [
  14891. null,
  14892. ""
  14893. ],
  14894. "Your messages are now encrypted but your buddy's identity has not been verified.": [
  14895. null,
  14896. ""
  14897. ],
  14898. "Your buddy's identify has been verified.": [
  14899. null,
  14900. ""
  14901. ],
  14902. "Your buddy has ended encryption on their end, you should do the same.": [
  14903. null,
  14904. ""
  14905. ],
  14906. "Your messages are not encrypted. Click here to enable OTR encryption.": [
  14907. null,
  14908. ""
  14909. ],
  14910. "Your messages are encrypted, but your buddy has not been verified.": [
  14911. null,
  14912. ""
  14913. ],
  14914. "Your messages are encrypted and your buddy verified.": [
  14915. null,
  14916. ""
  14917. ],
  14918. "Your buddy has closed their end of the private session, you should do the same": [
  14919. null,
  14920. ""
  14921. ],
  14922. "Contacts": [
  14923. null,
  14924. "Contacts"
  14925. ],
  14926. "Online": [
  14927. null,
  14928. "En ligne"
  14929. ],
  14930. "Busy": [
  14931. null,
  14932. "Occupé"
  14933. ],
  14934. "Away": [
  14935. null,
  14936. "Absent"
  14937. ],
  14938. "Offline": [
  14939. null,
  14940. "Déconnecté"
  14941. ],
  14942. "Click to add new chat contacts": [
  14943. null,
  14944. "Cliquez pour ajouter de nouveaux contacts"
  14945. ],
  14946. "Add a contact": [
  14947. null,
  14948. "Ajouter un contact"
  14949. ],
  14950. "Contact username": [
  14951. null,
  14952. "Nom du contact"
  14953. ],
  14954. "Add": [
  14955. null,
  14956. "Ajouter"
  14957. ],
  14958. "Contact name": [
  14959. null,
  14960. "Nom du contact"
  14961. ],
  14962. "Search": [
  14963. null,
  14964. "Rechercher"
  14965. ],
  14966. "No users found": [
  14967. null,
  14968. "Aucun utilisateur trouvé"
  14969. ],
  14970. "Click to add as a chat contact": [
  14971. null,
  14972. "Cliquer pour ajouter aux contacts de chat"
  14973. ],
  14974. "Click to open this room": [
  14975. null,
  14976. "Cliquer pour ouvrir ce salon"
  14977. ],
  14978. "Show more information on this room": [
  14979. null,
  14980. "Afficher davantage d'informations sur ce salon"
  14981. ],
  14982. "Description:": [
  14983. null,
  14984. "Description :"
  14985. ],
  14986. "Occupants:": [
  14987. null,
  14988. "Participants :"
  14989. ],
  14990. "Features:": [
  14991. null,
  14992. "Caractéristiques :"
  14993. ],
  14994. "Requires authentication": [
  14995. null,
  14996. "Nécessite une authentification"
  14997. ],
  14998. "Hidden": [
  14999. null,
  15000. "Masqué"
  15001. ],
  15002. "Requires an invitation": [
  15003. null,
  15004. "Nécessite une invitation"
  15005. ],
  15006. "Moderated": [
  15007. null,
  15008. "Modéré"
  15009. ],
  15010. "Non-anonymous": [
  15011. null,
  15012. "Non-anonyme"
  15013. ],
  15014. "Open room": [
  15015. null,
  15016. "Ouvrir un salon"
  15017. ],
  15018. "Permanent room": [
  15019. null,
  15020. "Salon permanent"
  15021. ],
  15022. "Public": [
  15023. null,
  15024. "Public"
  15025. ],
  15026. "Semi-anonymous": [
  15027. null,
  15028. "Semi-anonyme"
  15029. ],
  15030. "Temporary room": [
  15031. null,
  15032. "Salon temporaire"
  15033. ],
  15034. "Unmoderated": [
  15035. null,
  15036. "Non modéré"
  15037. ],
  15038. "Rooms": [
  15039. null,
  15040. "Salons"
  15041. ],
  15042. "Room name": [
  15043. null,
  15044. "Numéro de salon"
  15045. ],
  15046. "Nickname": [
  15047. null,
  15048. "Alias"
  15049. ],
  15050. "Server": [
  15051. null,
  15052. "Serveur"
  15053. ],
  15054. "Join": [
  15055. null,
  15056. "Rejoindre"
  15057. ],
  15058. "Show rooms": [
  15059. null,
  15060. "Afficher les salons"
  15061. ],
  15062. "No rooms on %1$s": [
  15063. null,
  15064. "Aucun salon dans %1$s"
  15065. ],
  15066. "Rooms on %1$s": [
  15067. null,
  15068. "Salons dans %1$s"
  15069. ],
  15070. "Set chatroom topic": [
  15071. null,
  15072. "Indiquer le sujet du salon"
  15073. ],
  15074. "Kick user from chatroom": [
  15075. null,
  15076. "Expulser l'utilisateur du salon."
  15077. ],
  15078. "Ban user from chatroom": [
  15079. null,
  15080. "Bannir l'utilisateur du salon."
  15081. ],
  15082. "Message": [
  15083. null,
  15084. "Message"
  15085. ],
  15086. "Save": [
  15087. null,
  15088. "Enregistrer"
  15089. ],
  15090. "Cancel": [
  15091. null,
  15092. "Annuler"
  15093. ],
  15094. "An error occurred while trying to save the form.": [
  15095. null,
  15096. "Une erreur est survenue lors de l'enregistrement du formulaire."
  15097. ],
  15098. "This chatroom requires a password": [
  15099. null,
  15100. "Ce salon nécessite un mot de passe."
  15101. ],
  15102. "Password: ": [
  15103. null,
  15104. "Mot de passe : "
  15105. ],
  15106. "Submit": [
  15107. null,
  15108. "Soumettre"
  15109. ],
  15110. "This room is not anonymous": [
  15111. null,
  15112. "Ce salon n'est pas anonyme"
  15113. ],
  15114. "This room now shows unavailable members": [
  15115. null,
  15116. "Ce salon affiche maintenant des membres indisponibles"
  15117. ],
  15118. "This room does not show unavailable members": [
  15119. null,
  15120. "Ce salon n'affiche pas les membres indisponibles"
  15121. ],
  15122. "Non-privacy-related room configuration has changed": [
  15123. null,
  15124. "Les paramètres du salon non liés à la confidentialité ont été modifiés"
  15125. ],
  15126. "Room logging is now enabled": [
  15127. null,
  15128. "Le logging du salon est activé"
  15129. ],
  15130. "Room logging is now disabled": [
  15131. null,
  15132. "Le logging du salon est désactivé"
  15133. ],
  15134. "This room is now non-anonymous": [
  15135. null,
  15136. "Ce salon est maintenant non-anonyme"
  15137. ],
  15138. "This room is now semi-anonymous": [
  15139. null,
  15140. "Ce salon est maintenant semi-anonyme"
  15141. ],
  15142. "This room is now fully-anonymous": [
  15143. null,
  15144. "Ce salon est maintenant entièrement anonyme"
  15145. ],
  15146. "A new room has been created": [
  15147. null,
  15148. "Un nouveau salon a été créé"
  15149. ],
  15150. "Your nickname has been changed": [
  15151. null,
  15152. "Votre alias a été modifié"
  15153. ],
  15154. "<strong>%1$s</strong> has been banned": [
  15155. null,
  15156. "<strong>%1$s</strong> a été banni"
  15157. ],
  15158. "<strong>%1$s</strong> has been kicked out": [
  15159. null,
  15160. "<strong>%1$s</strong> a été expulsé"
  15161. ],
  15162. "<strong>%1$s</strong> has been removed because of an affiliation change": [
  15163. null,
  15164. "<strong>%1$s</strong> a été supprimé à cause d'un changement d'affiliation"
  15165. ],
  15166. "<strong>%1$s</strong> has been removed for not being a member": [
  15167. null,
  15168. "<strong>%1$s</strong> a été supprimé car il n'est pas membre"
  15169. ],
  15170. "You have been banned from this room": [
  15171. null,
  15172. "Vous avez été banni de ce salon"
  15173. ],
  15174. "You have been kicked from this room": [
  15175. null,
  15176. "Vous avez été expulsé de ce salon"
  15177. ],
  15178. "You have been removed from this room because of an affiliation change": [
  15179. null,
  15180. "Vous avez été retiré de ce salon du fait d'un changement d'affiliation"
  15181. ],
  15182. "You have been removed from this room because the room has changed to members-only and you're not a member": [
  15183. null,
  15184. "Vous avez été retiré de ce salon parce que ce salon est devenu réservé aux membres et vous n'êtes pas membre"
  15185. ],
  15186. "You have been removed from this room because the MUC (Multi-user chat) service is being shut down.": [
  15187. null,
  15188. "Vous avez été retiré de ce salon parce que le service de chat multi-utilisateur a été désactivé."
  15189. ],
  15190. "You are not on the member list of this room": [
  15191. null,
  15192. "Vous n'êtes pas dans la liste des membres de ce salon"
  15193. ],
  15194. "No nickname was specified": [
  15195. null,
  15196. "Aucun alias n'a été indiqué"
  15197. ],
  15198. "You are not allowed to create new rooms": [
  15199. null,
  15200. "Vous n'êtes pas autorisé à créer des salons"
  15201. ],
  15202. "Your nickname doesn't conform to this room's policies": [
  15203. null,
  15204. "Votre alias n'est pas conforme à la politique de ce salon"
  15205. ],
  15206. "Your nickname is already taken": [
  15207. null,
  15208. "Votre alias est déjà utilisé"
  15209. ],
  15210. "This room does not (yet) exist": [
  15211. null,
  15212. "Ce salon n'existe pas encore"
  15213. ],
  15214. "This room has reached it's maximum number of occupants": [
  15215. null,
  15216. "Ce salon a atteint la limite maximale d'occupants"
  15217. ],
  15218. "Topic set by %1$s to: %2$s": [
  15219. null,
  15220. "Le sujet '%1$s' a été défini par %2$s"
  15221. ],
  15222. "This user is a moderator": [
  15223. null,
  15224. "Cet utilisateur est modérateur"
  15225. ],
  15226. "This user can send messages in this room": [
  15227. null,
  15228. "Cet utilisateur peut envoyer des messages dans ce salon"
  15229. ],
  15230. "This user can NOT send messages in this room": [
  15231. null,
  15232. "Cet utilisateur ne peut PAS envoyer de messages dans ce salon"
  15233. ],
  15234. "Click to chat with this contact": [
  15235. null,
  15236. "Cliquez pour discuter avec ce contact"
  15237. ],
  15238. "Click to remove this contact": [
  15239. null,
  15240. "Cliquez pour supprimer ce contact"
  15241. ],
  15242. "This contact is busy": [
  15243. null,
  15244. ""
  15245. ],
  15246. "This contact is online": [
  15247. null,
  15248. ""
  15249. ],
  15250. "This contact is offline": [
  15251. null,
  15252. ""
  15253. ],
  15254. "This contact is unavailable": [
  15255. null,
  15256. "Ce salon affiche maintenant des membres indisponibles"
  15257. ],
  15258. "This contact is away for an extended period": [
  15259. null,
  15260. ""
  15261. ],
  15262. "This contact is away": [
  15263. null,
  15264. ""
  15265. ],
  15266. "Contact requests": [
  15267. null,
  15268. "Demandes de contacts"
  15269. ],
  15270. "My contacts": [
  15271. null,
  15272. "Mes contacts"
  15273. ],
  15274. "Pending contacts": [
  15275. null,
  15276. "Contacts en attente"
  15277. ],
  15278. "Custom status": [
  15279. null,
  15280. "Statut personnel"
  15281. ],
  15282. "Click to change your chat status": [
  15283. null,
  15284. "Cliquez pour changer votre statut"
  15285. ],
  15286. "Click here to write a custom status message": [
  15287. null,
  15288. "Cliquez ici pour indiquer votre statut personnel"
  15289. ],
  15290. "online": [
  15291. null,
  15292. "en ligne"
  15293. ],
  15294. "busy": [
  15295. null,
  15296. "occupé"
  15297. ],
  15298. "away for long": [
  15299. null,
  15300. "absent pour une longue durée"
  15301. ],
  15302. "away": [
  15303. null,
  15304. "absent"
  15305. ],
  15306. "I am %1$s": [
  15307. null,
  15308. "Je suis %1$s"
  15309. ],
  15310. "Sign in": [
  15311. null,
  15312. "S'inscrire"
  15313. ],
  15314. "XMPP/Jabber Username:": [
  15315. null,
  15316. "Nom d'utilisateur XMPP/Jabber"
  15317. ],
  15318. "Password:": [
  15319. null,
  15320. "Mot de passe :"
  15321. ],
  15322. "Log In": [
  15323. null,
  15324. "Se connecter"
  15325. ],
  15326. "BOSH Service URL:": [
  15327. null,
  15328. "URL du service BOSH:"
  15329. ],
  15330. "Online Contacts": [
  15331. null,
  15332. "Contacts en ligne"
  15333. ],
  15334. "Connected": [
  15335. null,
  15336. "Connecté"
  15337. ],
  15338. "Attached": [
  15339. null,
  15340. "Attaché"
  15341. ]
  15342. }
  15343. }
  15344. };
  15345. if (typeof define === 'function' && define.amd) {
  15346. define("fr", ['jed'], function () {
  15347. return factory(new Jed(translations));
  15348. });
  15349. } else {
  15350. if (!window.locales) {
  15351. window.locales = {};
  15352. }
  15353. window.locales.fr = factory(new Jed(translations));
  15354. }
  15355. }(this, function (fr) {
  15356. return fr;
  15357. }));
  15358. (function (root, factory) {
  15359. var translations = {
  15360. "domain": "converse",
  15361. "locale_data": {
  15362. "converse": {
  15363. "": {
  15364. "project-id-version": "Converse.js 0.8.1",
  15365. "report-msgid-bugs-to": "",
  15366. "pot-creation-date": "2014-08-25 14:37+0200",
  15367. "po-revision-date": "2014-02-21 06:07+0200",
  15368. "last-translator": "GreenLunar <GreenLunar@github.com>",
  15369. "language-team": "Rahut <http://sourceforge.net/projects/rahut/>",
  15370. "language": "he",
  15371. "mime-version": "1.0",
  15372. "content-type": "text/plain; charset=UTF-8",
  15373. "content-transfer-encoding": "8bit",
  15374. "x-generator": "Poedit 1.5.1",
  15375. "plural-forms": "nplurals=2; plural=(n != 1);"
  15376. },
  15377. "unencrypted":[
  15378. null,"לא מוצפנת"
  15379. ],
  15380. "unverified":[
  15381. null,"לא מאומתת"
  15382. ],
  15383. "verified":[
  15384. null,"מאומתת"
  15385. ],
  15386. "finished":[
  15387. null,"מוגמרת"
  15388. ],
  15389. "This contact is busy":[
  15390. null,"איש קשר זה עסוק"
  15391. ],
  15392. "This contact is online":[
  15393. null,"איש קשר זה מקוון"
  15394. ],
  15395. "This contact is offline":[
  15396. null,"איש קשר זה לא מקוון"
  15397. ],
  15398. "This contact is unavailable":[
  15399. null,"איש קשר זה לא זמין"
  15400. ],
  15401. "This contact is away for an extended period":[
  15402. null,"איש קשר זה נעדר למשך זמן ממושך"
  15403. ],
  15404. "This contact is away":[
  15405. null,"איש קשר זה הינו נעדר"
  15406. ],
  15407. "Click to hide these contacts":[
  15408. null,"לחץ כדי להסתיר את אנשי קשר אלה"
  15409. ],
  15410. "My contacts":[
  15411. null,"אנשי הקשר שלי"
  15412. ],
  15413. "Pending contacts":[
  15414. null,"אנשי קשר ממתינים"
  15415. ],
  15416. "Contact requests":[
  15417. null,"בקשות איש קשר"
  15418. ],
  15419. "Ungrouped":[
  15420. null,"ללא קבוצה"
  15421. ],
  15422. "Contacts":[
  15423. null,"אנשי קשר"
  15424. ],
  15425. "Groups":[
  15426. null,"קבוצות"
  15427. ],
  15428. "Reconnecting":[
  15429. null,"כעת מתחבר"
  15430. ],
  15431. "Disconnected":[
  15432. null,"מנותק"
  15433. ],
  15434. "Error":[
  15435. null,"שגיאה"
  15436. ],
  15437. "Connecting":[
  15438. null,"כעת מתחבר"
  15439. ],
  15440. "Connection Failed":[
  15441. null,"חיבור נכשל"
  15442. ],
  15443. "Authenticating":[
  15444. null,"כעת מאמת"
  15445. ],
  15446. "Authentication Failed":[
  15447. null,"אימות נכשל"
  15448. ],
  15449. "Disconnecting":[
  15450. null,"כעת מתנתק"
  15451. ],
  15452. "Online Contacts":[
  15453. null,"אנשי קשר מקוונים"
  15454. ],
  15455. "Re-establishing encrypted session":[
  15456. null,"בסס מחדש ישיבה מוצפנת"
  15457. ],
  15458. "Generating private key.":[
  15459. null,"כעת מפיק מפתח פרטי."
  15460. ],
  15461. "Your browser might become unresponsive.":[
  15462. null,"הדפדפן שלך עשוי שלא להגיב."
  15463. ],
  15464. "Authentication request from %1$s\n\nYour buddy is attempting to verify your identity, by asking you the question below.\n\n%2$s":[
  15465. null,"בקשת אימות מאת %1$s\n\nהאישיות שכנגד מנסה לאמת את הזהות שלך, בעזרת שאילת שאלה להלן.\n\n%2$s"
  15466. ],
  15467. "Could not verify this user's identify.":[
  15468. null,"לא היתה אפשרות לאמת את זהות משתמש זה."
  15469. ],
  15470. "Exchanging private key with buddy.":[
  15471. null,"ממיר מפתח פרטי עם איש קשר."
  15472. ],
  15473. "Personal message":[
  15474. null,"הודעה אישית"
  15475. ],
  15476. "Are you sure you want to clear the messages from this room?":[
  15477. null,"האם אתה בטוח כי ברצונך לטהר את ההודעות מתוך חדר זה?"
  15478. ],
  15479. "me":[
  15480. null,"אני"
  15481. ],
  15482. "is typing":[
  15483. null,"מקליד/ה כעת"
  15484. ],
  15485. "has stopped typing":[
  15486. null,"חדל/ה מלהקליד"
  15487. ],
  15488. "Show this menu":[
  15489. null,"הצג את תפריט זה"
  15490. ],
  15491. "Write in the third person":[
  15492. null,"כתוב בגוף השלישי"
  15493. ],
  15494. "Remove messages":[
  15495. null,"הסר הודעות"
  15496. ],
  15497. "Are you sure you want to clear the messages from this chat box?":[
  15498. null,"האם אתה בטוח כי ברצונך לטהר את ההודעות מתוך תיבת שיחה זה?"
  15499. ],
  15500. "Your message could not be sent":[
  15501. null,"ההודעה שלך לא היתה יכולה להישלח"
  15502. ],
  15503. "We received an unencrypted message":[
  15504. null,"אנחנו קיבלנו הודעה לא מוצפנת"
  15505. ],
  15506. "We received an unreadable encrypted message":[
  15507. null,"אנחנו קיבלנו הודעה מוצפנת לא קריאה"
  15508. ],
  15509. "This user has requested an encrypted session.":[
  15510. null,"משתמש זה ביקש ישיבה מוצפנת."
  15511. ],
  15512. "Here are the fingerprints, please confirm them with %1$s, outside of this chat.\n\nFingerprint for you, %2$s: %3$s\n\nFingerprint for %1$s: %4$s\n\nIf you have confirmed that the fingerprints match, click OK, otherwise click Cancel.":[
  15513. null,"הרי טביעות האצבע, אנא אמת אותן עם %1$s, מחוץ לשיחה זו.\n\nטביעת אצבע עבורך, %2$s: %3$s\n\nטביעת אצבע עבור %1$s: %4$s\n\nהיה ואימתת כי טביעות האצבע תואמות, לחץ אישור (OK), אחרת לחץ ביטול (Cancel)."
  15514. ],
  15515. "You will be prompted to provide a security question and then an answer to that question.\n\nYour buddy will then be prompted the same question and if they type the exact same answer (case sensitive), their identity will be verified.":[
  15516. null,"אתה תתבקש לספק שאלת אבטחה ולאחריה תשובה לשאלה הזו.\n\nהאישיות שכנגד תתבקש עובר זאת לאותה שאלת אבטחה ואם זו תקלידו את את אותה התשובה במדויק (case sensitive), זהותה תאומת."
  15517. ],
  15518. "What is your security question?":[
  15519. null,"מהי שאלת האבטחה שלך?"
  15520. ],
  15521. "What is the answer to the security question?":[
  15522. null,"מהי התשובה לשאלת האבטחה?"
  15523. ],
  15524. "Invalid authentication scheme provided":[
  15525. null,"סופקה סכימת אימות שגויה"
  15526. ],
  15527. "Your messages are not encrypted anymore":[
  15528. null,"ההודעות שלך אינן מוצפנות עוד"
  15529. ],
  15530. "Your messages are now encrypted but your buddy's identity has not been verified.":[
  15531. null,"ההודעות שלך מוצפנות כעת אך זהות האישיות שכנגד טרם אומתה."
  15532. ],
  15533. "Your buddy's identify has been verified.":[
  15534. null,"זהות האישיות שכנגד אומתה."
  15535. ],
  15536. "Your buddy has ended encryption on their end, you should do the same.":[
  15537. null,"האישיות שכנגד סיימה הצפנה בקצה שלה, עליך לעשות את אותו הדבר."
  15538. ],
  15539. "Your messages are not encrypted. Click here to enable OTR encryption.":[
  15540. null,"ההודעות שלך אינן מוצפנות. לחץ כאן כדי לאפשר OTR."
  15541. ],
  15542. "Your messages are encrypted, but your buddy has not been verified.":[
  15543. null,"ההודעות שלך מוצפנות כעת, אך האישיות שכנגד טרם אומתה."
  15544. ],
  15545. "Your messages are encrypted and your buddy verified.":[
  15546. null,"ההודעות שלך מוצפנות כעת והאישיות שכנגד אומתה."
  15547. ],
  15548. "Your buddy has closed their end of the private session, you should do the same":[
  15549. null,"האישיות שכנגד סגרה את קצה הישיבה הפרטית שלה, עליך לעשות את אותו הדבר"
  15550. ],
  15551. "End encrypted conversation":[
  15552. null,"סיים ישיבה מוצפנת"
  15553. ],
  15554. "Refresh encrypted conversation":[
  15555. null,"רענן ישיבה מוצפנת"
  15556. ],
  15557. "Start encrypted conversation":[
  15558. null,"התחל ישיבה מוצפנת"
  15559. ],
  15560. "Verify with fingerprints":[
  15561. null,"אמת בעזרת טביעות אצבע"
  15562. ],
  15563. "Verify with SMP":[
  15564. null,"אמת בעזרת SMP"
  15565. ],
  15566. "What's this?":[
  15567. null,"מה זה?"
  15568. ],
  15569. "Online":[
  15570. null,"מקוון"
  15571. ],
  15572. "Busy":[
  15573. null,"עסוק"
  15574. ],
  15575. "Away":[
  15576. null,"נעדר"
  15577. ],
  15578. "Offline":[
  15579. null,"בלתי מקוון"
  15580. ],
  15581. "Contact name":[
  15582. null,"שם איש קשר"
  15583. ],
  15584. "Search":[
  15585. null,"חיפוש"
  15586. ],
  15587. "Contact username":[
  15588. null,"שם משתמש איש קשר"
  15589. ],
  15590. "Add":[
  15591. null,"הוסף"
  15592. ],
  15593. "Click to add new chat contacts":[
  15594. null,"לחץ כדי להוסיף אנשי קשר שיחה חדשים"
  15595. ],
  15596. "Add a contact":[
  15597. null,"הוסף איש קשר"
  15598. ],
  15599. "No users found":[
  15600. null,"לא נמצאו משתמשים"
  15601. ],
  15602. "Click to add as a chat contact":[
  15603. null,"לחץ כדי להוסיף בתור איש קשר שיחה"
  15604. ],
  15605. "Room name":[
  15606. null,"שם חדר"
  15607. ],
  15608. "Nickname":[
  15609. null,"שם כינוי"
  15610. ],
  15611. "Server":[
  15612. null,"שרת"
  15613. ],
  15614. "Join":[
  15615. null,"הצטרף"
  15616. ],
  15617. "Show rooms":[
  15618. null,"הצג חדרים"
  15619. ],
  15620. "Rooms":[
  15621. null,"חדרים"
  15622. ],
  15623. "No rooms on %1$s":[
  15624. null,"אין חדרים על %1$s"
  15625. ],
  15626. "Rooms on %1$s":[
  15627. null,"חדרים על %1$s"
  15628. ],
  15629. "Click to open this room":[
  15630. null,"לחץ כדי לפתוח את חדר זה"
  15631. ],
  15632. "Show more information on this room":[
  15633. null,"הצג עוד מידע אודות חדר זה"
  15634. ],
  15635. "Description:":[
  15636. null,"תיאור:"
  15637. ],
  15638. "Occupants:":[
  15639. null,"נוכחים:"
  15640. ],
  15641. "Features:":[
  15642. null,"תכונות:"
  15643. ],
  15644. "Requires authentication":[
  15645. null,"מצריך אישור"
  15646. ],
  15647. "Hidden":[
  15648. null,"נסתר"
  15649. ],
  15650. "Requires an invitation":[
  15651. null,"מצריך הזמנה"
  15652. ],
  15653. "Moderated":[
  15654. null,"מבוקר"
  15655. ],
  15656. "Non-anonymous":[
  15657. null,"לא אנונימי"
  15658. ],
  15659. "Open room":[
  15660. null,"חדר פתוח"
  15661. ],
  15662. "Permanent room":[
  15663. null,"חדר צמיתה"
  15664. ],
  15665. "Public":[
  15666. null,"פומבי"
  15667. ],
  15668. "Semi-anonymous":[
  15669. null,"אנונימי למחצה"
  15670. ],
  15671. "Temporary room":[
  15672. null,"חדר זמני"
  15673. ],
  15674. "Unmoderated":[
  15675. null,"לא מבוקר"
  15676. ],
  15677. "Set chatroom topic":[
  15678. null,"קבע נושא חדר שיחה"
  15679. ],
  15680. "Kick user from chatroom":[
  15681. null,"בעט משתמש מתוך חדר שיחה"
  15682. ],
  15683. "Ban user from chatroom":[
  15684. null,"אסור משתמש מתוך חדר שיחה"
  15685. ],
  15686. "Message":[
  15687. null,"הודעה"
  15688. ],
  15689. "Save":[
  15690. null,"שמור"
  15691. ],
  15692. "Cancel":[
  15693. null,"ביטול"
  15694. ],
  15695. "An error occurred while trying to save the form.":[
  15696. null,"אירעה שגיאה במהלך ניסיון שמירת הטופס."
  15697. ],
  15698. "This chatroom requires a password":[
  15699. null,"חדר שיחה זה מצריך סיסמה"
  15700. ],
  15701. "Password: ":[
  15702. null,"סיסמה: "
  15703. ],
  15704. "Submit":[
  15705. null,"שלח"
  15706. ],
  15707. "This room is not anonymous":[
  15708. null,"חדר זה אינו אנונימי"
  15709. ],
  15710. "This room now shows unavailable members":[
  15711. null,"חדר זה כעת מציג חברים לא זמינים"
  15712. ],
  15713. "This room does not show unavailable members":[
  15714. null,"חדר זה לא מציג חברים לא זמינים"
  15715. ],
  15716. "Non-privacy-related room configuration has changed":[
  15717. null,"תצורת חדר אשר לא-קשורה-בפרטיות שונתה"
  15718. ],
  15719. "Room logging is now enabled":[
  15720. null,"יומן חדר הינו מופעל כעת"
  15721. ],
  15722. "Room logging is now disabled":[
  15723. null,"יומן חדר הינו מנוטרל כעת"
  15724. ],
  15725. "This room is now non-anonymous":[
  15726. null,"חדר זה אינו אנונימי כעת"
  15727. ],
  15728. "This room is now semi-anonymous":[
  15729. null,"חדר זה הינו אנונימי למחצה כעת"
  15730. ],
  15731. "This room is now fully-anonymous":[
  15732. null,"חדר זה הינו אנונימי לחלוטין כעת"
  15733. ],
  15734. "A new room has been created":[
  15735. null,"חדר חדש נוצר"
  15736. ],
  15737. "Your nickname has been changed":[
  15738. null,"שם הכינוי שלך שונה"
  15739. ],
  15740. "<strong>%1$s</strong> has been banned":[
  15741. null,"<strong>%1$s</strong> נאסר(ה)"
  15742. ],
  15743. "<strong>%1$s</strong> has been kicked out":[
  15744. null,"<strong>%1$s</strong> נבעט(ה)"
  15745. ],
  15746. "<strong>%1$s</strong> has been removed because of an affiliation change":[
  15747. null,"<strong>%1$s</strong> הוסרה(ה) משום שינוי שיוך"
  15748. ],
  15749. "<strong>%1$s</strong> has been removed for not being a member":[
  15750. null,"<strong>%1$s</strong> הוסר(ה) משום אי הימצאות במסגרת מעמד של חבר"
  15751. ],
  15752. "You have been banned from this room":[
  15753. null,"נאסרת מתוך חדר זה"
  15754. ],
  15755. "You have been kicked from this room":[
  15756. null,"נבעטת מתוך חדר זה"
  15757. ],
  15758. "You have been removed from this room because of an affiliation change":[
  15759. null,"הוסרת מתוך חדר זה משום שינוי שיוך"
  15760. ],
  15761. "You have been removed from this room because the room has changed to members-only and you're not a member":[
  15762. null,"הוסרת מתוך חדר זה משום שהחדר שונה לחברים-בלבד ואינך במעמד של חבר"
  15763. ],
  15764. "You have been removed from this room because the MUC (Multi-user chat) service is being shut down.":[
  15765. null,"הוסרת מתוך חדר זה משום ששירות שמ״מ (שיחה מרובת משתמשים) זה כעת מצוי בהליכי סגירה."
  15766. ],
  15767. "You are not on the member list of this room":[
  15768. null,"אינך ברשימת החברים של חדר זה"
  15769. ],
  15770. "No nickname was specified":[
  15771. null,"לא צוין שום שם כינוי"
  15772. ],
  15773. "You are not allowed to create new rooms":[
  15774. null,"אין לך רשות ליצור חדרים חדשים"
  15775. ],
  15776. "Your nickname doesn't conform to this room's policies":[
  15777. null,"שם הכינוי שלך לא תואם את המדינויות של חדר זה"
  15778. ],
  15779. "Your nickname is already taken":[
  15780. null,"שם הכינוי שלך הינו תפוס"
  15781. ],
  15782. "This room does not (yet) exist":[
  15783. null,"חדר זה (עדיין) לא קיים"
  15784. ],
  15785. "This room has reached it's maximum number of occupants":[
  15786. null,"חדר זה הגיע לסף הנוכחים המרבי שלו"
  15787. ],
  15788. "Topic set by %1$s to: %2$s":[
  15789. null,"נושא חדר זה נקבע על ידי %1$s אל: %2$s"
  15790. ],
  15791. "This user is a moderator":[
  15792. null,"משתמש זה הינו אחראי"
  15793. ],
  15794. "This user can send messages in this room":[
  15795. null,"משתמש זה מסוגל לשלוח הודעות בתוך חדר זה"
  15796. ],
  15797. "This user can NOT send messages in this room":[
  15798. null,"משתמש זה ﬥﬡ מסוגל לשלוח הודעות בתוך חדר זה"
  15799. ],
  15800. "Click to restore this chat":[
  15801. null,"לחץ כדי לשחזר את שיחה זו"
  15802. ],
  15803. "Minimized":[
  15804. null,"ממוזער"
  15805. ],
  15806. "Are you sure you want to remove this contact?":[
  15807. null,"האם אתה בטוח כי ברצונך להסיר את איש קשר זה?"
  15808. ],
  15809. "Are you sure you want to decline this contact request?":[
  15810. null,"האם אתה בטוח כי ברצונך לסרב את בקשת איש קשר זה?"
  15811. ],
  15812. "Click to remove this contact":[
  15813. null,"לחץ כדי להסיר את איש קשר זה"
  15814. ],
  15815. "Click to accept this contact request":[
  15816. null,"לחץ כדי לקבל את בקשת איש קשר זה"
  15817. ],
  15818. "Click to decline this contact request":[
  15819. null,"לחץ כדי לסרב את בקשת איש קשר זה"
  15820. ],
  15821. "Click to chat with this contact":[
  15822. null,"לחץ כדי לשוחח עם איש קשר זה"
  15823. ],
  15824. "Type to filter":[
  15825. null,"הקלד כדי לסנן"
  15826. ],
  15827. "Custom status":[
  15828. null,"מצב מותאם"
  15829. ],
  15830. "online":[
  15831. null,"מקוון"
  15832. ],
  15833. "busy":[
  15834. null,"עסוק"
  15835. ],
  15836. "away for long":[
  15837. null,"נעדר לזמן מה"
  15838. ],
  15839. "away":[
  15840. null,"נעדר"
  15841. ],
  15842. "I am %1$s":[
  15843. null,"מצבי כעת הינו %1$s"
  15844. ],
  15845. "Click here to write a custom status message":[
  15846. null,"לחץ כאן כדי לכתוב הודעת מצב מותאמת"
  15847. ],
  15848. "Click to change your chat status":[
  15849. null,"לחץ כדי לשנות את הודעת השיחה שלך"
  15850. ],
  15851. "XMPP/Jabber Username:":[
  15852. null,"שם משתמש XMPP/Jabber:"
  15853. ],
  15854. "Password:":[
  15855. null,"סיסמה:"
  15856. ],
  15857. "Log In":[
  15858. null,"כניסה"
  15859. ],
  15860. "Sign in":[
  15861. null,"התחברות"
  15862. ],
  15863. "Toggle chat":[
  15864. null,"הפעל שיח"
  15865. ]
  15866. }
  15867. }
  15868. };
  15869. if (typeof define === 'function' && define.amd) {
  15870. define("he", ['jed'], function () {
  15871. return factory(new Jed(translations));
  15872. });
  15873. } else {
  15874. if (!window.locales) {
  15875. window.locales = {};
  15876. }
  15877. window.locales.he = factory(new Jed(translations));
  15878. }
  15879. }(this, function (he) {
  15880. return he;
  15881. }));
  15882. (function(root, factory) {
  15883. var translations = {
  15884. "domain" : "converse",
  15885. "locale_data" : {
  15886. "converse" : {
  15887. "" : {
  15888. "Project-Id-Version" : "Converse.js 0.4",
  15889. "Report-Msgid-Bugs-To" : "",
  15890. "POT-Creation-Date" : "2013-09-24 23:22+0200",
  15891. "PO-Revision-Date" : "2013-09-25 22:42+0200",
  15892. "Last-Translator" : "Krisztian Kompar <w3host@w3host.hu>",
  15893. "Language-Team" : "Hungarian",
  15894. "Language" : "hu",
  15895. "MIME-Version" : "1.0",
  15896. "Content-Type" : "text/plain; charset=UTF-8",
  15897. "Content-Transfer-Encoding" : "8bit",
  15898. "domain" : "converse",
  15899. "lang" : "hu",
  15900. "plural_forms" : "nplurals=2; plural=(n != 1);"
  15901. },
  15902. "Disconnected" : [ null, "Szétkapcsolva" ],
  15903. "Error" : [ null, "Hiba" ],
  15904. "Connecting" : [ null, "Kapcsolódás" ],
  15905. "Connection Failed" : [ null, "Kapcsolódási hiba" ],
  15906. "Authenticating" : [ null, "Azonosítás" ],
  15907. "Authentication Failed" : [ null, "Azonosítási hiba" ],
  15908. "Disconnecting" : [ null, "Szétkapcsolás" ],
  15909. "me" : [ null, "én" ],
  15910. "%1$s is typing" : [ null, "%1$s gépel" ],
  15911. "Show this menu" : [ null, "Mutasd ezt a menüt" ],
  15912. "Write in the third person" : [ null, "" ],
  15913. "Remove messages" : [ null, "Üzenet törlése" ],
  15914. "Personal message" : [ null, "Saját üzenet" ],
  15915. "Contacts" : [ null, "Kapcsolatok" ],
  15916. "Online" : [ null, "Elérhető" ],
  15917. "Busy" : [ null, "Foglalt" ],
  15918. "Away" : [ null, "Távol" ],
  15919. "Offline" : [ null, "Nem elérhető" ],
  15920. "Click to add new chat contacts" : [ null,
  15921. "Új kapcsolatok hozzáadása" ],
  15922. "Add a contact" : [ null, "Új kapcsolat" ],
  15923. "Contact username" : [ null, "Felhasználónév" ],
  15924. "Add" : [ null, "Hozzáadás" ],
  15925. "Contact name" : [ null, "Kapcsolat neve" ],
  15926. "Search" : [ null, "Keresés" ],
  15927. "No users found" : [ null, "Nincs találat" ],
  15928. "Click to add as a chat contact" : [ null,
  15929. "Csevegő kapcsolatként hozzáad" ],
  15930. "Click to open this room" : [ null, "Belépés a csevegő szobába" ],
  15931. "Show more information on this room" : [ null,
  15932. "További információk a csevegő szobáról" ],
  15933. "Description:" : [ null, "Leírás:" ],
  15934. "Occupants:" : [ null, "Jelenlevők:" ],
  15935. "Features:" : [ null, "Tulajdonságok" ],
  15936. "Requires authentication" : [ null, "Azonosítás szükséges" ],
  15937. "Hidden" : [ null, "Rejtett" ],
  15938. "Requires an invitation" : [ null, "Meghívás szükséges" ],
  15939. "Moderated" : [ null, "Moderált" ],
  15940. "Non-anonymous" : [ null, "NEM névtelen" ],
  15941. "Open room" : [ null, "Nyitott szoba" ],
  15942. "Permanent room" : [ null, "Állandó szoba" ],
  15943. "Public" : [ null, "Nyílvános" ],
  15944. "Semi-anonymous" : [ null, "Félig névtelen" ],
  15945. "Temporary room" : [ null, "Ideiglenes szoba" ],
  15946. "Unmoderated" : [ null, "Moderálatlan" ],
  15947. "Rooms" : [ null, "Szobák" ],
  15948. "Room name" : [ null, "A szoba neve" ],
  15949. "Nickname" : [ null, "Becenév" ],
  15950. "Server" : [ null, "Szerver" ],
  15951. "Join" : [ null, "Csatlakozás" ],
  15952. "Show rooms" : [ null, "Létező szobák" ],
  15953. "No rooms on %1$s" : [ null,
  15954. "Nincs csevegő szoba a(z) %1$s szerveren" ],
  15955. "Rooms on %1$s" : [ null, "Csevegő szobák a(z) %1$s szerveren" ],
  15956. "Set chatroom topic" : [ null, "Csevegőszoba téma beállítás" ],
  15957. "Kick user from chatroom" : [ null,
  15958. "Felhasználó kiléptetése a csevegő szobából" ],
  15959. "Ban user from chatroom" : [ null,
  15960. "Felhasználó kitíltása a csevegő szobából" ],
  15961. "Message" : [ null, "Üzenet" ],
  15962. "Save" : [ null, "Mentés" ],
  15963. "Cancel" : [ null, "Mégsem" ],
  15964. "An error occurred while trying to save the form." : [ null,
  15965. "Hiba történt az adatok mentése közben." ],
  15966. "This chatroom requires a password" : [ null,
  15967. "A csevegő szoba belépéshez jelszó szükséges" ],
  15968. "Password: " : [ null, "Jelszó:" ],
  15969. "Submit" : [ null, "Küldés" ],
  15970. "This room is not anonymous" : [ null,
  15971. "Ez a szoba NEM névtelen" ],
  15972. "This room now shows unavailable members" : [ null,
  15973. "Ez a szoba mutatja az elérhetetlen tagokat" ],
  15974. "This room does not show unavailable members" : [ null,
  15975. "Ez a szoba nem mutatja az elérhetetlen tagokat" ],
  15976. "Non-privacy-related room configuration has changed" : [ null,
  15977. "A szoba általános konfigurációja módosult" ],
  15978. "Room logging is now enabled" : [ null,
  15979. "A szobába a belépés lehetséges" ],
  15980. "Room logging is now disabled" : [ null,
  15981. "A szobába a belépés szünetel" ],
  15982. "This room is now non-anonymous" : [ null,
  15983. "Ez a szoba most NEM névtelen" ],
  15984. "This room is now semi-anonymous" : [ null,
  15985. "Ez a szoba most félig névtelen" ],
  15986. "This room is now fully-anonymous" : [ null,
  15987. "Ez a szoba most teljesen névtelen" ],
  15988. "A new room has been created" : [ null,
  15989. "Létrejött egy új csevegő szoba" ],
  15990. "Your nickname has been changed" : [ null,
  15991. "A beceneved módosításra került" ],
  15992. "<strong>%1$s</strong> has been banned" : [ null,
  15993. "A szobából kitíltva: <strong>%1$s</strong>" ],
  15994. "<strong>%1$s</strong> has been kicked out" : [ null,
  15995. "A szobából kidobva: <strong>%1$s</strong>" ],
  15996. "<strong>%1$s</strong> has been removed because of an affiliation change" : [
  15997. null,
  15998. "Taglista módosítás miatt a szobából kiléptetve: <strong>%1$s</strong>" ],
  15999. "<strong>%1$s</strong> has been removed for not being a member" : [
  16000. null,
  16001. "A taglistán nem szerepel így a szobából kiléptetve: <strong>%1$s</strong>" ],
  16002. "You have been banned from this room" : [ null,
  16003. "Ki lettél tíltva ebből a szobából" ],
  16004. "You have been kicked from this room" : [ null,
  16005. "Ki lettél dobva ebből a szobából" ],
  16006. "You have been removed from this room because of an affiliation change" : [
  16007. null,
  16008. "Taglista módosítás miatt kiléptettünk a csevegő szobából" ],
  16009. "You have been removed from this room because the room has changed to members-only and you're not a member" : [
  16010. null,
  16011. "Kiléptettünk a csevegő szobából, mert mostantól csak a taglistán szereplők lehetnek jelen." ],
  16012. "You have been removed from this room because the MUC (Multi-user chat) service is being shut down." : [
  16013. null,
  16014. "Kiléptettünk a csevegő szobából, mert a MUC (Multi-User Chat) szolgáltatás leállításra került." ],
  16015. "You are not on the member list of this room" : [ null,
  16016. "Nem szerepelsz a csevegő szoba taglistáján" ],
  16017. "No nickname was specified" : [ null,
  16018. "Nem lett megadva becenév" ],
  16019. "You are not allowed to create new rooms" : [ null,
  16020. "Nem lehet új csevegő szobát létrehozni" ],
  16021. "Your nickname doesn't conform to this room's policies" : [
  16022. null,
  16023. "A beceneved ütközik a csevegő szoba szabályzataival" ],
  16024. "Your nickname is already taken" : [ null,
  16025. "A becenevedet már valaki használja" ],
  16026. "This room does not (yet) exist" : [ null,
  16027. "Ez a szoba (még) nem létezik" ],
  16028. "This room has reached it's maximum number of occupants" : [
  16029. null,
  16030. "Ez a csevegő szoba elérte a maximális jelenlevők számát" ],
  16031. "Topic set by %1$s to: %2$s" : [ null,
  16032. "A következő témát állította be %1$s: %2$s" ],
  16033. "This user is a moderator" : [ null,
  16034. "Ez a felhasználó egy moderátor" ],
  16035. "This user can send messages in this room" : [ null,
  16036. "Ez a felhasználó küldhet üzenetet ebbe a szobába" ],
  16037. "This user can NOT send messages in this room" : [ null,
  16038. "Ez a felhasználó NEM küldhet üzenetet ebbe a szobába" ],
  16039. "Click to chat with this contact" : [ null,
  16040. "Csevegés indítása ezzel a kapcsolatunkkal" ],
  16041. "Click to remove this contact" : [ null, "A kapcsolat törlése" ],
  16042. "This contact is busy" : [ null, "Elfoglalt" ],
  16043. "This contact is online" : [ null, "Online" ],
  16044. "This contact is offline" : [ null, "Nincs bejelentkezve" ],
  16045. "This contact is unavailable" : [ null, "Elérhetetlen" ],
  16046. "This contact is away for an extended period" : [ null,
  16047. "Hosszabb ideje távol" ],
  16048. "This contact is away" : [ null, "Távol" ],
  16049. "Contact requests" : [ null, "Kapcsolat felvételi kérés" ],
  16050. "My contacts" : [ null, "Kapcsolatok:" ],
  16051. "Pending contacts" : [ null, "Függőben levő kapcsolatok" ],
  16052. "Custom status" : [ null, "Egyedi státusz" ],
  16053. "Click to change your chat status" : [ null,
  16054. "Saját státusz beállítása" ],
  16055. "Click here to write a custom status message" : [ null,
  16056. "Egyedi státusz üzenet írása" ],
  16057. "online" : [ null, "online" ],
  16058. "busy" : [ null, "elfoglalt" ],
  16059. "away for long" : [ null, "hosszú ideje távol" ],
  16060. "away" : [ null, "távol" ],
  16061. "I am %1$s" : [ null, "%1$s vagyok" ],
  16062. "Sign in" : [ null, "Belépés" ],
  16063. "XMPP/Jabber Username:" : [ null, "XMPP/Jabber azonosító:" ],
  16064. "Password:" : [ null, "Jelszó:" ],
  16065. "Log In" : [ null, "Belépés" ],
  16066. "BOSH Service URL:" : [ null, "BOSH szerver URL" ],
  16067. "Online Contacts" : [ null, "Online kapcsolatok" ]
  16068. }
  16069. }
  16070. };
  16071. if (typeof define === 'function' && define.amd) {
  16072. define("hu", [ 'jed' ], function() {
  16073. return factory(new Jed(translations));
  16074. });
  16075. } else {
  16076. if (!window.locales) {
  16077. window.locales = {};
  16078. }
  16079. window.locales.hu = factory(new Jed(translations));
  16080. }
  16081. }(this, function(hu) {
  16082. return hu;
  16083. }));
  16084. (function(root, factory) {
  16085. var translations = {
  16086. "domain": "converse",
  16087. "locale_data": {
  16088. "converse": {
  16089. "": {
  16090. "project-id-version": "Converse.js 0.7.0",
  16091. "report-msgid-bugs-to": "",
  16092. "pot-creation-date": "2014-01-22 17:07+0200",
  16093. "po-revision-date": "2014-01-25 21:30+0700",
  16094. "last-translator": "Priyadi Iman Nurcahyo <priyadi@priyadi.net>",
  16095. "language-team": "Bahasa Indonesia",
  16096. "mime-version": "1.0",
  16097. "content-type": "text/plain; charset=UTF-8",
  16098. "content-transfer-encoding": "8bit",
  16099. "language": "id"
  16100. },
  16101. "unencrypted": [null, "tak dienkripsi"],
  16102. "unverified": [null, "tak diverifikasi"],
  16103. "verified": [null, "diverifikasi"],
  16104. "finished": [null, "selesai"],
  16105. "This contact is busy": [null, "Teman ini sedang sibuk"],
  16106. "This contact is online": [null, "Teman ini terhubung"],
  16107. "This contact is offline": [null, "Teman ini tidak terhubung"],
  16108. "This contact is unavailable": [null, "Teman ini tidak tersedia"],
  16109. "This contact is away for an extended period": [null, "Teman ini tidak di tempat untuk waktu yang lama"],
  16110. "This contact is away": [null, "Teman ini tidak di tempat"],
  16111. "Disconnected": [null, "Terputus"],
  16112. "Error": [null, "Kesalahan"],
  16113. "Connecting": [null, "Menyambung"],
  16114. "Connection Failed": [null, "Gagal Menyambung"],
  16115. "Authenticating": [null, "Melakukan otentikasi"],
  16116. "Authentication Failed": [null, "Otentikasi gagal"],
  16117. "Disconnecting": [null, "Memutuskan hubungan"],
  16118. "Online Contacts": [null, "Teman yang Terhubung"],
  16119. "Re-establishing encrypted session": [null, "Menyambung kembali sesi terenkripsi"],
  16120. "Your browser needs to generate a private key, which will be used in your encrypted chat session. This can take up to 30 seconds during which your browser might freeze and become unresponsive.": [null, "Perambah anda perlu membuat kunci privat, yang akan digunakan pada sesi perbincangan anda. Ini akan membutuhkan waktu sampai 30 detik, dan selama itu perambah mungkin akan tidak responsif."],
  16121. "Private key generated.": [null, "Kunci privat berhasil dibuat."],
  16122. "Authentication request from %1$s\n\nYour buddy is attempting to verify your identity, by asking you the question below.\n\n%2$s": [null, "Permintaan otentikasi dari %1$s\n\nTeman anda mencoba untuk melakukan verifikasi identitas anda dengan cara menanyakan pertanyaan di bawah ini.\n\n%2$s"],
  16123. "Could not verify this user's identify.": [null, "Tak dapat melakukan verifikasi identitas pengguna ini."],
  16124. "Personal message": [null, "Pesan pribadi"],
  16125. "Start encrypted conversation": [null, "Mulai sesi terenkripsi"],
  16126. "Refresh encrypted conversation": [null, "Setel ulang percakapan terenkripsi"],
  16127. "End encrypted conversation": [null, "Sudahi percakapan terenkripsi"],
  16128. "Verify with SMP": [null, "Verifikasi menggunakan SMP"],
  16129. "Verify with fingerprints": [null, "Verifikasi menggunakan sidik jari"],
  16130. "What's this?": [null, "Apakah ini?"],
  16131. "me": [null, "saya"],
  16132. "Show this menu": [null, "Tampilkan menu ini"],
  16133. "Write in the third person": [null, "Tulis ini menggunakan bahasa pihak ketiga"],
  16134. "Remove messages": [null, "Hapus pesan"],
  16135. "Your message could not be sent": [null, "Pesan anda tak dapat dikirim"],
  16136. "We received an unencrypted message": [null, "Kami menerima pesan terenkripsi"],
  16137. "We received an unreadable encrypted message": [null, "Kami menerima pesan terenkripsi yang gagal dibaca"],
  16138. "This user has requested an encrypted session.": [null, "Pengguna ini meminta sesi terenkripsi"],
  16139. "Here are the fingerprints, please confirm them with %1$s, outside of this chat.\n\nFingerprint for you, %2$s: %3$s\n\nFingerprint for %1$s: %4$s\n\nIf you have confirmed that the fingerprints match, click OK, otherwise click Cancel.": [null, "Ini adalah sidik jari anda, konfirmasikan bersama mereka dengan %1$s, di luar percakapan ini.\n\nSidik jari untuk anda, %2$s: %3$s\n\nSidik jari untuk %1$s: %4$s\n\nJika anda bisa mengkonfirmasi sidik jadi cocok, klik Lanjutkan, jika tidak klik Batal."],
  16140. "You will be prompted to provide a security question and then an answer to that question.\n\nYour buddy will then be prompted the same question and if they type the exact same answer (case sensitive), their identity will have been verified.": [null, "Anda akan ditanyakan pertanyaan untuk keamanan beserta jawaban untuk pertanyaan tersebut.\n\nTeman anda akan ditanyakan pertanyaan yang sama dan jika dia memberikan jawaban yang sama (huruf kapital diperhatikan), identitas mereka diverifikasi."],
  16141. "What is your security question?": [null, "Apakah pertanyaan keamanan anda?"],
  16142. "What is the answer to the security question?": [null, "Apa jawaban dari pertanyaan keamanan tersebut?"],
  16143. "Invalid authentication scheme provided": [null, "Skema otentikasi salah"],
  16144. "Your messages are not encrypted anymore": [null, "Pesan anda tidak lagi terenkripsi"],
  16145. "Your messages are now encrypted but your buddy's identity has not been verified.": [null, "Pesan anda sekarang terenkripsi, namun identitas teman anda belum dapat diverifikasi."],
  16146. "Your buddy's identify has been verified.": [null, "Identitas teman anda telah diverifikasi."],
  16147. "Your buddy has ended encryption on their end, you should do the same.": [null, "Teman anda menghentikan percakapan terenkripsi, anda sebaiknya melakukan hal yang sama."],
  16148. "Your messages are not encrypted. Click here to enable OTR encryption.": [null, "Pesan anda tak terenkripsi. Klik di sini untuk menyalakan enkripsi OTR."],
  16149. "Your messages are encrypted, but your buddy has not been verified.": [null, "Pesan anda terenkripsi, tetapi teman anda belum diverifikasi."],
  16150. "Your messages are encrypted and your buddy verified.": [null, "Pesan anda terenkripsi dan teman anda telah diverifikasi."],
  16151. "Your buddy has closed their end of the private session, you should do the same": [null, "Teman anda telah mematikan sesi terenkripsi, dan anda juga sebaiknya melakukan hal yang sama"],
  16152. "Contacts": [null, "Teman"],
  16153. "Online": [null, "Terhubung"],
  16154. "Busy": [null, "Sibuk"],
  16155. "Away": [null, "Pergi"],
  16156. "Offline": [null, "Tak Terhubung"],
  16157. "Click to add new chat contacts": [null, "Klik untuk menambahkan teman baru"],
  16158. "Add a contact": [null, "Tambah teman"],
  16159. "Contact username": [null, "Username teman"],
  16160. "Add": [null, "Tambah"],
  16161. "Contact name": [null, "Nama teman"],
  16162. "Search": [null, "Cari"],
  16163. "No users found": [null, "Pengguna tak ditemukan"],
  16164. "Click to add as a chat contact": [null, "Klik untuk menambahkan sebagai teman"],
  16165. "Click to open this room": [null, "Klik untuk membuka ruangan ini"],
  16166. "Show more information on this room": [null, "Tampilkan informasi ruangan ini"],
  16167. "Description:": [null, "Keterangan:"],
  16168. "Occupants:": [null, "Penghuni:"],
  16169. "Features:": [null, "Fitur:"],
  16170. "Requires authentication": [null, "Membutuhkan otentikasi"],
  16171. "Hidden": [null, "Tersembunyi"],
  16172. "Requires an invitation": [null, "Membutuhkan undangan"],
  16173. "Moderated": [null, "Dimoderasi"],
  16174. "Non-anonymous": [null, "Tidak anonim"],
  16175. "Open room": [null, "Ruangan terbuka"],
  16176. "Permanent room": [null, "Ruangan permanen"],
  16177. "Public": [null, "Umum"],
  16178. "Semi-anonymous": [null, "Semi-anonim"],
  16179. "Temporary room": [null, "Ruangan sementara"],
  16180. "Unmoderated": [null, "Tak dimoderasi"],
  16181. "Rooms": [null, "Ruangan"],
  16182. "Room name": [null, "Nama ruangan"],
  16183. "Nickname": [null, "Nama panggilan"],
  16184. "Server": [null, "Server"],
  16185. "Join": [null, "Ikuti"],
  16186. "Show rooms": [null, "Perlihatkan ruangan"],
  16187. "No rooms on %1$s": [null, "Tak ada ruangan di %1$s"],
  16188. "Rooms on %1$s": [null, "Ruangan di %1$s"],
  16189. "Set chatroom topic": [null, "Setel topik ruangan"],
  16190. "Kick user from chatroom": [null, "Tendang pengguna dari ruangan"],
  16191. "Ban user from chatroom": [null, "Larang pengguna dari ruangan"],
  16192. "Message": [null, "Pesan"],
  16193. "Save": [null, "Simpan"],
  16194. "Cancel": [null, "Batal"],
  16195. "An error occurred while trying to save the form.": [null, "Kesalahan terjadi saat menyimpan formulir ini."],
  16196. "This chatroom requires a password": [null, "Ruangan ini membutuhkan kata sandi"],
  16197. "Password: ": [null, "Kata sandi: "],
  16198. "Submit": [null, "Kirim"],
  16199. "This room is not anonymous": [null, "Ruangan ini tidak anonim"],
  16200. "This room now shows unavailable members": [null, "Ruangan ini menampilkan anggota yang tak tersedia"],
  16201. "This room does not show unavailable members": [null, "Ruangan ini tidak menampilkan anggota yang tak tersedia"],
  16202. "Non-privacy-related room configuration has changed": [null, "Konfigurasi ruangan yang tak berhubungan dengan privasi telah diubah"],
  16203. "Room logging is now enabled": [null, "Pencatatan di ruangan ini sekarang dinyalakan"],
  16204. "Room logging is now disabled": [null, "Pencatatan di ruangan ini sekarang dimatikan"],
  16205. "This room is now non-anonymous": [null, "Ruangan ini sekarang tak-anonim"],
  16206. "This room is now semi-anonymous": [null, "Ruangan ini sekarang semi-anonim"],
  16207. "This room is now fully-anonymous": [null, "Ruangan ini sekarang anonim"],
  16208. "A new room has been created": [null, "Ruangan baru telah dibuat"],
  16209. "Your nickname has been changed": [null, "Nama panggilan anda telah diubah"],
  16210. "<strong>%1$s</strong> has been banned": [null, "<strong>%1$s</strong> telah dicekal"],
  16211. "<strong>%1$s</strong> has been kicked out": [null, "<strong>%1$s</strong> telah ditendang keluar"],
  16212. "<strong>%1$s</strong> has been removed because of an affiliation change": [null, "<strong>%1$s</strong> telah dihapus karena perubahan afiliasi"],
  16213. "<strong>%1$s</strong> has been removed for not being a member": [null, "<strong>%1$s</strong> telah dihapus karena bukan anggota"],
  16214. "You have been banned from this room": [null, "Anda telah dicekal dari ruangan ini"],
  16215. "You have been kicked from this room": [null, "Anda telah ditendang dari ruangan ini"],
  16216. "You have been removed from this room because of an affiliation change": [null, "Anda telah dihapus dari ruangan ini karena perubahan afiliasi"],
  16217. "You have been removed from this room because the room has changed to members-only and you're not a member": [null, "Anda telah dihapus dari ruangan ini karena ruangan ini hanya terbuka untuk anggota dan anda bukan anggota"],
  16218. "You have been removed from this room because the MUC (Multi-user chat) service is being shut down.": [null, "Anda telah dihapus dari ruangan ini karena layanan MUC (Multi-user chat) telah dimatikan."],
  16219. "You are not on the member list of this room": [null, "Anda bukan anggota dari ruangan ini"],
  16220. "No nickname was specified": [null, "Nama panggilan belum ditentukan"],
  16221. "You are not allowed to create new rooms": [null, "Anda tak diizinkan untuk membuat ruangan baru"],
  16222. "Your nickname doesn't conform to this room's policies": [null, "Nama panggilan anda tidak sesuai aturan ruangan ini"],
  16223. "Your nickname is already taken": [null, "Nama panggilan anda telah digunakan orang lain"],
  16224. "This room does not (yet) exist": [null, "Ruangan ini belum dibuat"],
  16225. "This room has reached it's maximum number of occupants": [null, "Ruangan ini telah mencapai jumlah penghuni maksimum"],
  16226. "Topic set by %1$s to: %2$s": [null, "Topik diganti oleh %1$s menjadi: %2$s"],
  16227. "This user is a moderator": [null, "Pengguna ini adalah moderator"],
  16228. "This user can send messages in this room": [null, "Pengguna ini dapat mengirim pesan di ruangan ini"],
  16229. "This user can NOT send messages in this room": [null, "Pengguna ini tak dapat mengirim pesan di ruangan ini"],
  16230. "Click to chat with this contact": [null, "Klik untuk mulai perbinjangan dengan teman ini"],
  16231. "Click to remove this contact": [null, "Klik untuk menghapus teman ini"],
  16232. "Contact requests": [null, "Permintaan pertemanan"],
  16233. "My contacts": [null, "Teman saya"],
  16234. "Pending contacts": [null, "Teman yang menunggu"],
  16235. "Custom status": [null, "Status kustom"],
  16236. "Click to change your chat status": [null, "Klik untuk mengganti status"],
  16237. "Click here to write a custom status message": [null, "Klik untuk menulis status kustom"],
  16238. "online": [null, "terhubung"],
  16239. "busy": [null, "sibuk"],
  16240. "away for long": [null, "lama tak di tempat"],
  16241. "away": [null, "tak di tempat"],
  16242. "I am %1$s": [null, "Saya %1$s"],
  16243. "Sign in": [null, "Masuk"],
  16244. "XMPP/Jabber Username:": [null, "Nama pengguna XMPP/Jabber:"],
  16245. "Password:": [null, "Kata sandi:"],
  16246. "Log In": [null, "Masuk"],
  16247. "BOSH Service URL:": [null, "URL Layanan BOSH:"]
  16248. }
  16249. }
  16250. };
  16251. if (typeof define === 'function' && define.amd) {
  16252. define("id", ['jed'], function () {
  16253. return factory(new Jed(translations));
  16254. });
  16255. } else {
  16256. if (!window.locales) {
  16257. window.locales = {};
  16258. }
  16259. window.locales.id = factory(new Jed(translations));
  16260. }
  16261. }(this, function (id) {
  16262. return id;
  16263. }));
  16264. (function (root, factory) {
  16265. var translations = {
  16266. "domain": "converse",
  16267. "locale_data": {
  16268. "converse": {
  16269. "": {
  16270. "Project-Id-Version": "Converse.js 0.4",
  16271. "Report-Msgid-Bugs-To": "",
  16272. "POT-Creation-Date": "2013-09-15 21:55+0200",
  16273. "PO-Revision-Date": "2013-09-15 22:00+0200",
  16274. "Last-Translator": "Fabio Bas <ctrlaltca@gmail.com>",
  16275. "Language-Team": "Italian",
  16276. "Language": "it",
  16277. "MIME-Version": "1.0",
  16278. "Content-Type": "text/plain; charset=UTF-8",
  16279. "Content-Transfer-Encoding": "8bit",
  16280. "Plural-Forms": "nplurals=2; plural=(n != 1);",
  16281. "domain": "converse",
  16282. "lang": "it",
  16283. "plural_forms": "nplurals=2; plural=(n != 1);"
  16284. },
  16285. "unencrypted": [
  16286. null,
  16287. ""
  16288. ],
  16289. "unverified": [
  16290. null,
  16291. ""
  16292. ],
  16293. "verified": [
  16294. null,
  16295. ""
  16296. ],
  16297. "finished": [
  16298. null,
  16299. ""
  16300. ],
  16301. "Disconnected": [
  16302. null,
  16303. "Disconnesso"
  16304. ],
  16305. "Error": [
  16306. null,
  16307. "Errore"
  16308. ],
  16309. "Connecting": [
  16310. null,
  16311. "Connessione in corso"
  16312. ],
  16313. "Connection Failed": [
  16314. null,
  16315. "Connessione fallita"
  16316. ],
  16317. "Authenticating": [
  16318. null,
  16319. "Autenticazione in corso"
  16320. ],
  16321. "Authentication Failed": [
  16322. null,
  16323. "Autenticazione fallita"
  16324. ],
  16325. "Disconnecting": [
  16326. null,
  16327. "Disconnessione in corso"
  16328. ],
  16329. "Re-establishing encrypted session": [
  16330. null,
  16331. ""
  16332. ],
  16333. "Your browser needs to generate a private key, which will be used in your encrypted chat session. This can take up to 30 seconds during which your browser might freeze and become unresponsive.": [
  16334. null,
  16335. ""
  16336. ],
  16337. "Private key generated.": [
  16338. null,
  16339. ""
  16340. ],
  16341. "Authentication request from %1$s\n\nYour buddy is attempting to verify your identity, by asking you the question below.\n\n%2$s": [
  16342. null,
  16343. ""
  16344. ],
  16345. "Could not verify this user's identify.": [
  16346. null,
  16347. ""
  16348. ],
  16349. "Personal message": [
  16350. null,
  16351. "Messaggio personale"
  16352. ],
  16353. "Start encrypted conversation": [
  16354. null,
  16355. ""
  16356. ],
  16357. "Refresh encrypted conversation": [
  16358. null,
  16359. ""
  16360. ],
  16361. "End encrypted conversation": [
  16362. null,
  16363. ""
  16364. ],
  16365. "Verify with SMP": [
  16366. null,
  16367. ""
  16368. ],
  16369. "Verify with fingerprints": [
  16370. null,
  16371. ""
  16372. ],
  16373. "What's this?": [
  16374. null,
  16375. ""
  16376. ],
  16377. "me": [
  16378. null,
  16379. ""
  16380. ],
  16381. "Show this menu": [
  16382. null,
  16383. "Mostra questo menu"
  16384. ],
  16385. "Write in the third person": [
  16386. null,
  16387. "Scrivi in terza persona"
  16388. ],
  16389. "Remove messages": [
  16390. null,
  16391. "Rimuovi messaggi"
  16392. ],
  16393. "Your message could not be sent": [
  16394. null,
  16395. ""
  16396. ],
  16397. "We received an unencrypted message": [
  16398. null,
  16399. ""
  16400. ],
  16401. "We received an unreadable encrypted message": [
  16402. null,
  16403. ""
  16404. ],
  16405. "This user has requested an encrypted session.": [
  16406. null,
  16407. ""
  16408. ],
  16409. "Here are the fingerprints, please confirm them with %1$s, outside of this chat.\n\nFingerprint for you, %2$s: %3$s\n\nFingerprint for %1$s: %4$s\n\nIf you have confirmed that the fingerprints match, click OK, otherwise click Cancel.": [
  16410. null,
  16411. ""
  16412. ],
  16413. "You will be prompted to provide a security question and then an answer to that question.\n\nYour buddy will then be prompted the same question and if they type the exact same answer (case sensitive), their identity will have been verified.": [
  16414. null,
  16415. ""
  16416. ],
  16417. "What is your security question?": [
  16418. null,
  16419. ""
  16420. ],
  16421. "What is the answer to the security question?": [
  16422. null,
  16423. ""
  16424. ],
  16425. "Invalid authentication scheme provided": [
  16426. null,
  16427. ""
  16428. ],
  16429. "Your messages are not encrypted anymore": [
  16430. null,
  16431. ""
  16432. ],
  16433. "Your messages are now encrypted but your buddy's identity has not been verified.": [
  16434. null,
  16435. ""
  16436. ],
  16437. "Your buddy's identify has been verified.": [
  16438. null,
  16439. ""
  16440. ],
  16441. "Your buddy has ended encryption on their end, you should do the same.": [
  16442. null,
  16443. ""
  16444. ],
  16445. "Your messages are not encrypted. Click here to enable OTR encryption.": [
  16446. null,
  16447. ""
  16448. ],
  16449. "Your messages are encrypted, but your buddy has not been verified.": [
  16450. null,
  16451. ""
  16452. ],
  16453. "Your messages are encrypted and your buddy verified.": [
  16454. null,
  16455. ""
  16456. ],
  16457. "Your buddy has closed their end of the private session, you should do the same": [
  16458. null,
  16459. ""
  16460. ],
  16461. "Contacts": [
  16462. null,
  16463. "Contatti"
  16464. ],
  16465. "Online": [
  16466. null,
  16467. "In linea"
  16468. ],
  16469. "Busy": [
  16470. null,
  16471. "Occupato"
  16472. ],
  16473. "Away": [
  16474. null,
  16475. "Assente"
  16476. ],
  16477. "Offline": [
  16478. null,
  16479. "Non in linea"
  16480. ],
  16481. "Click to add new chat contacts": [
  16482. null,
  16483. "Clicca per aggiungere nuovi contatti alla chat"
  16484. ],
  16485. "Add a contact": [
  16486. null,
  16487. "Aggiungi contatti"
  16488. ],
  16489. "Contact username": [
  16490. null,
  16491. "Nome utente del contatto"
  16492. ],
  16493. "Add": [
  16494. null,
  16495. "Aggiungi"
  16496. ],
  16497. "Contact name": [
  16498. null,
  16499. "Nome del contatto"
  16500. ],
  16501. "Search": [
  16502. null,
  16503. "Cerca"
  16504. ],
  16505. "No users found": [
  16506. null,
  16507. "Nessun utente trovato"
  16508. ],
  16509. "Click to add as a chat contact": [
  16510. null,
  16511. "Clicca per aggiungere il contatto alla chat"
  16512. ],
  16513. "Click to open this room": [
  16514. null,
  16515. "Clicca per aprire questa stanza"
  16516. ],
  16517. "Show more information on this room": [
  16518. null,
  16519. "Mostra più informazioni su questa stanza"
  16520. ],
  16521. "Description:": [
  16522. null,
  16523. "Descrizione:"
  16524. ],
  16525. "Occupants:": [
  16526. null,
  16527. "Utenti presenti:"
  16528. ],
  16529. "Features:": [
  16530. null,
  16531. "Funzionalità:"
  16532. ],
  16533. "Requires authentication": [
  16534. null,
  16535. "Richiede autenticazione"
  16536. ],
  16537. "Hidden": [
  16538. null,
  16539. "Nascosta"
  16540. ],
  16541. "Requires an invitation": [
  16542. null,
  16543. "Richiede un invito"
  16544. ],
  16545. "Moderated": [
  16546. null,
  16547. "Moderata"
  16548. ],
  16549. "Non-anonymous": [
  16550. null,
  16551. "Non-anonima"
  16552. ],
  16553. "Open room": [
  16554. null,
  16555. "Stanza aperta"
  16556. ],
  16557. "Permanent room": [
  16558. null,
  16559. "Stanza permanente"
  16560. ],
  16561. "Public": [
  16562. null,
  16563. "Pubblica"
  16564. ],
  16565. "Semi-anonymous": [
  16566. null,
  16567. "Semi-anonima"
  16568. ],
  16569. "Temporary room": [
  16570. null,
  16571. "Stanza temporanea"
  16572. ],
  16573. "Unmoderated": [
  16574. null,
  16575. "Non moderata"
  16576. ],
  16577. "Rooms": [
  16578. null,
  16579. "Stanze"
  16580. ],
  16581. "Room name": [
  16582. null,
  16583. "Nome stanza"
  16584. ],
  16585. "Nickname": [
  16586. null,
  16587. "Soprannome"
  16588. ],
  16589. "Server": [
  16590. null,
  16591. "Server"
  16592. ],
  16593. "Join": [
  16594. null,
  16595. "Entra"
  16596. ],
  16597. "Show rooms": [
  16598. null,
  16599. "Mostra stanze"
  16600. ],
  16601. "No rooms on %1$s": [
  16602. null,
  16603. "Nessuna stanza su %1$s"
  16604. ],
  16605. "Rooms on %1$s": [
  16606. null,
  16607. "Stanze su %1$s"
  16608. ],
  16609. "Set chatroom topic": [
  16610. null,
  16611. "Cambia oggetto della stanza"
  16612. ],
  16613. "Kick user from chatroom": [
  16614. null,
  16615. "Espelli utente dalla stanza"
  16616. ],
  16617. "Ban user from chatroom": [
  16618. null,
  16619. "Bandisci utente dalla stanza"
  16620. ],
  16621. "Message": [
  16622. null,
  16623. "Messaggio"
  16624. ],
  16625. "Save": [
  16626. null,
  16627. "Salva"
  16628. ],
  16629. "Cancel": [
  16630. null,
  16631. "Annulla"
  16632. ],
  16633. "An error occurred while trying to save the form.": [
  16634. null,
  16635. "Errore durante il salvataggio del modulo"
  16636. ],
  16637. "This chatroom requires a password": [
  16638. null,
  16639. "Questa stanza richiede una password"
  16640. ],
  16641. "Password: ": [
  16642. null,
  16643. "Password: "
  16644. ],
  16645. "Submit": [
  16646. null,
  16647. "Invia"
  16648. ],
  16649. "This room is not anonymous": [
  16650. null,
  16651. "Questa stanza non è anonima"
  16652. ],
  16653. "This room now shows unavailable members": [
  16654. null,
  16655. "Questa stanza mostra i membri non disponibili al momento"
  16656. ],
  16657. "This room does not show unavailable members": [
  16658. null,
  16659. "Questa stanza non mostra i membri non disponibili"
  16660. ],
  16661. "Non-privacy-related room configuration has changed": [
  16662. null,
  16663. "Una configurazione della stanza non legata alla privacy è stata modificata"
  16664. ],
  16665. "Room logging is now enabled": [
  16666. null,
  16667. "La registrazione è abilitata nella stanza"
  16668. ],
  16669. "Room logging is now disabled": [
  16670. null,
  16671. "La registrazione è disabilitata nella stanza"
  16672. ],
  16673. "This room is now non-anonymous": [
  16674. null,
  16675. "Questa stanza è non-anonima"
  16676. ],
  16677. "This room is now semi-anonymous": [
  16678. null,
  16679. "Questa stanza è semi-anonima"
  16680. ],
  16681. "This room is now fully-anonymous": [
  16682. null,
  16683. "Questa stanza è completamente-anonima"
  16684. ],
  16685. "A new room has been created": [
  16686. null,
  16687. "Una nuova stanza è stata creata"
  16688. ],
  16689. "Your nickname has been changed": [
  16690. null,
  16691. "Il tuo soprannome è stato cambiato"
  16692. ],
  16693. "<strong>%1$s</strong> has been banned": [
  16694. null,
  16695. "<strong>%1$s</strong> è stato bandito"
  16696. ],
  16697. "<strong>%1$s</strong> has been kicked out": [
  16698. null,
  16699. "<strong>%1$s</strong> è stato espulso"
  16700. ],
  16701. "<strong>%1$s</strong> has been removed because of an affiliation change": [
  16702. null,
  16703. "<strong>%1$s</strong> è stato rimosso a causa di un cambio di affiliazione"
  16704. ],
  16705. "<strong>%1$s</strong> has been removed for not being a member": [
  16706. null,
  16707. "<strong>%1$s</strong> è stato rimosso in quanto non membro"
  16708. ],
  16709. "You have been banned from this room": [
  16710. null,
  16711. "Sei stato bandito da questa stanza"
  16712. ],
  16713. "You have been kicked from this room": [
  16714. null,
  16715. "Sei stato espulso da questa stanza"
  16716. ],
  16717. "You have been removed from this room because of an affiliation change": [
  16718. null,
  16719. "Sei stato rimosso da questa stanza a causa di un cambio di affiliazione"
  16720. ],
  16721. "You have been removed from this room because the room has changed to members-only and you're not a member": [
  16722. null,
  16723. "Sei stato rimosso da questa stanza poiché ora la stanza accetta solo membri"
  16724. ],
  16725. "You have been removed from this room because the MUC (Multi-user chat) service is being shut down.": [
  16726. null,
  16727. "Sei stato rimosso da questa stanza poiché il servizio MUC (Chat multi utente) è in fase di spegnimento"
  16728. ],
  16729. "You are not on the member list of this room": [
  16730. null,
  16731. "Non sei nella lista dei membri di questa stanza"
  16732. ],
  16733. "No nickname was specified": [
  16734. null,
  16735. "Nessun soprannome specificato"
  16736. ],
  16737. "You are not allowed to create new rooms": [
  16738. null,
  16739. "Non ti è permesso creare nuove stanze"
  16740. ],
  16741. "Your nickname doesn't conform to this room's policies": [
  16742. null,
  16743. "Il tuo soprannome non è conforme alle regole di questa stanza"
  16744. ],
  16745. "Your nickname is already taken": [
  16746. null,
  16747. "Il tuo soprannome è già utilizzato"
  16748. ],
  16749. "This room does not (yet) exist": [
  16750. null,
  16751. "Questa stanza non esiste (per ora)"
  16752. ],
  16753. "This room has reached it's maximum number of occupants": [
  16754. null,
  16755. "Questa stanza ha raggiunto il limite massimo di utenti"
  16756. ],
  16757. "Topic set by %1$s to: %2$s": [
  16758. null,
  16759. "Topic impostato da %1$s a: %2$s"
  16760. ],
  16761. "This user is a moderator": [
  16762. null,
  16763. "Questo utente è un moderatore"
  16764. ],
  16765. "This user can send messages in this room": [
  16766. null,
  16767. "Questo utente può inviare messaggi in questa stanza"
  16768. ],
  16769. "This user can NOT send messages in this room": [
  16770. null,
  16771. "Questo utente NON può inviare messaggi in questa stanza"
  16772. ],
  16773. "Click to chat with this contact": [
  16774. null,
  16775. "Clicca per parlare con questo contatto"
  16776. ],
  16777. "Click to remove this contact": [
  16778. null,
  16779. "Clicca per rimuovere questo contatto"
  16780. ],
  16781. "This contact is busy": [
  16782. null,
  16783. ""
  16784. ],
  16785. "This contact is online": [
  16786. null,
  16787. ""
  16788. ],
  16789. "This contact is offline": [
  16790. null,
  16791. ""
  16792. ],
  16793. "This contact is unavailable": [
  16794. null,
  16795. "Questa stanza mostra i membri non disponibili al momento"
  16796. ],
  16797. "This contact is away for an extended period": [
  16798. null,
  16799. ""
  16800. ],
  16801. "This contact is away": [
  16802. null,
  16803. ""
  16804. ],
  16805. "Contact requests": [
  16806. null,
  16807. "Richieste dei contatti"
  16808. ],
  16809. "My contacts": [
  16810. null,
  16811. "I miei contatti"
  16812. ],
  16813. "Pending contacts": [
  16814. null,
  16815. "Contatti in attesa"
  16816. ],
  16817. "Custom status": [
  16818. null,
  16819. "Stato personalizzato"
  16820. ],
  16821. "Click to change your chat status": [
  16822. null,
  16823. "Clicca per cambiare il tuo stato"
  16824. ],
  16825. "Click here to write a custom status message": [
  16826. null,
  16827. "Clicca qui per scrivere un messaggio di stato personalizzato"
  16828. ],
  16829. "online": [
  16830. null,
  16831. "in linea"
  16832. ],
  16833. "busy": [
  16834. null,
  16835. "occupato"
  16836. ],
  16837. "away for long": [
  16838. null,
  16839. "assente da molto"
  16840. ],
  16841. "away": [
  16842. null,
  16843. "assente"
  16844. ],
  16845. "I am %1$s": [
  16846. null,
  16847. "Sono %1$s"
  16848. ],
  16849. "Sign in": [
  16850. null,
  16851. "Accesso"
  16852. ],
  16853. "XMPP/Jabber Username:": [
  16854. null,
  16855. "Nome utente:"
  16856. ],
  16857. "Password:": [
  16858. null,
  16859. "Password:"
  16860. ],
  16861. "Log In": [
  16862. null,
  16863. "Entra"
  16864. ],
  16865. "BOSH Service URL:": [
  16866. null,
  16867. "Indirizzo servizio BOSH:"
  16868. ],
  16869. "Online Contacts": [
  16870. null,
  16871. "Contatti in linea"
  16872. ],
  16873. "Connected": [
  16874. null,
  16875. "Connesso"
  16876. ],
  16877. "Attached": [
  16878. null,
  16879. "Allegato"
  16880. ]
  16881. }
  16882. }
  16883. };
  16884. if (typeof define === 'function' && define.amd) {
  16885. define("it", ['jed'], function () {
  16886. return factory(new Jed(translations));
  16887. });
  16888. } else {
  16889. if (!window.locales) {
  16890. window.locales = {};
  16891. }
  16892. window.locales.it = factory(new Jed(translations));
  16893. }
  16894. }(this, function (it) {
  16895. return it;
  16896. }));
  16897. (function (root, factory) {
  16898. var translations = {
  16899. "domain": "converse",
  16900. "locale_data": {
  16901. "converse": {
  16902. "": {
  16903. "Project-Id-Version": "Converse.js 0.4",
  16904. "Report-Msgid-Bugs-To": "",
  16905. "POT-Creation-Date": "2014-01-07 11:12+0900",
  16906. "PO-Revision-Date": "2014-01-07 11:32+0900",
  16907. "Last-Translator": "Mako N <mako@pasero.net>",
  16908. "Language-Team": "Language JA",
  16909. "Language": "JA",
  16910. "MIME-Version": "1.0",
  16911. "Content-Type": "text/plain; charset=UTF-8",
  16912. "Content-Transfer-Encoding": "8bit",
  16913. "Plural-Forms": "nplurals=1; plural=0;"
  16914. },
  16915. "unencrypted": [
  16916. null,
  16917. "暗号化されていません"
  16918. ],
  16919. "unverified": [
  16920. null,
  16921. "検証されていません"
  16922. ],
  16923. "verified": [
  16924. null,
  16925. "検証されました"
  16926. ],
  16927. "finished": [
  16928. null,
  16929. "完了"
  16930. ],
  16931. "This contact is busy": [
  16932. null,
  16933. "この相手先は取り込み中です"
  16934. ],
  16935. "This contact is online": [
  16936. null,
  16937. "この相手先は在席しています"
  16938. ],
  16939. "This contact is offline": [
  16940. null,
  16941. "この相手先はオフラインです"
  16942. ],
  16943. "This contact is unavailable": [
  16944. null,
  16945. "この相手先は不通です"
  16946. ],
  16947. "This contact is away for an extended period": [
  16948. null,
  16949. "この相手先は不在です"
  16950. ],
  16951. "This contact is away": [
  16952. null,
  16953. "この相手先は離席中です"
  16954. ],
  16955. "Disconnected": [
  16956. null,
  16957. "切断中"
  16958. ],
  16959. "Error": [
  16960. null,
  16961. "エラー"
  16962. ],
  16963. "Connecting": [
  16964. null,
  16965. "接続中です"
  16966. ],
  16967. "Connection Failed": [
  16968. null,
  16969. "接続に失敗しました"
  16970. ],
  16971. "Authenticating": [
  16972. null,
  16973. "認証中"
  16974. ],
  16975. "Authentication Failed": [
  16976. null,
  16977. "認証に失敗"
  16978. ],
  16979. "Disconnecting": [
  16980. null,
  16981. "切断"
  16982. ],
  16983. "Online Contacts": [
  16984. null,
  16985. "オンラインの相手先"
  16986. ],
  16987. "Re-establishing encrypted session": [
  16988. null,
  16989. "暗号化セッションの再接続"
  16990. ],
  16991. "Your browser needs to generate a private key, which will be used in your encrypted chat session. This can take up to 30 seconds during which your browser might freeze and become unresponsive.": [
  16992. null,
  16993. "暗号化チャットで使用する秘密鍵を生成する必要があります。これには30秒ほどかかり、そのあいだブラウザがフリーズして反応しないかもしれません。"
  16994. ],
  16995. "Private key generated.": [
  16996. null,
  16997. "秘密鍵を生成しました。"
  16998. ],
  16999. "Authentication request from %1$s\n\nYour buddy is attempting to verify your identity, by asking you the question below.\n\n%2$s": [
  17000. null,
  17001. "%1$s からの認証のリクエスト\n\n相手はあなたの本人性を検証しようとしています。次の質問に答えてください。\n\n%2$s"
  17002. ],
  17003. "Could not verify this user's identify.": [
  17004. null,
  17005. "このユーザーの本人性を検証できませんでした。"
  17006. ],
  17007. "Personal message": [
  17008. null,
  17009. "私信"
  17010. ],
  17011. "Start encrypted conversation": [
  17012. null,
  17013. "暗号化された会話を開始"
  17014. ],
  17015. "Refresh encrypted conversation": [
  17016. null,
  17017. "暗号化された会話をリフレッシュ"
  17018. ],
  17019. "End encrypted conversation": [
  17020. null,
  17021. "暗号化された会話を終了"
  17022. ],
  17023. "Verify with SMP": [
  17024. null,
  17025. "SMP で検証"
  17026. ],
  17027. "Verify with fingerprints": [
  17028. null,
  17029. "鍵指紋で検証"
  17030. ],
  17031. "What's this?": [
  17032. null,
  17033. "これは何ですか?"
  17034. ],
  17035. "me": [
  17036. null,
  17037. "私"
  17038. ],
  17039. "Show this menu": [
  17040. null,
  17041. "このメニューを表示"
  17042. ],
  17043. "Write in the third person": [
  17044. null,
  17045. "第三者に書く"
  17046. ],
  17047. "Remove messages": [
  17048. null,
  17049. "メッセージを削除"
  17050. ],
  17051. "Your message could not be sent": [
  17052. null,
  17053. "メッセージを送信できませんでした"
  17054. ],
  17055. "We received an unencrypted message": [
  17056. null,
  17057. "暗号化されていないメッセージを受信しました"
  17058. ],
  17059. "We received an unreadable encrypted message": [
  17060. null,
  17061. "読めない暗号化メッセージを受信しました"
  17062. ],
  17063. "This user has requested an encrypted session.": [
  17064. null,
  17065. "このユーザーは暗号化セッションを求めています。"
  17066. ],
  17067. "Here are the fingerprints, please confirm them with %1$s, outside of this chat.\n\nFingerprint for you, %2$s: %3$s\n\nFingerprint for %1$s: %4$s\n\nIf you have confirmed that the fingerprints match, click OK, otherwise click Cancel.": [
  17068. null,
  17069. "これは鍵指紋です。チャット以外の方法でこれらを %1$s と確認してください。\n\nあなた %2$s の鍵指紋: %3$s\n\n%1$s の鍵指紋: %4$s\n\n確認して、鍵指紋が正しければ「OK」を、正しくなければ「キャンセル」をクリックしてください。"
  17070. ],
  17071. "You will be prompted to provide a security question and then an answer to that question.\n\nYour buddy will then be prompted the same question and if they type the exact same answer (case sensitive), their identity will have been verified.": [
  17072. null,
  17073. "秘密の質問を入力し、それに対して答えるように促されます。\n\n相手にも、同じ質問が表示され、正しく同じ答(大文字・小文字は区別されます)を入力することで、本人性を検証します。"
  17074. ],
  17075. "What is your security question?": [
  17076. null,
  17077. "秘密の質問はなんですか?"
  17078. ],
  17079. "What is the answer to the security question?": [
  17080. null,
  17081. "秘密の質問の答はなんですか?"
  17082. ],
  17083. "Invalid authentication scheme provided": [
  17084. null,
  17085. "認証の方式が正しくありません"
  17086. ],
  17087. "Your messages are not encrypted anymore": [
  17088. null,
  17089. "メッセージはもう暗号化されません"
  17090. ],
  17091. "Your messages are now encrypted but your buddy's identity has not been verified.": [
  17092. null,
  17093. "メッセージは暗号化されますが、相手が本人であることは検証されていません。"
  17094. ],
  17095. "Your buddy's identify has been verified.": [
  17096. null,
  17097. "相手の本人性を検証しました。"
  17098. ],
  17099. "Your buddy has ended encryption on their end, you should do the same.": [
  17100. null,
  17101. "相手は、暗号化を終了しました。あなたもそれに合わせる必要があります。"
  17102. ],
  17103. "Your messages are not encrypted. Click here to enable OTR encryption.": [
  17104. null,
  17105. "メッセージは暗号化されません。OTR 暗号化を有効にするにはここをクリックしてください。"
  17106. ],
  17107. "Your messages are encrypted, but your buddy has not been verified.": [
  17108. null,
  17109. "メッセージは暗号化されますが、相手は検証されていません。"
  17110. ],
  17111. "Your messages are encrypted and your buddy verified.": [
  17112. null,
  17113. "メッセージは暗号化され、相手も検証されています。"
  17114. ],
  17115. "Your buddy has closed their end of the private session, you should do the same": [
  17116. null,
  17117. "相手は私信を終了しました。あなたも同じようにしてください"
  17118. ],
  17119. "Contacts": [
  17120. null,
  17121. "相手先"
  17122. ],
  17123. "Online": [
  17124. null,
  17125. "オンライン"
  17126. ],
  17127. "Busy": [
  17128. null,
  17129. "取り込み中"
  17130. ],
  17131. "Away": [
  17132. null,
  17133. "離席中"
  17134. ],
  17135. "Offline": [
  17136. null,
  17137. "オフライン"
  17138. ],
  17139. "Click to add new chat contacts": [
  17140. null,
  17141. "クリックして新しいチャットの相手先を追加"
  17142. ],
  17143. "Add a contact": [
  17144. null,
  17145. "相手先を追加"
  17146. ],
  17147. "Contact username": [
  17148. null,
  17149. "相手先の名前"
  17150. ],
  17151. "Add": [
  17152. null,
  17153. "追加"
  17154. ],
  17155. "Contact name": [
  17156. null,
  17157. "名前"
  17158. ],
  17159. "Search": [
  17160. null,
  17161. "検索"
  17162. ],
  17163. "No users found": [
  17164. null,
  17165. "ユーザーが見つかりません"
  17166. ],
  17167. "Click to add as a chat contact": [
  17168. null,
  17169. "クリックしてチャットの相手先として追加"
  17170. ],
  17171. "Click to open this room": [
  17172. null,
  17173. "クリックしてこの談話室を開く"
  17174. ],
  17175. "Show more information on this room": [
  17176. null,
  17177. "この談話室についての詳細を見る"
  17178. ],
  17179. "Description:": [
  17180. null,
  17181. "説明: "
  17182. ],
  17183. "Occupants:": [
  17184. null,
  17185. "入室者:"
  17186. ],
  17187. "Features:": [
  17188. null,
  17189. "特徴:"
  17190. ],
  17191. "Requires authentication": [
  17192. null,
  17193. "認証の要求"
  17194. ],
  17195. "Hidden": [
  17196. null,
  17197. "非表示"
  17198. ],
  17199. "Requires an invitation": [
  17200. null,
  17201. "招待の要求"
  17202. ],
  17203. "Moderated": [
  17204. null,
  17205. "発言制限"
  17206. ],
  17207. "Non-anonymous": [
  17208. null,
  17209. "非匿名"
  17210. ],
  17211. "Open room": [
  17212. null,
  17213. "開放談話室"
  17214. ],
  17215. "Permanent room": [
  17216. null,
  17217. "常設談話室"
  17218. ],
  17219. "Public": [
  17220. null,
  17221. "公開談話室"
  17222. ],
  17223. "Semi-anonymous": [
  17224. null,
  17225. "半匿名"
  17226. ],
  17227. "Temporary room": [
  17228. null,
  17229. "臨時談話室"
  17230. ],
  17231. "Unmoderated": [
  17232. null,
  17233. "発言制限なし"
  17234. ],
  17235. "Rooms": [
  17236. null,
  17237. "談話室"
  17238. ],
  17239. "Room name": [
  17240. null,
  17241. "談話室の名前"
  17242. ],
  17243. "Nickname": [
  17244. null,
  17245. "ニックネーム"
  17246. ],
  17247. "Server": [
  17248. null,
  17249. "サーバー"
  17250. ],
  17251. "Join": [
  17252. null,
  17253. "入室"
  17254. ],
  17255. "Show rooms": [
  17256. null,
  17257. "談話室一覧を見る"
  17258. ],
  17259. "No rooms on %1$s": [
  17260. null,
  17261. "%1$s に談話室はありません"
  17262. ],
  17263. "Rooms on %1$s": [
  17264. null,
  17265. "%1$s の談話室一覧"
  17266. ],
  17267. "Set chatroom topic": [
  17268. null,
  17269. "談話室の話題を設定"
  17270. ],
  17271. "Kick user from chatroom": [
  17272. null,
  17273. "ユーザーを談話室から蹴り出す"
  17274. ],
  17275. "Ban user from chatroom": [
  17276. null,
  17277. "ユーザーを談話室から締め出す"
  17278. ],
  17279. "Message": [
  17280. null,
  17281. "メッセージ"
  17282. ],
  17283. "Save": [
  17284. null,
  17285. "保存"
  17286. ],
  17287. "Cancel": [
  17288. null,
  17289. "キャンセル"
  17290. ],
  17291. "An error occurred while trying to save the form.": [
  17292. null,
  17293. "フォームを保存する際にエラーが発生しました。"
  17294. ],
  17295. "This chatroom requires a password": [
  17296. null,
  17297. "この談話室にはパスワードが必要です"
  17298. ],
  17299. "Password: ": [
  17300. null,
  17301. "パスワード:"
  17302. ],
  17303. "Submit": [
  17304. null,
  17305. "送信"
  17306. ],
  17307. "This room is not anonymous": [
  17308. null,
  17309. "この談話室は非匿名です"
  17310. ],
  17311. "This room now shows unavailable members": [
  17312. null,
  17313. "この談話室はメンバー以外にも見えます"
  17314. ],
  17315. "This room does not show unavailable members": [
  17316. null,
  17317. "この談話室はメンバー以外には見えません"
  17318. ],
  17319. "Non-privacy-related room configuration has changed": [
  17320. null,
  17321. "談話室の設定(プライバシーに無関係)が変更されました"
  17322. ],
  17323. "Room logging is now enabled": [
  17324. null,
  17325. "談話室の記録を取りはじめます"
  17326. ],
  17327. "Room logging is now disabled": [
  17328. null,
  17329. "談話室の記録を止めます"
  17330. ],
  17331. "This room is now non-anonymous": [
  17332. null,
  17333. "この談話室はただいま非匿名です"
  17334. ],
  17335. "This room is now semi-anonymous": [
  17336. null,
  17337. "この談話室はただいま半匿名です"
  17338. ],
  17339. "This room is now fully-anonymous": [
  17340. null,
  17341. "この談話室はただいま匿名です"
  17342. ],
  17343. "A new room has been created": [
  17344. null,
  17345. "新しい談話室が作成されました"
  17346. ],
  17347. "Your nickname has been changed": [
  17348. null,
  17349. "ニックネームを変更しました"
  17350. ],
  17351. "<strong>%1$s</strong> has been banned": [
  17352. null,
  17353. "<strong>%1$s</strong> を締め出しました"
  17354. ],
  17355. "<strong>%1$s</strong> has been kicked out": [
  17356. null,
  17357. "<strong>%1$s</strong> を蹴り出しました"
  17358. ],
  17359. "<strong>%1$s</strong> has been removed because of an affiliation change": [
  17360. null,
  17361. "分掌の変更のため、<strong>%1$s</strong> を削除しました"
  17362. ],
  17363. "<strong>%1$s</strong> has been removed for not being a member": [
  17364. null,
  17365. "メンバーでなくなったため、<strong>%1$s</strong> を削除しました"
  17366. ],
  17367. "You have been banned from this room": [
  17368. null,
  17369. "この談話室から締め出されました"
  17370. ],
  17371. "You have been kicked from this room": [
  17372. null,
  17373. "この談話室から蹴り出されました"
  17374. ],
  17375. "You have been removed from this room because of an affiliation change": [
  17376. null,
  17377. "分掌の変更のため、この談話室から削除されました"
  17378. ],
  17379. "You have been removed from this room because the room has changed to members-only and you're not a member": [
  17380. null,
  17381. "談話室がメンバー制に変更されました。メンバーではないため、この談話室から削除されました"
  17382. ],
  17383. "You have been removed from this room because the MUC (Multi-user chat) service is being shut down.": [
  17384. null,
  17385. "MUC(グループチャット)のサービスが停止したため、この談話室から削除されました。"
  17386. ],
  17387. "You are not on the member list of this room": [
  17388. null,
  17389. "この談話室のメンバー一覧にいません"
  17390. ],
  17391. "No nickname was specified": [
  17392. null,
  17393. "ニックネームがありません"
  17394. ],
  17395. "You are not allowed to create new rooms": [
  17396. null,
  17397. "新しい談話室を作成する権限がありません"
  17398. ],
  17399. "Your nickname doesn't conform to this room's policies": [
  17400. null,
  17401. "ニックネームがこの談話室のポリシーに従っていません"
  17402. ],
  17403. "Your nickname is already taken": [
  17404. null,
  17405. "ニックネームは既に使われています"
  17406. ],
  17407. "This room does not (yet) exist": [
  17408. null,
  17409. "この談話室は存在しません"
  17410. ],
  17411. "This room has reached it's maximum number of occupants": [
  17412. null,
  17413. "この談話室は入室者数の上限に達しています"
  17414. ],
  17415. "Topic set by %1$s to: %2$s": [
  17416. null,
  17417. "%1$s が話題を設定しました: %2$s"
  17418. ],
  17419. "This user is a moderator": [
  17420. null,
  17421. "このユーザーは司会者です"
  17422. ],
  17423. "This user can send messages in this room": [
  17424. null,
  17425. "このユーザーはこの談話室で発言できます"
  17426. ],
  17427. "This user can NOT send messages in this room": [
  17428. null,
  17429. "このユーザーはこの談話室で発言できません"
  17430. ],
  17431. "Click to chat with this contact": [
  17432. null,
  17433. "クリックしてこの相手先とチャット"
  17434. ],
  17435. "Click to remove this contact": [
  17436. null,
  17437. "クリックしてこの相手先を削除"
  17438. ],
  17439. "Contact requests": [
  17440. null,
  17441. "会話に呼び出し"
  17442. ],
  17443. "My contacts": [
  17444. null,
  17445. "相手先一覧"
  17446. ],
  17447. "Pending contacts": [
  17448. null,
  17449. "保留中の相手先"
  17450. ],
  17451. "Custom status": [
  17452. null,
  17453. "独自の在席状況"
  17454. ],
  17455. "Click to change your chat status": [
  17456. null,
  17457. "クリックして、在席状況を変更"
  17458. ],
  17459. "Click here to write a custom status message": [
  17460. null,
  17461. "状況メッセージを入力するには、ここをクリック"
  17462. ],
  17463. "online": [
  17464. null,
  17465. "在席"
  17466. ],
  17467. "busy": [
  17468. null,
  17469. "取り込み中"
  17470. ],
  17471. "away for long": [
  17472. null,
  17473. "不在"
  17474. ],
  17475. "away": [
  17476. null,
  17477. "離席中"
  17478. ],
  17479. "I am %1$s": [
  17480. null,
  17481. "私はいま %1$s"
  17482. ],
  17483. "Sign in": [
  17484. null,
  17485. "サインイン"
  17486. ],
  17487. "XMPP/Jabber Username:": [
  17488. null,
  17489. "XMPP/Jabber ユーザー名:"
  17490. ],
  17491. "Password:": [
  17492. null,
  17493. "パスワード:"
  17494. ],
  17495. "Log In": [
  17496. null,
  17497. "ログイン"
  17498. ],
  17499. "BOSH Service URL:": [
  17500. null,
  17501. "BOSH サービス URL:"
  17502. ]
  17503. }
  17504. }
  17505. };
  17506. if (typeof define === 'function' && define.amd) {
  17507. define("ja", ['jed'], function () {
  17508. return factory(new Jed(translations));
  17509. });
  17510. } else {
  17511. if (!window.locales) {
  17512. window.locales = {};
  17513. }
  17514. window.locales.ja = factory(new Jed(translations));
  17515. }
  17516. }(this, function (ja) {
  17517. return ja;
  17518. }));
  17519. (function (root, factory) {
  17520. var translations = {
  17521. "domain": "converse",
  17522. "locale_data": {
  17523. "converse": {
  17524. "": {
  17525. "Project-Id-Version": "Converse.js 0.4",
  17526. "Report-Msgid-Bugs-To": "",
  17527. "POT-Creation-Date": "2013-09-15 21:55+0200",
  17528. "PO-Revision-Date": "2013-09-15 22:03+0200",
  17529. "Last-Translator": "Maarten Kling <maarten@fourdigits.nl>",
  17530. "Language-Team": "Dutch",
  17531. "Language": "nl",
  17532. "MIME-Version": "1.0",
  17533. "Content-Type": "text/plain; charset=UTF-8",
  17534. "Content-Transfer-Encoding": "8bit",
  17535. "Plural-Forms": "nplurals=2; plural=(n != 1);",
  17536. "domain": "converse",
  17537. "lang": "nl",
  17538. "plural_forms": "nplurals=2; plural=(n != 1);"
  17539. },
  17540. "unencrypted": [
  17541. null,
  17542. "ongecodeerde"
  17543. ],
  17544. "unverified": [
  17545. null,
  17546. "niet geverifieerd"
  17547. ],
  17548. "verified": [
  17549. null,
  17550. "geverifieerd"
  17551. ],
  17552. "finished": [
  17553. null,
  17554. "klaar"
  17555. ],
  17556. "Disconnected": [
  17557. null,
  17558. "Verbinding verbroken."
  17559. ],
  17560. "Error": [
  17561. null,
  17562. "Error"
  17563. ],
  17564. "Connecting": [
  17565. null,
  17566. "Verbinden"
  17567. ],
  17568. "Connection Failed": [
  17569. null,
  17570. "Verbinden mislukt"
  17571. ],
  17572. "Authenticating": [
  17573. null,
  17574. "Authenticeren"
  17575. ],
  17576. "Authentication Failed": [
  17577. null,
  17578. "Authenticeren mislukt"
  17579. ],
  17580. "Disconnecting": [
  17581. null,
  17582. ""
  17583. ],
  17584. "Re-establishing encrypted session": [
  17585. null,
  17586. "Bezig versleutelde sessie te herstellen"
  17587. ],
  17588. "Your browser needs to generate a private key, which will be used in your encrypted chat session. This can take up to 30 seconds during which your browser might freeze and become unresponsive.": [
  17589. null,
  17590. ""
  17591. ],
  17592. "Private key generated.": [
  17593. null,
  17594. "Private key gegenereerd."
  17595. ],
  17596. "Authentication request from %1$s\n\nYour buddy is attempting to verify your identity, by asking you the question below.\n\n%2$s": [
  17597. null,
  17598. ""
  17599. ],
  17600. "Could not verify this user's identify.": [
  17601. null,
  17602. "Niet kon de identiteit van deze gebruiker niet identificeren."
  17603. ],
  17604. "Personal message": [
  17605. null,
  17606. "Persoonlijk bericht"
  17607. ],
  17608. "Start encrypted conversation": [
  17609. null,
  17610. "Start encrypted gesprek"
  17611. ],
  17612. "Refresh encrypted conversation": [
  17613. null,
  17614. "Ververs encrypted gesprek"
  17615. ],
  17616. "End encrypted conversation": [
  17617. null,
  17618. "Beeindig encrypted gesprek"
  17619. ],
  17620. "Verify with SMP": [
  17621. null,
  17622. ""
  17623. ],
  17624. "Verify with fingerprints": [
  17625. null,
  17626. ""
  17627. ],
  17628. "What's this?": [
  17629. null,
  17630. "Wat is dit?"
  17631. ],
  17632. "me": [
  17633. null,
  17634. "ikzelf"
  17635. ],
  17636. "Show this menu": [
  17637. null,
  17638. "Toon dit menu"
  17639. ],
  17640. "Write in the third person": [
  17641. null,
  17642. "Schrijf in de 3de persoon"
  17643. ],
  17644. "Remove messages": [
  17645. null,
  17646. "Verwijder bericht"
  17647. ],
  17648. "Your message could not be sent": [
  17649. null,
  17650. "Je bericht kon niet worden verzonden"
  17651. ],
  17652. "We received an unencrypted message": [
  17653. null,
  17654. "We ontvingen een unencrypted bericht "
  17655. ],
  17656. "We received an unreadable encrypted message": [
  17657. null,
  17658. "We ontvangen een onleesbaar unencrypted bericht"
  17659. ],
  17660. "This user has requested an encrypted session.": [
  17661. null,
  17662. "Deze gebruiker heeft een encrypted sessie aangevraagd."
  17663. ],
  17664. "Here are the fingerprints, please confirm them with %1$s, outside of this chat.\n\nFingerprint for you, %2$s: %3$s\n\nFingerprint for %1$s: %4$s\n\nIf you have confirmed that the fingerprints match, click OK, otherwise click Cancel.": [
  17665. null,
  17666. ""
  17667. ],
  17668. "You will be prompted to provide a security question and then an answer to that question.\n\nYour buddy will then be prompted the same question and if they type the exact same answer (case sensitive), their identity will have been verified.": [
  17669. null,
  17670. ""
  17671. ],
  17672. "What is your security question?": [
  17673. null,
  17674. "Wat is jou sericury vraag?"
  17675. ],
  17676. "What is the answer to the security question?": [
  17677. null,
  17678. "Wat is het antwoord op de security vraag?"
  17679. ],
  17680. "Invalid authentication scheme provided": [
  17681. null,
  17682. ""
  17683. ],
  17684. "Your messages are not encrypted anymore": [
  17685. null,
  17686. "Je berichten zijn niet meer encrypted"
  17687. ],
  17688. "Your messages are now encrypted but your buddy's identity has not been verified.": [
  17689. null,
  17690. ""
  17691. ],
  17692. "Your buddy's identify has been verified.": [
  17693. null,
  17694. "Jou contact is geverifieerd"
  17695. ],
  17696. "Your buddy has ended encryption on their end, you should do the same.": [
  17697. null,
  17698. "Jou contact heeft encryption aanstaan, je moet het zelfde doen."
  17699. ],
  17700. "Your messages are not encrypted. Click here to enable OTR encryption.": [
  17701. null,
  17702. "Jou bericht is niet encrypted. KLik hier om ORC encrytion aan te zetten."
  17703. ],
  17704. "Your messages are encrypted, but your buddy has not been verified.": [
  17705. null,
  17706. "Jou berichten zijn encrypted, maar je contact is niet geverifieerd."
  17707. ],
  17708. "Your messages are encrypted and your buddy verified.": [
  17709. null,
  17710. "Jou bericht is encrypted en jou contact is geverifieerd."
  17711. ],
  17712. "Your buddy has closed their end of the private session, you should do the same": [
  17713. null,
  17714. ""
  17715. ],
  17716. "Contacts": [
  17717. null,
  17718. "Contacten"
  17719. ],
  17720. "Online": [
  17721. null,
  17722. "Online"
  17723. ],
  17724. "Busy": [
  17725. null,
  17726. "Bezet"
  17727. ],
  17728. "Away": [
  17729. null,
  17730. "Afwezig"
  17731. ],
  17732. "Offline": [
  17733. null,
  17734. ""
  17735. ],
  17736. "Click to add new chat contacts": [
  17737. null,
  17738. "Klik om nieuwe contacten toe te voegen"
  17739. ],
  17740. "Add a contact": [
  17741. null,
  17742. "Voeg contact toe"
  17743. ],
  17744. "Contact username": [
  17745. null,
  17746. "Contact gebruikernaam"
  17747. ],
  17748. "Add": [
  17749. null,
  17750. "Toevoegen"
  17751. ],
  17752. "Contact name": [
  17753. null,
  17754. "Contact naam"
  17755. ],
  17756. "Search": [
  17757. null,
  17758. "Zoeken"
  17759. ],
  17760. "No users found": [
  17761. null,
  17762. "Geen gebruikers gevonden"
  17763. ],
  17764. "Click to add as a chat contact": [
  17765. null,
  17766. "Klik om contact toe te voegen"
  17767. ],
  17768. "Click to open this room": [
  17769. null,
  17770. "Klik om room te openen"
  17771. ],
  17772. "Show more information on this room": [
  17773. null,
  17774. "Toon meer informatie over deze room"
  17775. ],
  17776. "Description:": [
  17777. null,
  17778. "Beschrijving"
  17779. ],
  17780. "Occupants:": [
  17781. null,
  17782. "Deelnemers:"
  17783. ],
  17784. "Features:": [
  17785. null,
  17786. "Functies:"
  17787. ],
  17788. "Requires authentication": [
  17789. null,
  17790. "Verificatie vereist"
  17791. ],
  17792. "Hidden": [
  17793. null,
  17794. "Verborgen"
  17795. ],
  17796. "Requires an invitation": [
  17797. null,
  17798. "Veriest een uitnodiging"
  17799. ],
  17800. "Moderated": [
  17801. null,
  17802. "Gemodereerd"
  17803. ],
  17804. "Non-anonymous": [
  17805. null,
  17806. "Niet annoniem"
  17807. ],
  17808. "Open room": [
  17809. null,
  17810. "Open room"
  17811. ],
  17812. "Permanent room": [
  17813. null,
  17814. "Blijvend room"
  17815. ],
  17816. "Public": [
  17817. null,
  17818. "Publiek"
  17819. ],
  17820. "Semi-anonymous": [
  17821. null,
  17822. "Semi annoniem"
  17823. ],
  17824. "Temporary room": [
  17825. null,
  17826. "Tijdelijke room"
  17827. ],
  17828. "Unmoderated": [
  17829. null,
  17830. "Niet gemodereerd"
  17831. ],
  17832. "Rooms": [
  17833. null,
  17834. "Rooms"
  17835. ],
  17836. "Room name": [
  17837. null,
  17838. "Room naam"
  17839. ],
  17840. "Nickname": [
  17841. null,
  17842. "Nickname"
  17843. ],
  17844. "Server": [
  17845. null,
  17846. "Server"
  17847. ],
  17848. "Join": [
  17849. null,
  17850. "Deelnemen"
  17851. ],
  17852. "Show rooms": [
  17853. null,
  17854. "Toon rooms"
  17855. ],
  17856. "No rooms on %1$s": [
  17857. null,
  17858. "Geen room op %1$s"
  17859. ],
  17860. "Rooms on %1$s": [
  17861. null,
  17862. "Room op %1$s"
  17863. ],
  17864. "Set chatroom topic": [
  17865. null,
  17866. "Zet chatroom topic"
  17867. ],
  17868. "Kick user from chatroom": [
  17869. null,
  17870. "Goei gebruiker uit chatroom"
  17871. ],
  17872. "Ban user from chatroom": [
  17873. null,
  17874. "Ban gebruiker van chatroom"
  17875. ],
  17876. "Message": [
  17877. null,
  17878. "Bericht"
  17879. ],
  17880. "Save": [
  17881. null,
  17882. "Opslaan"
  17883. ],
  17884. "Cancel": [
  17885. null,
  17886. "Annuleren"
  17887. ],
  17888. "An error occurred while trying to save the form.": [
  17889. null,
  17890. "Een error tijdens het opslaan van het formulier."
  17891. ],
  17892. "This chatroom requires a password": [
  17893. null,
  17894. "Chatroom heeft een wachtwoord"
  17895. ],
  17896. "Password: ": [
  17897. null,
  17898. "Wachtwoord: "
  17899. ],
  17900. "Submit": [
  17901. null,
  17902. "Indienen"
  17903. ],
  17904. "This room is not anonymous": [
  17905. null,
  17906. "Deze room is niet annoniem"
  17907. ],
  17908. "This room now shows unavailable members": [
  17909. null,
  17910. ""
  17911. ],
  17912. "This room does not show unavailable members": [
  17913. null,
  17914. ""
  17915. ],
  17916. "Non-privacy-related room configuration has changed": [
  17917. null,
  17918. ""
  17919. ],
  17920. "Room logging is now enabled": [
  17921. null,
  17922. ""
  17923. ],
  17924. "Room logging is now disabled": [
  17925. null,
  17926. ""
  17927. ],
  17928. "This room is now non-anonymous": [
  17929. null,
  17930. "Deze room is nu niet annoniem"
  17931. ],
  17932. "This room is now semi-anonymous": [
  17933. null,
  17934. "Deze room is nu semie annoniem"
  17935. ],
  17936. "This room is now fully-anonymous": [
  17937. null,
  17938. "Deze room is nu volledig annoniem"
  17939. ],
  17940. "A new room has been created": [
  17941. null,
  17942. "Een nieuwe room is gemaakt"
  17943. ],
  17944. "Your nickname has been changed": [
  17945. null,
  17946. "Je nickname is veranderd"
  17947. ],
  17948. "<strong>%1$s</strong> has been banned": [
  17949. null,
  17950. "<strong>%1$s</strong> is verbannen"
  17951. ],
  17952. "<strong>%1$s</strong> has been kicked out": [
  17953. null,
  17954. "<strong>%1$s</strong> has been kicked out"
  17955. ],
  17956. "<strong>%1$s</strong> has been removed because of an affiliation change": [
  17957. null,
  17958. ""
  17959. ],
  17960. "<strong>%1$s</strong> has been removed for not being a member": [
  17961. null,
  17962. ""
  17963. ],
  17964. "You have been banned from this room": [
  17965. null,
  17966. "Je bent verbannen uit deze room"
  17967. ],
  17968. "You have been kicked from this room": [
  17969. null,
  17970. "Je bent uit de room gegooid"
  17971. ],
  17972. "You have been removed from this room because of an affiliation change": [
  17973. null,
  17974. ""
  17975. ],
  17976. "You have been removed from this room because the room has changed to members-only and you're not a member": [
  17977. null,
  17978. ""
  17979. ],
  17980. "You have been removed from this room because the MUC (Multi-user chat) service is being shut down.": [
  17981. null,
  17982. ""
  17983. ],
  17984. "You are not on the member list of this room": [
  17985. null,
  17986. "Je bent niet een gebruiker van deze room"
  17987. ],
  17988. "No nickname was specified": [
  17989. null,
  17990. "Geen nickname ingegeven"
  17991. ],
  17992. "You are not allowed to create new rooms": [
  17993. null,
  17994. "Je bent niet toegestaan nieuwe rooms te maken"
  17995. ],
  17996. "Your nickname doesn't conform to this room's policies": [
  17997. null,
  17998. "Je nickname is niet conform policy"
  17999. ],
  18000. "Your nickname is already taken": [
  18001. null,
  18002. "Je nickname bestaat al"
  18003. ],
  18004. "This room does not (yet) exist": [
  18005. null,
  18006. "Deze room bestaat niet"
  18007. ],
  18008. "This room has reached it's maximum number of occupants": [
  18009. null,
  18010. "Deze room heeft het maximale aantal gebruikers"
  18011. ],
  18012. "Topic set by %1$s to: %2$s": [
  18013. null,
  18014. ""
  18015. ],
  18016. "This user is a moderator": [
  18017. null,
  18018. "Dit is een moderator"
  18019. ],
  18020. "This user can send messages in this room": [
  18021. null,
  18022. "Deze gebruiker kan berichten sturen in deze room"
  18023. ],
  18024. "This user can NOT send messages in this room": [
  18025. null,
  18026. "Deze gebruiker kan NIET een bericht sturen in deze room"
  18027. ],
  18028. "Click to chat with this contact": [
  18029. null,
  18030. "Klik om te chatten met contact"
  18031. ],
  18032. "Click to remove this contact": [
  18033. null,
  18034. "Klik om contact te verwijderen"
  18035. ],
  18036. "This contact is busy": [
  18037. null,
  18038. "Contact is bezet"
  18039. ],
  18040. "This contact is online": [
  18041. null,
  18042. "Contact is online"
  18043. ],
  18044. "This contact is offline": [
  18045. null,
  18046. "Contact is offline"
  18047. ],
  18048. "This contact is unavailable": [
  18049. null,
  18050. "Contact is niet beschikbaar"
  18051. ],
  18052. "This contact is away for an extended period": [
  18053. null,
  18054. "Contact is afwezig voor lange periode"
  18055. ],
  18056. "This contact is away": [
  18057. null,
  18058. "Conact is afwezig"
  18059. ],
  18060. "Contact requests": [
  18061. null,
  18062. "Contact uitnodiging"
  18063. ],
  18064. "My contacts": [
  18065. null,
  18066. "Mijn contacts"
  18067. ],
  18068. "Pending contacts": [
  18069. null,
  18070. "Conacten in afwachting van"
  18071. ],
  18072. "Custom status": [
  18073. null,
  18074. ""
  18075. ],
  18076. "Click to change your chat status": [
  18077. null,
  18078. "Klik hier om status te wijzigen"
  18079. ],
  18080. "Click here to write a custom status message": [
  18081. null,
  18082. "Klik hier om custom status bericht te maken"
  18083. ],
  18084. "online": [
  18085. null,
  18086. "online"
  18087. ],
  18088. "busy": [
  18089. null,
  18090. "bezet"
  18091. ],
  18092. "away for long": [
  18093. null,
  18094. "afwezig lange tijd"
  18095. ],
  18096. "away": [
  18097. null,
  18098. "afwezig"
  18099. ],
  18100. "I am %1$s": [
  18101. null,
  18102. "Ik ben %1$s"
  18103. ],
  18104. "Sign in": [
  18105. null,
  18106. "Aanmelden"
  18107. ],
  18108. "XMPP/Jabber Username:": [
  18109. null,
  18110. "XMPP/Jabber Username:"
  18111. ],
  18112. "Password:": [
  18113. null,
  18114. "Wachtwoord:"
  18115. ],
  18116. "Log In": [
  18117. null,
  18118. "Aanmelden"
  18119. ],
  18120. "BOSH Service URL:": [
  18121. null,
  18122. ""
  18123. ],
  18124. "Online Contacts": [
  18125. null,
  18126. "Online Contacten"
  18127. ],
  18128. "%1$s is typing": [
  18129. null,
  18130. "%1$s is aan typen"
  18131. ],
  18132. "Connected": [
  18133. null,
  18134. "Verbonden"
  18135. ],
  18136. "Attached": [
  18137. null,
  18138. "Bijlage"
  18139. ]
  18140. }
  18141. }
  18142. };
  18143. if (typeof define === 'function' && define.amd) {
  18144. define("nl", ['jed'], function () {
  18145. return factory(new Jed(translations));
  18146. });
  18147. } else {
  18148. if (!window.locales) {
  18149. window.locales = {};
  18150. }
  18151. window.locales.nl = factory(new Jed(translations));
  18152. }
  18153. }(this, function (nl) {
  18154. return nl;
  18155. }));
  18156. (function (root, factory) {
  18157. var translations = {
  18158. "domain": "converse",
  18159. "locale_data": {
  18160. "converse": {
  18161. "": {
  18162. "Project-Id-Version": "Converse.js 0.6.3",
  18163. "Report-Msgid-Bugs-To": "",
  18164. "POT-Creation-Date": "2013-09-15 21:55+0200",
  18165. "PO-Revision-Date": "2014-07-07 11:02+0200",
  18166. "Last-Translator": "Alan Meira <alan@engarte.com>",
  18167. "Language-Team": "Brazilian Portuguese",
  18168. "Language": "pt_BR",
  18169. "MIME-Version": "1.0",
  18170. "Content-Type": "text/plain; charset=UTF-8",
  18171. "Content-Transfer-Encoding": "8bit",
  18172. "Plural-Forms": "nplurals=2; plural=(n > 1);",
  18173. "domain": "converse",
  18174. "lang": "pt_BR",
  18175. "plural_forms": "nplurals=2; plural=(n != 1);"
  18176. },
  18177. "unencrypted": [
  18178. null,
  18179. "não-criptografado"
  18180. ],
  18181. "unverified": [
  18182. null,
  18183. "não-verificado"
  18184. ],
  18185. "verified": [
  18186. null,
  18187. "não-verificado"
  18188. ],
  18189. "finished": [
  18190. null,
  18191. "finalizado"
  18192. ],
  18193. "Disconnected": [
  18194. null,
  18195. "Desconectado"
  18196. ],
  18197. "Error": [
  18198. null,
  18199. "Erro"
  18200. ],
  18201. "Connecting": [
  18202. null,
  18203. "Conectando"
  18204. ],
  18205. "Connection Failed": [
  18206. null,
  18207. "Falha de conexão"
  18208. ],
  18209. "Authenticating": [
  18210. null,
  18211. "Autenticando"
  18212. ],
  18213. "Authentication Failed": [
  18214. null,
  18215. "Falha de autenticação"
  18216. ],
  18217. "Disconnecting": [
  18218. null,
  18219. "Desconectando"
  18220. ],
  18221. "Re-establishing encrypted session": [
  18222. null,
  18223. "Reestabelecendo sessão criptografada"
  18224. ],
  18225. "Your browser needs to generate a private key, which will be used in your encrypted chat session. This can take up to 30 seconds during which your browser might freeze and become unresponsive.": [
  18226. null,
  18227. "Seu navegador precisa gerar uma chave-privada, que será usada em sua sessão criptografada de bate-papo. Isso pode levar até 30 segundos durante os quais seu navegador poderá se travar ou não responder."
  18228. ],
  18229. "Private key generated.": [
  18230. null,
  18231. "Chave-privada gerada:"
  18232. ],
  18233. "Authentication request from %1$s\n\nYour buddy is attempting to verify your identity, by asking you the question below.\n\n%2$s": [
  18234. null,
  18235. "Pedido de autenticação de %$s\n\nSeu contato está tentando verificar sua identidade, perguntando a questão abaixo.\n\n%2$s"
  18236. ],
  18237. "Could not verify this user's identify.": [
  18238. null,
  18239. "Não foi possível verificar a identidade deste usuário."
  18240. ],
  18241. "Personal message": [
  18242. null,
  18243. "Mensagem pessoal"
  18244. ],
  18245. "Start encrypted conversation": [
  18246. null,
  18247. "Iniciar conversa criptografada"
  18248. ],
  18249. "Refresh encrypted conversation": [
  18250. null,
  18251. "Atualizar conversa criptografada"
  18252. ],
  18253. "End encrypted conversation": [
  18254. null,
  18255. "Finalizar conversa criptografada"
  18256. ],
  18257. "Verify with SMP": [
  18258. null,
  18259. "Verificar com SMP"
  18260. ],
  18261. "Verify with fingerprints": [
  18262. null,
  18263. "Verificar com assinatura digital"
  18264. ],
  18265. "What's this?": [
  18266. null,
  18267. "O que é isso?"
  18268. ],
  18269. "me": [
  18270. null,
  18271. "eu"
  18272. ],
  18273. "Show this menu": [
  18274. null,
  18275. "Mostrar o menu"
  18276. ],
  18277. "Write in the third person": [
  18278. null,
  18279. "Escrever em terceira pessoa"
  18280. ],
  18281. "Remove messages": [
  18282. null,
  18283. "Remover mensagens"
  18284. ],
  18285. "Your message could not be sent": [
  18286. null,
  18287. "Sua mensagem não pôde ser enviada"
  18288. ],
  18289. "We received an unencrypted message": [
  18290. null,
  18291. "Recebemos uma mensagem não-criptografada"
  18292. ],
  18293. "We received an unreadable encrypted message": [
  18294. null,
  18295. "Recebemos uma mensagem não-criptografada ilegível"
  18296. ],
  18297. "This user has requested an encrypted session.": [
  18298. null,
  18299. "Usuário pediu uma sessão criptografada."
  18300. ],
  18301. "Here are the fingerprints, please confirm them with %1$s, outside of this chat.\n\nFingerprint for you, %2$s: %3$s\n\nFingerprint for %1$s: %4$s\n\nIf you have confirmed that the fingerprints match, click OK, otherwise click Cancel.": [
  18302. null,
  18303. "Aqui estão as assinaturas digitais, por favor confirme elas com %1$s, fora deste chat.\n\nAssinaturas para você, %2$s: %3$s\n\nAssinaturas para %1$s: %4$s\n\nSe você tiver confirmado que as assinaturas conferem, clique OK, caso contrário, clique Cancel."
  18304. ],
  18305. "You will be prompted to provide a security question and then an answer to that question.\n\nYour buddy will then be prompted the same question and if they type the exact same answer (case sensitive), their identity will have been verified.": [
  18306. null,
  18307. "Será solicitado que você informe uma pergunta de segurança e também uma resposta.\n\nNós iremos, então, transfeir a pergunta para seu contato e caso ele envie corretamente a mesma resposta (caso sensitivo), a identidade dele será verificada."
  18308. ],
  18309. "What is your security question?": [
  18310. null,
  18311. "Qual é a sua pergunta de segurança?"
  18312. ],
  18313. "What is the answer to the security question?": [
  18314. null,
  18315. "Qual é a resposta para a pergunta de segurança?"
  18316. ],
  18317. "Invalid authentication scheme provided": [
  18318. null,
  18319. "Schema de autenticação fornecido é inválido"
  18320. ],
  18321. "Your messages are not encrypted anymore": [
  18322. null,
  18323. "Suas mensagens não estão mais criptografadas"
  18324. ],
  18325. "Your messages are now encrypted but your buddy's identity has not been verified.": [
  18326. null,
  18327. "Suas mensagens estão agora criptografadas mas a identidade do contato não foi confirmada."
  18328. ],
  18329. "Your buddy's identify has been verified.": [
  18330. null,
  18331. "A identidade do contato foi verificada."
  18332. ],
  18333. "Your buddy has ended encryption on their end, you should do the same.": [
  18334. null,
  18335. "Seu contato parou de usar criptografia, você deveria fazer o mesmo."
  18336. ],
  18337. "Your messages are not encrypted. Click here to enable OTR encryption.": [
  18338. null,
  18339. "Suas mensagens não estão criptografadas. Clique aqui para habilitar criptografia OTR."
  18340. ],
  18341. "Your messages are encrypted, but your buddy has not been verified.": [
  18342. null,
  18343. "Suas mensagens estão criptografadas, mas seu contato não foi verificado."
  18344. ],
  18345. "Your messages are encrypted and your buddy verified.": [
  18346. null,
  18347. "Suas mensagens estão criptografadas e seu contato verificado."
  18348. ],
  18349. "Your buddy has closed their end of the private session, you should do the same": [
  18350. null,
  18351. "Seu contato fechou a sessão privada, você deveria fazer o mesmo"
  18352. ],
  18353. "Contacts": [
  18354. null,
  18355. "Contatos"
  18356. ],
  18357. "Online": [
  18358. null,
  18359. "Online"
  18360. ],
  18361. "Busy": [
  18362. null,
  18363. "Ocupado"
  18364. ],
  18365. "Away": [
  18366. null,
  18367. "Ausente"
  18368. ],
  18369. "Offline": [
  18370. null,
  18371. "Offline"
  18372. ],
  18373. "Click to add new chat contacts": [
  18374. null,
  18375. "Clique para adicionar novos contatos ao chat"
  18376. ],
  18377. "Add a contact": [
  18378. null,
  18379. "Adicionar contato"
  18380. ],
  18381. "Contact username": [
  18382. null,
  18383. "Usuário do contatt"
  18384. ],
  18385. "Add": [
  18386. null,
  18387. "Adicionar"
  18388. ],
  18389. "Contact name": [
  18390. null,
  18391. "Nome do contato"
  18392. ],
  18393. "Search": [
  18394. null,
  18395. "Procurar"
  18396. ],
  18397. "No users found": [
  18398. null,
  18399. "Não foram encontrados usuários"
  18400. ],
  18401. "Click to add as a chat contact": [
  18402. null,
  18403. "Clique para adicionar como um contato do chat"
  18404. ],
  18405. "Click to open this room": [
  18406. null,
  18407. "CLique para abrir a sala"
  18408. ],
  18409. "Show more information on this room": [
  18410. null,
  18411. "Mostrar mais informações nessa sala"
  18412. ],
  18413. "Description:": [
  18414. null,
  18415. "Descrição:"
  18416. ],
  18417. "Occupants:": [
  18418. null,
  18419. "Ocupantes:"
  18420. ],
  18421. "Features:": [
  18422. null,
  18423. "Recursos:"
  18424. ],
  18425. "Requires authentication": [
  18426. null,
  18427. "Requer autenticação"
  18428. ],
  18429. "Hidden": [
  18430. null,
  18431. "Escondido"
  18432. ],
  18433. "Requires an invitation": [
  18434. null,
  18435. "Requer um convite"
  18436. ],
  18437. "Moderated": [
  18438. null,
  18439. "Moderado"
  18440. ],
  18441. "Non-anonymous": [
  18442. null,
  18443. "Não anônimo"
  18444. ],
  18445. "Open room": [
  18446. null,
  18447. "Sala aberta"
  18448. ],
  18449. "Permanent room": [
  18450. null,
  18451. "Sala permanente"
  18452. ],
  18453. "Public": [
  18454. null,
  18455. "Público"
  18456. ],
  18457. "Semi-anonymous": [
  18458. null,
  18459. "Semi anônimo"
  18460. ],
  18461. "Temporary room": [
  18462. null,
  18463. "Sala temporária"
  18464. ],
  18465. "Unmoderated": [
  18466. null,
  18467. "Sem moderação"
  18468. ],
  18469. "Rooms": [
  18470. null,
  18471. "Salas"
  18472. ],
  18473. "Room name": [
  18474. null,
  18475. "Nome da sala"
  18476. ],
  18477. "Nickname": [
  18478. null,
  18479. "Apelido"
  18480. ],
  18481. "Server": [
  18482. null,
  18483. "Server"
  18484. ],
  18485. "Join": [
  18486. null,
  18487. "Entrar"
  18488. ],
  18489. "Show rooms": [
  18490. null,
  18491. "Mostar salas"
  18492. ],
  18493. "No rooms on %1$s": [
  18494. null,
  18495. "Sem salas em %1$s"
  18496. ],
  18497. "Rooms on %1$s": [
  18498. null,
  18499. "Salas em %1$s"
  18500. ],
  18501. "Set chatroom topic": [
  18502. null,
  18503. "Definir tópico do chat"
  18504. ],
  18505. "Kick user from chatroom": [
  18506. null,
  18507. "Expulsar usuário do chat"
  18508. ],
  18509. "Ban user from chatroom": [
  18510. null,
  18511. "Banir usuário do chat"
  18512. ],
  18513. "Message": [
  18514. null,
  18515. "Mensagem"
  18516. ],
  18517. "Save": [
  18518. null,
  18519. "Salvar"
  18520. ],
  18521. "Cancel": [
  18522. null,
  18523. "Cancelar"
  18524. ],
  18525. "An error occurred while trying to save the form.": [
  18526. null,
  18527. "Ocorreu um erro enquanto tentava salvar o formulário"
  18528. ],
  18529. "This chatroom requires a password": [
  18530. null,
  18531. "Esse chat precisa de senha"
  18532. ],
  18533. "Password: ": [
  18534. null,
  18535. "Senha: "
  18536. ],
  18537. "Submit": [
  18538. null,
  18539. "Enviar"
  18540. ],
  18541. "This room is not anonymous": [
  18542. null,
  18543. "Essa sala não é anônima"
  18544. ],
  18545. "This room now shows unavailable members": [
  18546. null,
  18547. "Agora esta sala mostra membros indisponíveis"
  18548. ],
  18549. "This room does not show unavailable members": [
  18550. null,
  18551. "Essa sala não mostra membros indisponíveis"
  18552. ],
  18553. "Non-privacy-related room configuration has changed": [
  18554. null,
  18555. "Configuraçõs não relacionadas à privacidade mudaram"
  18556. ],
  18557. "Room logging is now enabled": [
  18558. null,
  18559. "O log da sala está ativado"
  18560. ],
  18561. "Room logging is now disabled": [
  18562. null,
  18563. "O log da sala está desativado"
  18564. ],
  18565. "This room is now non-anonymous": [
  18566. null,
  18567. "Esse sala é não anônima"
  18568. ],
  18569. "This room is now semi-anonymous": [
  18570. null,
  18571. "Essa sala agora é semi anônima"
  18572. ],
  18573. "This room is now fully-anonymous": [
  18574. null,
  18575. "Essa sala agora é totalmente anônima"
  18576. ],
  18577. "A new room has been created": [
  18578. null,
  18579. "Uma nova sala foi criada"
  18580. ],
  18581. "Your nickname has been changed": [
  18582. null,
  18583. "Seu apelido foi mudado"
  18584. ],
  18585. "<strong>%1$s</strong> has been banned": [
  18586. null,
  18587. "<strong>%1$s</strong> foi banido"
  18588. ],
  18589. "<strong>%1$s</strong> has been kicked out": [
  18590. null,
  18591. "<strong>%1$s</strong> foi expulso"
  18592. ],
  18593. "<strong>%1$s</strong> has been removed because of an affiliation change": [
  18594. null,
  18595. "<srtong>%1$s</strong> foi removido por causa de troca de associação"
  18596. ],
  18597. "<strong>%1$s</strong> has been removed for not being a member": [
  18598. null,
  18599. "<strong>%1$s</strong> foi removido por não ser um membro"
  18600. ],
  18601. "You have been banned from this room": [
  18602. null,
  18603. "Você foi banido dessa sala"
  18604. ],
  18605. "You have been kicked from this room": [
  18606. null,
  18607. "Você foi expulso dessa sala"
  18608. ],
  18609. "You have been removed from this room because of an affiliation change": [
  18610. null,
  18611. "Você foi removido da sala devido a uma mudança de associação"
  18612. ],
  18613. "You have been removed from this room because the room has changed to members-only and you're not a member": [
  18614. null,
  18615. "Você foi removido da sala porque ela foi mudada para somente membrose você não é um membro"
  18616. ],
  18617. "You have been removed from this room because the MUC (Multi-user chat) service is being shut down.": [
  18618. null,
  18619. "Você foi removido da sala devido a MUC (Multi-user chat)o serviço está sendo desligado"
  18620. ],
  18621. "You are not on the member list of this room": [
  18622. null,
  18623. "Você não é membro dessa sala"
  18624. ],
  18625. "No nickname was specified": [
  18626. null,
  18627. "Você não escolheu um apelido "
  18628. ],
  18629. "You are not allowed to create new rooms": [
  18630. null,
  18631. "Você não tem permitição de criar novas salas"
  18632. ],
  18633. "Your nickname doesn't conform to this room's policies": [
  18634. null,
  18635. "Seu apelido não está de acordo com as regras da sala"
  18636. ],
  18637. "Your nickname is already taken": [
  18638. null,
  18639. "Seu apelido já foi escolhido"
  18640. ],
  18641. "This room does not (yet) exist": [
  18642. null,
  18643. "A sala não existe (ainda)"
  18644. ],
  18645. "This room has reached it's maximum number of occupants": [
  18646. null,
  18647. "A sala atingiu o número máximo de ocupantes"
  18648. ],
  18649. "Topic set by %1$s to: %2$s": [
  18650. null,
  18651. "Topico definido por %1$s para: %2$s"
  18652. ],
  18653. "This user is a moderator": [
  18654. null,
  18655. "Esse usuário é o moderador"
  18656. ],
  18657. "This user can send messages in this room": [
  18658. null,
  18659. "Esse usuário pode enviar mensagens nessa sala"
  18660. ],
  18661. "This user can NOT send messages in this room": [
  18662. null,
  18663. "Esse usuário NÃO pode enviar mensagens nessa sala"
  18664. ],
  18665. "Click to chat with this contact": [
  18666. null,
  18667. "Clique para conversar com o contato"
  18668. ],
  18669. "Click to remove this contact": [
  18670. null,
  18671. "Clique para remover o contato"
  18672. ],
  18673. "This contact is busy": [
  18674. null,
  18675. "Este contato está ocupado"
  18676. ],
  18677. "This contact is online": [
  18678. null,
  18679. "Este contato está online"
  18680. ],
  18681. "This contact is offline": [
  18682. null,
  18683. "Este contato está offline"
  18684. ],
  18685. "This contact is unavailable": [
  18686. null,
  18687. "Este contato está indisponível"
  18688. ],
  18689. "This contact is away for an extended period": [
  18690. null,
  18691. "Este contato está ausente por um longo período"
  18692. ],
  18693. "This contact is away": [
  18694. null,
  18695. "Este contato está ausente"
  18696. ],
  18697. "Contact requests": [
  18698. null,
  18699. "Solicitação de contatos"
  18700. ],
  18701. "My contacts": [
  18702. null,
  18703. "Meus contatos"
  18704. ],
  18705. "Pending contacts": [
  18706. null,
  18707. "Contados pendentes"
  18708. ],
  18709. "Custom status": [
  18710. null,
  18711. "Status customizado"
  18712. ],
  18713. "Click to change your chat status": [
  18714. null,
  18715. "Clique para mudar seu status no chat"
  18716. ],
  18717. "Click here to write a custom status message": [
  18718. null,
  18719. "Clique aqui para customizar a mensagem de status"
  18720. ],
  18721. "online": [
  18722. null,
  18723. "online"
  18724. ],
  18725. "busy": [
  18726. null,
  18727. "ocupado"
  18728. ],
  18729. "away for long": [
  18730. null,
  18731. "ausente a bastante tempo"
  18732. ],
  18733. "away": [
  18734. null,
  18735. "ausente"
  18736. ],
  18737. "I am %1$s": [
  18738. null,
  18739. "Estou %1$s"
  18740. ],
  18741. "Sign in": [
  18742. null,
  18743. "Conectar-se"
  18744. ],
  18745. "XMPP/Jabber Username:": [
  18746. null,
  18747. "Usuário XMPP/Jabber:"
  18748. ],
  18749. "Password:": [
  18750. null,
  18751. "Senha:"
  18752. ],
  18753. "Log In": [
  18754. null,
  18755. "Entrar"
  18756. ],
  18757. "BOSH Service URL:": [
  18758. null,
  18759. "URL de serviço BOSH:"
  18760. ],
  18761. "Online Contacts": [
  18762. null,
  18763. "Contatos online"
  18764. ],
  18765. "%1$s is typing": [
  18766. null,
  18767. "%1$s está digitando"
  18768. ],
  18769. "Connected": [
  18770. null,
  18771. "Conectado"
  18772. ],
  18773. "Attached": [
  18774. null,
  18775. "Anexado"
  18776. ]
  18777. }
  18778. }
  18779. };
  18780. if (typeof define === 'function' && define.amd) {
  18781. define("pt_BR", ['jed'], function () {
  18782. return factory(new Jed(translations));
  18783. });
  18784. } else {
  18785. if (!window.locales) {
  18786. window.locales = {};
  18787. }
  18788. window.locales.pt_BR = factory(new Jed(translations));
  18789. }
  18790. }(this, function (i18n) {
  18791. return i18n;
  18792. })
  18793. );
  18794. (function (root, factory) {
  18795. var translations = {
  18796. "domain": "converse",
  18797. "locale_data":
  18798. {
  18799. "converse": {
  18800. "": {
  18801. "Project-Id-Version": "Converse.js 0.4",
  18802. "Report-Msgid-Bugs-To": "",
  18803. "POT-Creation-Date": "2013-09-15 22:06+0200",
  18804. "PO-Revision-Date": "2013-09-29 17:24+0300",
  18805. "Last-Translator": "Boris Kocherov <bk@raskon.org>",
  18806. "Language-Team": "<bk@raskon.ru>",
  18807. "Language": "ru",
  18808. "MIME-Version": "1.0",
  18809. "Content-Type": "text/plain; charset=UTF-8",
  18810. "Content-Transfer-Encoding": "8bit",
  18811. "X-Generator": "Poedit 1.5.5"
  18812. },
  18813. "unencrypted": [
  18814. null,
  18815. "не зашифровано"
  18816. ],
  18817. "unverified": [
  18818. null,
  18819. "непроверено"
  18820. ],
  18821. "verified": [
  18822. null,
  18823. "проверено"
  18824. ],
  18825. "finished": [
  18826. null,
  18827. "закончено"
  18828. ],
  18829. "Disconnected": [
  18830. null,
  18831. "Отключено"
  18832. ],
  18833. "Error": [
  18834. null,
  18835. "Ошибка"
  18836. ],
  18837. "Connecting": [
  18838. null,
  18839. "Соединение"
  18840. ],
  18841. "Connection Failed": [
  18842. null,
  18843. "Не удалось соединится"
  18844. ],
  18845. "Authenticating": [
  18846. null,
  18847. "Авторизация"
  18848. ],
  18849. "Authentication Failed": [
  18850. null,
  18851. "Не удалось авторизоваться"
  18852. ],
  18853. "Disconnecting": [
  18854. null,
  18855. "Отключаемся"
  18856. ],
  18857. "Private key generated.": [
  18858. null,
  18859. "Приватный ключ сгенерирован."
  18860. ],
  18861. "Personal message": [
  18862. null,
  18863. "Введите сообщение"
  18864. ],
  18865. "What's this?": [
  18866. null,
  18867. "Что это?"
  18868. ],
  18869. "me": [
  18870. null,
  18871. "Я"
  18872. ],
  18873. "Show this menu": [
  18874. null,
  18875. "Показать это меню"
  18876. ],
  18877. "Remove messages": [
  18878. null,
  18879. "Удалить сообщения"
  18880. ],
  18881. "Your message could not be sent": [
  18882. null,
  18883. "Ваше сообщение не послано"
  18884. ],
  18885. "Your messages are not encrypted anymore": [
  18886. null,
  18887. "Ваши сообщения больше не шифруются"
  18888. ],
  18889. "Your messages are now encrypted but your buddy's identity has not been verified.": [
  18890. null,
  18891. "Ваши сообщения шифруются, но ваша учётная запись не проверена вашим собеседником."
  18892. ],
  18893. "Your buddy's identify has been verified.": [
  18894. null,
  18895. "Ваша учётная запись проверена вашим собеседником."
  18896. ],
  18897. "Your messages are not encrypted. Click here to enable OTR encryption.": [
  18898. null,
  18899. "Ваши сообщения не шифруются. Нажмите здесь чтобы настроить шифрование."
  18900. ],
  18901. "Your messages are encrypted, but your buddy has not been verified.": [
  18902. null,
  18903. "Ваши сообщения шифруются, но ваш контакт не проверен."
  18904. ],
  18905. "Your messages are encrypted and your buddy verified.": [
  18906. null,
  18907. "Ваши сообщения шифруются и ваш контакт проверен"
  18908. ],
  18909. "Contacts": [
  18910. null,
  18911. "Контакты"
  18912. ],
  18913. "Online": [
  18914. null,
  18915. "В сети"
  18916. ],
  18917. "Busy": [
  18918. null,
  18919. "Занят"
  18920. ],
  18921. "Away": [
  18922. null,
  18923. "Отошёл"
  18924. ],
  18925. "Offline": [
  18926. null,
  18927. "Не в сети"
  18928. ],
  18929. "Click to add new chat contacts": [
  18930. null,
  18931. "Добавить новую конференцию"
  18932. ],
  18933. "Add a contact": [
  18934. null,
  18935. "Добавть контакт"
  18936. ],
  18937. "Contact username": [
  18938. null,
  18939. "Имя пользователя"
  18940. ],
  18941. "Add": [
  18942. null,
  18943. "Добавить"
  18944. ],
  18945. "Contact name": [
  18946. null,
  18947. "Имя контакта"
  18948. ],
  18949. "Search": [
  18950. null,
  18951. "Поиск"
  18952. ],
  18953. "No users found": [
  18954. null,
  18955. "Пользователи не найдены"
  18956. ],
  18957. "Click to add as a chat contact": [
  18958. null,
  18959. "Добавить контакт"
  18960. ],
  18961. "Click to open this room": [
  18962. null,
  18963. "Зайти в конференцию"
  18964. ],
  18965. "Show more information on this room": [
  18966. null,
  18967. "Показать больше информации об этой конференции"
  18968. ],
  18969. "Description:": [
  18970. null,
  18971. "Описание:"
  18972. ],
  18973. "Occupants:": [
  18974. null,
  18975. "Участники:"
  18976. ],
  18977. "Features:": [
  18978. null,
  18979. "Свойства:"
  18980. ],
  18981. "Requires authentication": [
  18982. null,
  18983. "Требуется авторизация"
  18984. ],
  18985. "Hidden": [
  18986. null,
  18987. "Скрыто"
  18988. ],
  18989. "Requires an invitation": [
  18990. null,
  18991. "Требуется приглашение"
  18992. ],
  18993. "Moderated": [
  18994. null,
  18995. "Модерируемая"
  18996. ],
  18997. "Non-anonymous": [
  18998. null,
  18999. "Не анонимная"
  19000. ],
  19001. "Open room": [
  19002. null,
  19003. "Открыть конференцию"
  19004. ],
  19005. "Permanent room": [
  19006. null,
  19007. "Перманентная конференция"
  19008. ],
  19009. "Public": [
  19010. null,
  19011. "Публичный"
  19012. ],
  19013. "Semi-anonymous": [
  19014. null,
  19015. "Частично анонимная"
  19016. ],
  19017. "Temporary room": [
  19018. null,
  19019. "Временная конференция"
  19020. ],
  19021. "Unmoderated": [
  19022. null,
  19023. "Немодерируемая"
  19024. ],
  19025. "Rooms": [
  19026. null,
  19027. "Конфер."
  19028. ],
  19029. "Room name": [
  19030. null,
  19031. "Имя конференции"
  19032. ],
  19033. "Nickname": [
  19034. null,
  19035. "Псевдоним"
  19036. ],
  19037. "Server": [
  19038. null,
  19039. "Сервер"
  19040. ],
  19041. "Join": [
  19042. null,
  19043. "Подключиться"
  19044. ],
  19045. "Show rooms": [
  19046. null,
  19047. "Обновить"
  19048. ],
  19049. "No rooms on %1$s": [
  19050. null,
  19051. "Нет доступных конференций %1$s"
  19052. ],
  19053. "Rooms on %1$s": [
  19054. null,
  19055. "Конференции %1$s:"
  19056. ],
  19057. "Set chatroom topic": [
  19058. null,
  19059. "Установить тему"
  19060. ],
  19061. "Kick user from chatroom": [
  19062. null,
  19063. "Отключить пользователя от кнофер."
  19064. ],
  19065. "Ban user from chatroom": [
  19066. null,
  19067. "Забанить пользователя в этой конф."
  19068. ],
  19069. "Message": [
  19070. null,
  19071. "Сообщение"
  19072. ],
  19073. "Save": [
  19074. null,
  19075. "Сохранить"
  19076. ],
  19077. "Cancel": [
  19078. null,
  19079. "Отменить"
  19080. ],
  19081. "An error occurred while trying to save the form.": [
  19082. null,
  19083. "При сохранение формы произошла ошибка."
  19084. ],
  19085. "This chatroom requires a password": [
  19086. null,
  19087. "Для доступа в конфер. необходим пароль."
  19088. ],
  19089. "Password: ": [
  19090. null,
  19091. "Пароль: "
  19092. ],
  19093. "Submit": [
  19094. null,
  19095. "Отправить"
  19096. ],
  19097. "This room is not anonymous": [
  19098. null,
  19099. "Эта комната не анонимная"
  19100. ],
  19101. "This room now shows unavailable members": [
  19102. null,
  19103. "Эта комната показывает доступных собеседников"
  19104. ],
  19105. "This room does not show unavailable members": [
  19106. null,
  19107. "Эта комната не показывает недоступных собеседников"
  19108. ],
  19109. "This room is now non-anonymous": [
  19110. null,
  19111. "Эта комната не анонимная"
  19112. ],
  19113. "This room is now semi-anonymous": [
  19114. null,
  19115. "Эта комната частично анонимная"
  19116. ],
  19117. "This room is now fully-anonymous": [
  19118. null,
  19119. "Эта комната стала полностью анонимной"
  19120. ],
  19121. "A new room has been created": [
  19122. null,
  19123. "Новая комната была создана"
  19124. ],
  19125. "Your nickname has been changed": [
  19126. null,
  19127. "Ваш псевдоним уже используется другим пользователем"
  19128. ],
  19129. "<strong>%1$s</strong> has been banned": [
  19130. null,
  19131. "<strong>%1$s</strong> забанен"
  19132. ],
  19133. "<strong>%1$s</strong> has been kicked out": [
  19134. null,
  19135. "<strong>%1$s</strong> выдворен"
  19136. ],
  19137. "<strong>%1$s</strong> has been removed because of an affiliation change": [
  19138. null,
  19139. "<strong>%1$s</strong> has been removed because of an affiliation change"
  19140. ],
  19141. "<strong>%1$s</strong> has been removed for not being a member": [
  19142. null,
  19143. "<strong>%1$s</strong> удалён потому что не участник"
  19144. ],
  19145. "You have been banned from this room": [
  19146. null,
  19147. "Вам запрещено подключатся к этой конференции"
  19148. ],
  19149. "You have been kicked from this room": [
  19150. null,
  19151. "Вам запрещено подключатся к этой конференции"
  19152. ],
  19153. "You have been removed from this room because of an affiliation change": [
  19154. null,
  19155. "<strong>%1$s</strong> удалён потому что изменились права"
  19156. ],
  19157. "You have been removed from this room because the room has changed to members-only and you're not a member": [
  19158. null,
  19159. "Вы отключены от этой конференции потому что режим изменился: только-участники"
  19160. ],
  19161. "You have been removed from this room because the MUC (Multi-user chat) service is being shut down.": [
  19162. null,
  19163. "Вы отключены от этой конференции потому что сервись конференций выключен."
  19164. ],
  19165. "You are not on the member list of this room": [
  19166. null,
  19167. "Вас нет в списке этой конференции"
  19168. ],
  19169. "No nickname was specified": [
  19170. null,
  19171. "Вы не указали псевдоним"
  19172. ],
  19173. "You are not allowed to create new rooms": [
  19174. null,
  19175. "Вы не имеете права создавать конфер."
  19176. ],
  19177. "Your nickname doesn't conform to this room's policies": [
  19178. null,
  19179. "Псевдоним не согласуется с правилами конфер."
  19180. ],
  19181. "Your nickname is already taken": [
  19182. null,
  19183. "Ваш ник уже используется другим пользователем"
  19184. ],
  19185. "This room does not (yet) exist": [
  19186. null,
  19187. "Эта комната не существует"
  19188. ],
  19189. "This room has reached it's maximum number of occupants": [
  19190. null,
  19191. "Конференция достигла максимального количества участников"
  19192. ],
  19193. "Topic set by %1$s to: %2$s": [
  19194. null,
  19195. "Тема %2$s устатновлена %1$s"
  19196. ],
  19197. "This user is a moderator": [
  19198. null,
  19199. "Модератор"
  19200. ],
  19201. "This user can send messages in this room": [
  19202. null,
  19203. "Собеседник"
  19204. ],
  19205. "This user can NOT send messages in this room": [
  19206. null,
  19207. "Пользователь не может посылать сообщения в эту комнату"
  19208. ],
  19209. "Click to chat with this contact": [
  19210. null,
  19211. "Начать общение"
  19212. ],
  19213. "Click to remove this contact": [
  19214. null,
  19215. "Удалить контакт"
  19216. ],
  19217. "This contact is busy": [
  19218. null,
  19219. "Занят"
  19220. ],
  19221. "This contact is online": [
  19222. null,
  19223. "В сети"
  19224. ],
  19225. "This contact is offline": [
  19226. null,
  19227. "Не в сети"
  19228. ],
  19229. "This contact is unavailable": [
  19230. null,
  19231. "Не доступен"
  19232. ],
  19233. "This contact is away for an extended period": [
  19234. null,
  19235. "На долго отошёл"
  19236. ],
  19237. "This contact is away": [
  19238. null,
  19239. "Отошёл"
  19240. ],
  19241. "Contact requests": [
  19242. null,
  19243. "Запросы на авторизацию"
  19244. ],
  19245. "My contacts": [
  19246. null,
  19247. "Контакты"
  19248. ],
  19249. "Pending contacts": [
  19250. null,
  19251. "Собеседники ожидающие авторизации"
  19252. ],
  19253. "Custom status": [
  19254. null,
  19255. "Произвольный статус"
  19256. ],
  19257. "Click to change your chat status": [
  19258. null,
  19259. "Изменить ваш статус"
  19260. ],
  19261. "Click here to write a custom status message": [
  19262. null,
  19263. "Редактировать произвольный статус"
  19264. ],
  19265. "online": [
  19266. null,
  19267. "на связи"
  19268. ],
  19269. "busy": [
  19270. null,
  19271. "занят"
  19272. ],
  19273. "away for long": [
  19274. null,
  19275. "отошёл на долго"
  19276. ],
  19277. "away": [
  19278. null,
  19279. "отошёл"
  19280. ],
  19281. "I am %1$s": [
  19282. null,
  19283. "%1$s"
  19284. ],
  19285. "Sign in": [
  19286. null,
  19287. "Подписать"
  19288. ],
  19289. "XMPP/Jabber Username:": [
  19290. null,
  19291. "JID:"
  19292. ],
  19293. "Password:": [
  19294. null,
  19295. "Пароль:"
  19296. ],
  19297. "Log In": [
  19298. null,
  19299. "Войти"
  19300. ],
  19301. "Online Contacts": [
  19302. null,
  19303. "Cписок собеседников"
  19304. ]
  19305. }
  19306. }
  19307. };
  19308. if (typeof define === 'function' && define.amd) {
  19309. define("ru", ['jed'], function () {
  19310. return factory(new Jed(translations));
  19311. });
  19312. } else {
  19313. if (!window.locales) {
  19314. window.locales = {};
  19315. }
  19316. window.locales.ru = factory(new Jed(translations));
  19317. }
  19318. }(this, function (ru) {
  19319. return ru;
  19320. }));
  19321. (function (root, factory) {
  19322. var translations = {
  19323. "domain": "converse",
  19324. "locale_data": {
  19325. "converse": {
  19326. "": {
  19327. "Project-Id-Version": "Converse.js 0.8",
  19328. "Report-Msgid-Bugs-To": "",
  19329. "POT-Creation-Date": "2014-01-07 11:12+0900",
  19330. "PO-Revision-Date": "2014-01-07 11:32+0900",
  19331. "Last-Translator": "Huxisuz Hu <huxisuz@gmail.com>",
  19332. "Language-Team": "Language zh",
  19333. "Language": "zh",
  19334. "MIME-Version": "1.0",
  19335. "Content-Type": "text/plain; charset=UTF-8",
  19336. "Content-Transfer-Encoding": "8bit",
  19337. "Plural-Forms": "nplurals=1; plural=0;"
  19338. },
  19339. "unencrypted": [null,"未加密"],
  19340. "unverified": [null,"未验证"],
  19341. "verified": [null,"已验证"],
  19342. "finished": [null,"结束了"],
  19343. "This contact is busy": [null,"对方忙碌中"],
  19344. "This contact is online": [null,"对方在线中"],
  19345. "This contact is offline": [null,"对方已下线"],
  19346. "This contact is unavailable": [null,"对方免打扰"],
  19347. "This contact is away for an extended period": [null,"对方暂时离开"],
  19348. "This contact is away": [null,"对方离开"],
  19349. "Disconnected": [null,"连接已断开"],
  19350. "Error": [null,"错误"],
  19351. "Connecting": [null,"连接中"],
  19352. "Connection Failed": [null,"连接失败"],
  19353. "Authenticating": [null,"验证中"],
  19354. "Authentication Failed": [null,"验证失败"],
  19355. "Disconnecting": [null,"断开链接中"],
  19356. "Online Contacts": [null,"在线好友"],
  19357. "Re-establishing encrypted session": [null,"重新建立加密会话"],
  19358. "Generating private key.": [null,"正在生成私钥"],
  19359. "Your browser might become unresponsive.": [null,"您的浏览器可能会暂时无响应"],
  19360. "Authentication request from %1$s\n\nYour buddy is attempting to verify your identity, by asking you the question below.\n\n%2$s": [null,"来自%1$s的验证请求 \n\n对方正在试图验证您的信息,请回答如下问题:\n\n%2$s"],
  19361. "Could not verify this user's identify.": [null,"无法验证对方信息。"],
  19362. "Exchanging private key with buddy.": [null,"正在与对方交换私钥"],
  19363. "Personal message": [null,"私信"],
  19364. "me": [null,"我"],
  19365. "Show this menu": [null,"显示此项菜单"],
  19366. "Write in the third person": [null,"以第三者身份写"],
  19367. "Remove messages": [null,"移除消息"],
  19368. "Are you sure you want to clear the messages from this chat box?": [null,"你确定清除此次的聊天记录吗?"],
  19369. "Your message could not be sent": [null,"您的消息无法送出"],
  19370. "We received an unencrypted message": [null,"我们收到了一条未加密的信息"],
  19371. "We received an unreadable encrypted message": [null,"我们收到一条无法读取的信息"],
  19372. "This user has requested an encrypted session.": [null,"此用户请求了一个加密会话。"],
  19373. "Here are the fingerprints, please confirm them with %1$s, outside of this chat.\n\nFingerprint for you, %2$s: %3$s\n\nFingerprint for %1$s: %4$s\n\nIf you have confirmed that the fingerprints match, click OK, otherwise click Cancel.": [null,"这里是指纹。请与 %1$s 确认。\n\n您的 %2$s 指纹: %3$s\n\n%1$s 的指纹: %4$s\n\n如果确认符合,请点击OK,否则点击取消"],
  19374. "What is your security question?": [null,"您的安全问题是?"],
  19375. "What is the answer to the security question?": [null,"此安全问题的答案是?"],
  19376. "Invalid authentication scheme provided": [null,"非法的认证方式"],
  19377. "Your messages are not encrypted anymore": [null,"您的消息将不再被加密"],
  19378. "Your messages are now encrypted but your buddy's identity has not been verified.": [null,"您的消息现已加密,但是对方身份尚未验证"],
  19379. "Your buddy's identify has been verified.": [null,"对方的身份已通过验证。"],
  19380. "Your buddy has ended encryption on their end, you should do the same.": [null,"对方已结束加密,您也需要做同样的操作。"],
  19381. "Your messages are not encrypted. Click here to enable OTR encryption.": [null,"您的消息未加密。点击这里来启用OTR加密"],
  19382. "Your messages are encrypted, but your buddy has not been verified.": [null,"您的消息已加密,但对方未通过验证"],
  19383. "Your messages are encrypted and your buddy verified.": [null,"您的消息已加密,对方已验证。"],
  19384. "Your buddy has closed their end of the private session, you should do the same": [null,"对方已关闭私有会话,您也应该关闭"],
  19385. "End encrypted conversation": [null,"结束加密的会话"],
  19386. "Refresh encrypted conversation": [null,"刷新加密的会话"],
  19387. "Start encrypted conversation": [null,"开始加密的会话"],
  19388. "Verify with fingerprints": [null,"验证指纹"],
  19389. "Verify with SMP": [null,"验证SMP"],
  19390. "What's this?": [null,"这是什么?"],
  19391. "Online": [null,"在线"],
  19392. "Busy": [null,"忙碌中"],
  19393. "Away": [null,"离开"],
  19394. "Offline": [null,"离线"],
  19395. "Contacts": [null,"联系人"],
  19396. "Contact name": [null,"联系人名称"],
  19397. "Search": [null,"搜索"],
  19398. "Contact username": [null,"联系人姓名"],
  19399. "Add": [null,"添加"],
  19400. "Click to add new chat contacts": [null,"点击添加新联系人"],
  19401. "Add a contact": [null,"添加联系人"],
  19402. "No users found": [null,"未找到用户"],
  19403. "Click to add as a chat contact": [null,"点击添加为好友"],
  19404. "Room name": [null,"聊天室名称"],
  19405. "Nickname": [null,"昵称"],
  19406. "Server": [null,"服务器"],
  19407. "Join": [null,"加入"],
  19408. "Show rooms": [null,"显示所有聊天室"],
  19409. "Rooms": [null,"聊天室"],
  19410. "No rooms on %1$s": [null,"%1$s 上没有聊天室"],
  19411. "Rooms on %1$s": [null,"%1$s 上的聊天室"],
  19412. "Click to open this room": [null,"打开聊天室"],
  19413. "Show more information on this room": [null,"显示次聊天室的更多信息"],
  19414. "Description:": [null,"描述: "],
  19415. "Occupants:": [null,"成员:"],
  19416. "Features:": [null,"特性:"],
  19417. "Requires authentication": [null,"需要验证"],
  19418. "Hidden": [null,"隐藏的"],
  19419. "Requires an invitation": [null,"需要被邀请"],
  19420. "Moderated": [null,"发言受限"],
  19421. "Non-anonymous": [null,"非匿名"],
  19422. "Open room": [null,"打开聊天室"],
  19423. "Permanent room": [null,"永久聊天室"],
  19424. "Public": [null,"公开的"],
  19425. "Semi-anonymous": [null,"半匿名"],
  19426. "Temporary room": [null,"临时聊天室"],
  19427. "Unmoderated": [null,"无发言限制"],
  19428. "Set chatroom topic": [null,"设置房间主题"],
  19429. "Kick user from chatroom": [null,"把用户踢出房间"],
  19430. "Ban user from chatroom": [null,"阻止此用户进入房间"],
  19431. "Message": [null,"信息"],
  19432. "Save": [null,"保存"],
  19433. "Cancel": [null,"取消"],
  19434. "An error occurred while trying to save the form.": [null,"保存表单是出错。"],
  19435. "This chatroom requires a password": [null,"此聊天室需要密码"],
  19436. "Password: ": [null,"密码:"],
  19437. "Submit": [null,"发送"],
  19438. "This room is not anonymous": [null,"此为非匿名聊天室"],
  19439. "This room now shows unavailable members": [null,"此聊天室显示不可用用户"],
  19440. "This room does not show unavailable members": [null,"此聊天室不显示不可用用户"],
  19441. "Non-privacy-related room configuration has changed": [null,"此聊天室设置(非私密性)已改变"],
  19442. "Room logging is now enabled": [null,"聊天室聊天记录已启用"],
  19443. "Room logging is now disabled": [null,"聊天室聊天记录已禁用"],
  19444. "This room is now non-anonymous": [null,"此聊天室非匿名"],
  19445. "This room is now semi-anonymous": [null,"此聊天室半匿名"],
  19446. "This room is now fully-anonymous": [null,"此聊天室完全匿名"],
  19447. "A new room has been created": [null,"新聊天室已创建"],
  19448. "Your nickname has been changed": [null,"您的昵称被更改了"],
  19449. "<strong>%1$s</strong> has been banned": [null,"<strong>%1$s</strong> 已被禁止"],
  19450. "<strong>%1$s</strong> has been kicked out": [null,"<strong>%1$s</strong> 已被踢出"],
  19451. "<strong>%1$s</strong> has been removed because of an affiliation change": [null,"由于关系解除、<strong>%1$s</strong> 已被移除"],
  19452. "<strong>%1$s</strong> has been removed for not being a member": [null,"由于不是成员、<strong>%1$s</strong> 已被移除"],
  19453. "You have been banned from this room": [null,"您已被此聊天室禁止入内"],
  19454. "You have been kicked from this room": [null,"您已被踢出次房间"],
  19455. "You have been removed from this room because of an affiliation change": [null,"由于关系变化,您已被移除此房间"],
  19456. "You have been removed from this room because the room has changed to members-only and you're not a member": [null,"您已被移除此房间因为此房间更改为只允许成员加入,而您非成员"],
  19457. "You have been removed from this room because the MUC (Multi-user chat) service is being shut down.": [null,"由于服务不可用,您已被移除此房间。"],
  19458. "You are not on the member list of this room": [null,"您并非此房间成员"],
  19459. "No nickname was specified": [null,"未指定昵称"],
  19460. "You are not allowed to create new rooms": [null,"您可此创建新房间了"],
  19461. "Your nickname doesn't conform to this room's policies": [null,"您的昵称不符合此房间标准"],
  19462. "Your nickname is already taken": [null,"您的昵称已被占用"],
  19463. "This room does not (yet) exist": [null,"此房间不存在"],
  19464. "This room has reached it's maximum number of occupants": [null,"此房间人数已达上线"],
  19465. "Topic set by %1$s to: %2$s": [null,"%1$s 设置话题为: %2$s"],
  19466. "This user is a moderator": [null,"此用户是主持人"],
  19467. "This user can send messages in this room": [null,"此用户在这房间里可发消息"],
  19468. "This user can NOT send messages in this room": [null,"此用户不可在此房间发消息"],
  19469. "Minimized": [null,"最小化的"],
  19470. "Click to remove this contact": [null,"点击移除联系人"],
  19471. "Accept": [null,"接受"],
  19472. "Click to chat with this contact": [null,"点击与对方交谈"],
  19473. "My contacts": [null,"我的好友列表"],
  19474. "Contact requests": [null,"来自好友的请求"],
  19475. "Pending contacts": [null,"保留中的联系人"],
  19476. "Custom status": [null,"DIY状态"],
  19477. "online": [null,"在线"],
  19478. "busy": [null,"忙碌"],
  19479. "away for long": [null,"长时间离开"],
  19480. "away": [null,"离开"],
  19481. "I am %1$s": [null,"我现在%1$s"],
  19482. "Click here to write a custom status message": [null,"点击这里,填写状态信息"],
  19483. "Click to change your chat status": [null,"点击这里改变聊天状态"],
  19484. "XMPP/Jabber Username:": [null,"XMPP/Jabber用户名:"],
  19485. "Password:": [null,"密码:"],
  19486. "Log In": [null,"登录"],
  19487. "Sign in": [null,"登录"],
  19488. "Toggle chat": [null,"折叠聊天窗口"]
  19489. }
  19490. }
  19491. };
  19492. if (typeof define === 'function' && define.amd) {
  19493. define("zh", ['jed'], function () {
  19494. return factory(new Jed(translations));
  19495. });
  19496. } else {
  19497. if (!window.locales) {
  19498. window.locales = {};
  19499. }
  19500. window.locales.zh = factory(new Jed(translations));
  19501. }
  19502. }(this, function (zh) {
  19503. return zh;
  19504. }));
  19505. /*
  19506. * This file specifies the language dependencies.
  19507. *
  19508. * Translations take up a lot of space and you are therefore advised to remove
  19509. * from here any languages that you don't need.
  19510. */
  19511. (function (root, factory) {
  19512. define("locales", [
  19513. 'jed',
  19514. 'af',
  19515. 'de',
  19516. 'en',
  19517. 'es',
  19518. 'fr',
  19519. 'he',
  19520. 'hu',
  19521. 'id',
  19522. 'it',
  19523. 'ja',
  19524. 'nl',
  19525. 'pt_BR',
  19526. 'ru',
  19527. 'zh'
  19528. ], function (jed, af, de, en, es, fr, he, hu, id, it, ja, nl, pt_BR, ru, zh) {
  19529. root.locales = {
  19530. 'af': af,
  19531. 'de': de,
  19532. 'en': en,
  19533. 'es': es,
  19534. 'fr': fr,
  19535. 'he': he,
  19536. 'hu': hu,
  19537. 'id': id,
  19538. 'it': it,
  19539. 'ja': ja,
  19540. 'nl': nl,
  19541. 'pt-br': pt_BR,
  19542. 'ru': ru,
  19543. 'zh':zh
  19544. };
  19545. });
  19546. })(this);
  19547. // Backbone.js 1.1.2
  19548. // (c) 2010-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
  19549. // Backbone may be freely distributed under the MIT license.
  19550. // For all details and documentation:
  19551. // http://backbonejs.org
  19552. (function(root, factory) {
  19553. // Set up Backbone appropriately for the environment. Start with AMD.
  19554. if (typeof define === 'function' && define.amd) {
  19555. define('backbone',['underscore', 'jquery', 'exports'], function(_, $, exports) {
  19556. // Export global even in AMD case in case this script is loaded with
  19557. // others that may still expect a global Backbone.
  19558. root.Backbone = factory(root, exports, _, $);
  19559. });
  19560. // Next for Node.js or CommonJS. jQuery may not be needed as a module.
  19561. } else if (typeof exports !== 'undefined') {
  19562. var _ = require('underscore');
  19563. factory(root, exports, _);
  19564. // Finally, as a browser global.
  19565. } else {
  19566. root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$));
  19567. }
  19568. }(this, function(root, Backbone, _, $) {
  19569. // Initial Setup
  19570. // -------------
  19571. // Save the previous value of the `Backbone` variable, so that it can be
  19572. // restored later on, if `noConflict` is used.
  19573. var previousBackbone = root.Backbone;
  19574. // Create local references to array methods we'll want to use later.
  19575. var array = [];
  19576. var push = array.push;
  19577. var slice = array.slice;
  19578. var splice = array.splice;
  19579. // Current version of the library. Keep in sync with `package.json`.
  19580. Backbone.VERSION = '1.1.2';
  19581. // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
  19582. // the `$` variable.
  19583. Backbone.$ = $;
  19584. // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
  19585. // to its previous owner. Returns a reference to this Backbone object.
  19586. Backbone.noConflict = function() {
  19587. root.Backbone = previousBackbone;
  19588. return this;
  19589. };
  19590. // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
  19591. // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and
  19592. // set a `X-Http-Method-Override` header.
  19593. Backbone.emulateHTTP = false;
  19594. // Turn on `emulateJSON` to support legacy servers that can't deal with direct
  19595. // `application/json` requests ... will encode the body as
  19596. // `application/x-www-form-urlencoded` instead and will send the model in a
  19597. // form param named `model`.
  19598. Backbone.emulateJSON = false;
  19599. // Backbone.Events
  19600. // ---------------
  19601. // A module that can be mixed in to *any object* in order to provide it with
  19602. // custom events. You may bind with `on` or remove with `off` callback
  19603. // functions to an event; `trigger`-ing an event fires all callbacks in
  19604. // succession.
  19605. //
  19606. // var object = {};
  19607. // _.extend(object, Backbone.Events);
  19608. // object.on('expand', function(){ alert('expanded'); });
  19609. // object.trigger('expand');
  19610. //
  19611. var Events = Backbone.Events = {
  19612. // Bind an event to a `callback` function. Passing `"all"` will bind
  19613. // the callback to all events fired.
  19614. on: function(name, callback, context) {
  19615. if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
  19616. this._events || (this._events = {});
  19617. var events = this._events[name] || (this._events[name] = []);
  19618. events.push({callback: callback, context: context, ctx: context || this});
  19619. return this;
  19620. },
  19621. // Bind an event to only be triggered a single time. After the first time
  19622. // the callback is invoked, it will be removed.
  19623. once: function(name, callback, context) {
  19624. if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this;
  19625. var self = this;
  19626. var once = _.once(function() {
  19627. self.off(name, once);
  19628. callback.apply(this, arguments);
  19629. });
  19630. once._callback = callback;
  19631. return this.on(name, once, context);
  19632. },
  19633. // Remove one or many callbacks. If `context` is null, removes all
  19634. // callbacks with that function. If `callback` is null, removes all
  19635. // callbacks for the event. If `name` is null, removes all bound
  19636. // callbacks for all events.
  19637. off: function(name, callback, context) {
  19638. var retain, ev, events, names, i, l, j, k;
  19639. if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
  19640. if (!name && !callback && !context) {
  19641. this._events = void 0;
  19642. return this;
  19643. }
  19644. names = name ? [name] : _.keys(this._events);
  19645. for (i = 0, l = names.length; i < l; i++) {
  19646. name = names[i];
  19647. if (events = this._events[name]) {
  19648. this._events[name] = retain = [];
  19649. if (callback || context) {
  19650. for (j = 0, k = events.length; j < k; j++) {
  19651. ev = events[j];
  19652. if ((callback && callback !== ev.callback && callback !== ev.callback._callback) ||
  19653. (context && context !== ev.context)) {
  19654. retain.push(ev);
  19655. }
  19656. }
  19657. }
  19658. if (!retain.length) delete this._events[name];
  19659. }
  19660. }
  19661. return this;
  19662. },
  19663. // Trigger one or many events, firing all bound callbacks. Callbacks are
  19664. // passed the same arguments as `trigger` is, apart from the event name
  19665. // (unless you're listening on `"all"`, which will cause your callback to
  19666. // receive the true name of the event as the first argument).
  19667. trigger: function(name) {
  19668. if (!this._events) return this;
  19669. var args = slice.call(arguments, 1);
  19670. if (!eventsApi(this, 'trigger', name, args)) return this;
  19671. var events = this._events[name];
  19672. var allEvents = this._events.all;
  19673. if (events) triggerEvents(events, args);
  19674. if (allEvents) triggerEvents(allEvents, arguments);
  19675. return this;
  19676. },
  19677. // Tell this object to stop listening to either specific events ... or
  19678. // to every object it's currently listening to.
  19679. stopListening: function(obj, name, callback) {
  19680. var listeningTo = this._listeningTo;
  19681. if (!listeningTo) return this;
  19682. var remove = !name && !callback;
  19683. if (!callback && typeof name === 'object') callback = this;
  19684. if (obj) (listeningTo = {})[obj._listenId] = obj;
  19685. for (var id in listeningTo) {
  19686. obj = listeningTo[id];
  19687. obj.off(name, callback, this);
  19688. if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id];
  19689. }
  19690. return this;
  19691. }
  19692. };
  19693. // Regular expression used to split event strings.
  19694. var eventSplitter = /\s+/;
  19695. // Implement fancy features of the Events API such as multiple event
  19696. // names `"change blur"` and jQuery-style event maps `{change: action}`
  19697. // in terms of the existing API.
  19698. var eventsApi = function(obj, action, name, rest) {
  19699. if (!name) return true;
  19700. // Handle event maps.
  19701. if (typeof name === 'object') {
  19702. for (var key in name) {
  19703. obj[action].apply(obj, [key, name[key]].concat(rest));
  19704. }
  19705. return false;
  19706. }
  19707. // Handle space separated event names.
  19708. if (eventSplitter.test(name)) {
  19709. var names = name.split(eventSplitter);
  19710. for (var i = 0, l = names.length; i < l; i++) {
  19711. obj[action].apply(obj, [names[i]].concat(rest));
  19712. }
  19713. return false;
  19714. }
  19715. return true;
  19716. };
  19717. // A difficult-to-believe, but optimized internal dispatch function for
  19718. // triggering events. Tries to keep the usual cases speedy (most internal
  19719. // Backbone events have 3 arguments).
  19720. var triggerEvents = function(events, args) {
  19721. var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
  19722. switch (args.length) {
  19723. case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
  19724. case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
  19725. case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
  19726. case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
  19727. default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
  19728. }
  19729. };
  19730. var listenMethods = {listenTo: 'on', listenToOnce: 'once'};
  19731. // Inversion-of-control versions of `on` and `once`. Tell *this* object to
  19732. // listen to an event in another object ... keeping track of what it's
  19733. // listening to.
  19734. _.each(listenMethods, function(implementation, method) {
  19735. Events[method] = function(obj, name, callback) {
  19736. var listeningTo = this._listeningTo || (this._listeningTo = {});
  19737. var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
  19738. listeningTo[id] = obj;
  19739. if (!callback && typeof name === 'object') callback = this;
  19740. obj[implementation](name, callback, this);
  19741. return this;
  19742. };
  19743. });
  19744. // Aliases for backwards compatibility.
  19745. Events.bind = Events.on;
  19746. Events.unbind = Events.off;
  19747. // Allow the `Backbone` object to serve as a global event bus, for folks who
  19748. // want global "pubsub" in a convenient place.
  19749. _.extend(Backbone, Events);
  19750. // Backbone.Model
  19751. // --------------
  19752. // Backbone **Models** are the basic data object in the framework --
  19753. // frequently representing a row in a table in a database on your server.
  19754. // A discrete chunk of data and a bunch of useful, related methods for
  19755. // performing computations and transformations on that data.
  19756. // Create a new model with the specified attributes. A client id (`cid`)
  19757. // is automatically generated and assigned for you.
  19758. var Model = Backbone.Model = function(attributes, options) {
  19759. var attrs = attributes || {};
  19760. options || (options = {});
  19761. this.cid = _.uniqueId('c');
  19762. this.attributes = {};
  19763. if (options.collection) this.collection = options.collection;
  19764. if (options.parse) attrs = this.parse(attrs, options) || {};
  19765. attrs = _.defaults({}, attrs, _.result(this, 'defaults'));
  19766. this.set(attrs, options);
  19767. this.changed = {};
  19768. this.initialize.apply(this, arguments);
  19769. };
  19770. // Attach all inheritable methods to the Model prototype.
  19771. _.extend(Model.prototype, Events, {
  19772. // A hash of attributes whose current and previous value differ.
  19773. changed: null,
  19774. // The value returned during the last failed validation.
  19775. validationError: null,
  19776. // The default name for the JSON `id` attribute is `"id"`. MongoDB and
  19777. // CouchDB users may want to set this to `"_id"`.
  19778. idAttribute: 'id',
  19779. // Initialize is an empty function by default. Override it with your own
  19780. // initialization logic.
  19781. initialize: function(){},
  19782. // Return a copy of the model's `attributes` object.
  19783. toJSON: function(options) {
  19784. return _.clone(this.attributes);
  19785. },
  19786. // Proxy `Backbone.sync` by default -- but override this if you need
  19787. // custom syncing semantics for *this* particular model.
  19788. sync: function() {
  19789. return Backbone.sync.apply(this, arguments);
  19790. },
  19791. // Get the value of an attribute.
  19792. get: function(attr) {
  19793. return this.attributes[attr];
  19794. },
  19795. // Get the HTML-escaped value of an attribute.
  19796. escape: function(attr) {
  19797. return _.escape(this.get(attr));
  19798. },
  19799. // Returns `true` if the attribute contains a value that is not null
  19800. // or undefined.
  19801. has: function(attr) {
  19802. return this.get(attr) != null;
  19803. },
  19804. // Set a hash of model attributes on the object, firing `"change"`. This is
  19805. // the core primitive operation of a model, updating the data and notifying
  19806. // anyone who needs to know about the change in state. The heart of the beast.
  19807. set: function(key, val, options) {
  19808. var attr, attrs, unset, changes, silent, changing, prev, current;
  19809. if (key == null) return this;
  19810. // Handle both `"key", value` and `{key: value}` -style arguments.
  19811. if (typeof key === 'object') {
  19812. attrs = key;
  19813. options = val;
  19814. } else {
  19815. (attrs = {})[key] = val;
  19816. }
  19817. options || (options = {});
  19818. // Run validation.
  19819. if (!this._validate(attrs, options)) return false;
  19820. // Extract attributes and options.
  19821. unset = options.unset;
  19822. silent = options.silent;
  19823. changes = [];
  19824. changing = this._changing;
  19825. this._changing = true;
  19826. if (!changing) {
  19827. this._previousAttributes = _.clone(this.attributes);
  19828. this.changed = {};
  19829. }
  19830. current = this.attributes, prev = this._previousAttributes;
  19831. // Check for changes of `id`.
  19832. if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
  19833. // For each `set` attribute, update or delete the current value.
  19834. for (attr in attrs) {
  19835. val = attrs[attr];
  19836. if (!_.isEqual(current[attr], val)) changes.push(attr);
  19837. if (!_.isEqual(prev[attr], val)) {
  19838. this.changed[attr] = val;
  19839. } else {
  19840. delete this.changed[attr];
  19841. }
  19842. unset ? delete current[attr] : current[attr] = val;
  19843. }
  19844. // Trigger all relevant attribute changes.
  19845. if (!silent) {
  19846. if (changes.length) this._pending = options;
  19847. for (var i = 0, l = changes.length; i < l; i++) {
  19848. this.trigger('change:' + changes[i], this, current[changes[i]], options);
  19849. }
  19850. }
  19851. // You might be wondering why there's a `while` loop here. Changes can
  19852. // be recursively nested within `"change"` events.
  19853. if (changing) return this;
  19854. if (!silent) {
  19855. while (this._pending) {
  19856. options = this._pending;
  19857. this._pending = false;
  19858. this.trigger('change', this, options);
  19859. }
  19860. }
  19861. this._pending = false;
  19862. this._changing = false;
  19863. return this;
  19864. },
  19865. // Remove an attribute from the model, firing `"change"`. `unset` is a noop
  19866. // if the attribute doesn't exist.
  19867. unset: function(attr, options) {
  19868. return this.set(attr, void 0, _.extend({}, options, {unset: true}));
  19869. },
  19870. // Clear all attributes on the model, firing `"change"`.
  19871. clear: function(options) {
  19872. var attrs = {};
  19873. for (var key in this.attributes) attrs[key] = void 0;
  19874. return this.set(attrs, _.extend({}, options, {unset: true}));
  19875. },
  19876. // Determine if the model has changed since the last `"change"` event.
  19877. // If you specify an attribute name, determine if that attribute has changed.
  19878. hasChanged: function(attr) {
  19879. if (attr == null) return !_.isEmpty(this.changed);
  19880. return _.has(this.changed, attr);
  19881. },
  19882. // Return an object containing all the attributes that have changed, or
  19883. // false if there are no changed attributes. Useful for determining what
  19884. // parts of a view need to be updated and/or what attributes need to be
  19885. // persisted to the server. Unset attributes will be set to undefined.
  19886. // You can also pass an attributes object to diff against the model,
  19887. // determining if there *would be* a change.
  19888. changedAttributes: function(diff) {
  19889. if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
  19890. var val, changed = false;
  19891. var old = this._changing ? this._previousAttributes : this.attributes;
  19892. for (var attr in diff) {
  19893. if (_.isEqual(old[attr], (val = diff[attr]))) continue;
  19894. (changed || (changed = {}))[attr] = val;
  19895. }
  19896. return changed;
  19897. },
  19898. // Get the previous value of an attribute, recorded at the time the last
  19899. // `"change"` event was fired.
  19900. previous: function(attr) {
  19901. if (attr == null || !this._previousAttributes) return null;
  19902. return this._previousAttributes[attr];
  19903. },
  19904. // Get all of the attributes of the model at the time of the previous
  19905. // `"change"` event.
  19906. previousAttributes: function() {
  19907. return _.clone(this._previousAttributes);
  19908. },
  19909. // Fetch the model from the server. If the server's representation of the
  19910. // model differs from its current attributes, they will be overridden,
  19911. // triggering a `"change"` event.
  19912. fetch: function(options) {
  19913. options = options ? _.clone(options) : {};
  19914. if (options.parse === void 0) options.parse = true;
  19915. var model = this;
  19916. var success = options.success;
  19917. options.success = function(resp) {
  19918. if (!model.set(model.parse(resp, options), options)) return false;
  19919. if (success) success(model, resp, options);
  19920. model.trigger('sync', model, resp, options);
  19921. };
  19922. wrapError(this, options);
  19923. return this.sync('read', this, options);
  19924. },
  19925. // Set a hash of model attributes, and sync the model to the server.
  19926. // If the server returns an attributes hash that differs, the model's
  19927. // state will be `set` again.
  19928. save: function(key, val, options) {
  19929. var attrs, method, xhr, attributes = this.attributes;
  19930. // Handle both `"key", value` and `{key: value}` -style arguments.
  19931. if (key == null || typeof key === 'object') {
  19932. attrs = key;
  19933. options = val;
  19934. } else {
  19935. (attrs = {})[key] = val;
  19936. }
  19937. options = _.extend({validate: true}, options);
  19938. // If we're not waiting and attributes exist, save acts as
  19939. // `set(attr).save(null, opts)` with validation. Otherwise, check if
  19940. // the model will be valid when the attributes, if any, are set.
  19941. if (attrs && !options.wait) {
  19942. if (!this.set(attrs, options)) return false;
  19943. } else {
  19944. if (!this._validate(attrs, options)) return false;
  19945. }
  19946. // Set temporary attributes if `{wait: true}`.
  19947. if (attrs && options.wait) {
  19948. this.attributes = _.extend({}, attributes, attrs);
  19949. }
  19950. // After a successful server-side save, the client is (optionally)
  19951. // updated with the server-side state.
  19952. if (options.parse === void 0) options.parse = true;
  19953. var model = this;
  19954. var success = options.success;
  19955. options.success = function(resp) {
  19956. // Ensure attributes are restored during synchronous saves.
  19957. model.attributes = attributes;
  19958. var serverAttrs = model.parse(resp, options);
  19959. if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
  19960. if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
  19961. return false;
  19962. }
  19963. if (success) success(model, resp, options);
  19964. model.trigger('sync', model, resp, options);
  19965. };
  19966. wrapError(this, options);
  19967. method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
  19968. if (method === 'patch') options.attrs = attrs;
  19969. xhr = this.sync(method, this, options);
  19970. // Restore attributes.
  19971. if (attrs && options.wait) this.attributes = attributes;
  19972. return xhr;
  19973. },
  19974. // Destroy this model on the server if it was already persisted.
  19975. // Optimistically removes the model from its collection, if it has one.
  19976. // If `wait: true` is passed, waits for the server to respond before removal.
  19977. destroy: function(options) {
  19978. options = options ? _.clone(options) : {};
  19979. var model = this;
  19980. var success = options.success;
  19981. var destroy = function() {
  19982. model.trigger('destroy', model, model.collection, options);
  19983. };
  19984. options.success = function(resp) {
  19985. if (options.wait || model.isNew()) destroy();
  19986. if (success) success(model, resp, options);
  19987. if (!model.isNew()) model.trigger('sync', model, resp, options);
  19988. };
  19989. if (this.isNew()) {
  19990. options.success();
  19991. return false;
  19992. }
  19993. wrapError(this, options);
  19994. var xhr = this.sync('delete', this, options);
  19995. if (!options.wait) destroy();
  19996. return xhr;
  19997. },
  19998. // Default URL for the model's representation on the server -- if you're
  19999. // using Backbone's restful methods, override this to change the endpoint
  20000. // that will be called.
  20001. url: function() {
  20002. var base =
  20003. _.result(this, 'urlRoot') ||
  20004. _.result(this.collection, 'url') ||
  20005. urlError();
  20006. if (this.isNew()) return base;
  20007. return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id);
  20008. },
  20009. // **parse** converts a response into the hash of attributes to be `set` on
  20010. // the model. The default implementation is just to pass the response along.
  20011. parse: function(resp, options) {
  20012. return resp;
  20013. },
  20014. // Create a new model with identical attributes to this one.
  20015. clone: function() {
  20016. return new this.constructor(this.attributes);
  20017. },
  20018. // A model is new if it has never been saved to the server, and lacks an id.
  20019. isNew: function() {
  20020. return !this.has(this.idAttribute);
  20021. },
  20022. // Check if the model is currently in a valid state.
  20023. isValid: function(options) {
  20024. return this._validate({}, _.extend(options || {}, { validate: true }));
  20025. },
  20026. // Run validation against the next complete set of model attributes,
  20027. // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
  20028. _validate: function(attrs, options) {
  20029. if (!options.validate || !this.validate) return true;
  20030. attrs = _.extend({}, this.attributes, attrs);
  20031. var error = this.validationError = this.validate(attrs, options) || null;
  20032. if (!error) return true;
  20033. this.trigger('invalid', this, error, _.extend(options, {validationError: error}));
  20034. return false;
  20035. }
  20036. });
  20037. // Underscore methods that we want to implement on the Model.
  20038. var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit'];
  20039. // Mix in each Underscore method as a proxy to `Model#attributes`.
  20040. _.each(modelMethods, function(method) {
  20041. Model.prototype[method] = function() {
  20042. var args = slice.call(arguments);
  20043. args.unshift(this.attributes);
  20044. return _[method].apply(_, args);
  20045. };
  20046. });
  20047. // Backbone.Collection
  20048. // -------------------
  20049. // If models tend to represent a single row of data, a Backbone Collection is
  20050. // more analagous to a table full of data ... or a small slice or page of that
  20051. // table, or a collection of rows that belong together for a particular reason
  20052. // -- all of the messages in this particular folder, all of the documents
  20053. // belonging to this particular author, and so on. Collections maintain
  20054. // indexes of their models, both in order, and for lookup by `id`.
  20055. // Create a new **Collection**, perhaps to contain a specific type of `model`.
  20056. // If a `comparator` is specified, the Collection will maintain
  20057. // its models in sort order, as they're added and removed.
  20058. var Collection = Backbone.Collection = function(models, options) {
  20059. options || (options = {});
  20060. if (options.model) this.model = options.model;
  20061. if (options.comparator !== void 0) this.comparator = options.comparator;
  20062. this._reset();
  20063. this.initialize.apply(this, arguments);
  20064. if (models) this.reset(models, _.extend({silent: true}, options));
  20065. };
  20066. // Default options for `Collection#set`.
  20067. var setOptions = {add: true, remove: true, merge: true};
  20068. var addOptions = {add: true, remove: false};
  20069. // Define the Collection's inheritable methods.
  20070. _.extend(Collection.prototype, Events, {
  20071. // The default model for a collection is just a **Backbone.Model**.
  20072. // This should be overridden in most cases.
  20073. model: Model,
  20074. // Initialize is an empty function by default. Override it with your own
  20075. // initialization logic.
  20076. initialize: function(){},
  20077. // The JSON representation of a Collection is an array of the
  20078. // models' attributes.
  20079. toJSON: function(options) {
  20080. return this.map(function(model){ return model.toJSON(options); });
  20081. },
  20082. // Proxy `Backbone.sync` by default.
  20083. sync: function() {
  20084. return Backbone.sync.apply(this, arguments);
  20085. },
  20086. // Add a model, or list of models to the set.
  20087. add: function(models, options) {
  20088. return this.set(models, _.extend({merge: false}, options, addOptions));
  20089. },
  20090. // Remove a model, or a list of models from the set.
  20091. remove: function(models, options) {
  20092. var singular = !_.isArray(models);
  20093. models = singular ? [models] : _.clone(models);
  20094. options || (options = {});
  20095. var i, l, index, model;
  20096. for (i = 0, l = models.length; i < l; i++) {
  20097. model = models[i] = this.get(models[i]);
  20098. if (!model) continue;
  20099. delete this._byId[model.id];
  20100. delete this._byId[model.cid];
  20101. index = this.indexOf(model);
  20102. this.models.splice(index, 1);
  20103. this.length--;
  20104. if (!options.silent) {
  20105. options.index = index;
  20106. model.trigger('remove', model, this, options);
  20107. }
  20108. this._removeReference(model, options);
  20109. }
  20110. return singular ? models[0] : models;
  20111. },
  20112. // Update a collection by `set`-ing a new list of models, adding new ones,
  20113. // removing models that are no longer present, and merging models that
  20114. // already exist in the collection, as necessary. Similar to **Model#set**,
  20115. // the core operation for updating the data contained by the collection.
  20116. set: function(models, options) {
  20117. options = _.defaults({}, options, setOptions);
  20118. if (options.parse) models = this.parse(models, options);
  20119. var singular = !_.isArray(models);
  20120. models = singular ? (models ? [models] : []) : _.clone(models);
  20121. var i, l, id, model, attrs, existing, sort;
  20122. var at = options.at;
  20123. var targetModel = this.model;
  20124. var sortable = this.comparator && (at == null) && options.sort !== false;
  20125. var sortAttr = _.isString(this.comparator) ? this.comparator : null;
  20126. var toAdd = [], toRemove = [], modelMap = {};
  20127. var add = options.add, merge = options.merge, remove = options.remove;
  20128. var order = !sortable && add && remove ? [] : false;
  20129. // Turn bare objects into model references, and prevent invalid models
  20130. // from being added.
  20131. for (i = 0, l = models.length; i < l; i++) {
  20132. attrs = models[i] || {};
  20133. if (attrs instanceof Model) {
  20134. id = model = attrs;
  20135. } else {
  20136. id = attrs[targetModel.prototype.idAttribute || 'id'];
  20137. }
  20138. // If a duplicate is found, prevent it from being added and
  20139. // optionally merge it into the existing model.
  20140. if (existing = this.get(id)) {
  20141. if (remove) modelMap[existing.cid] = true;
  20142. if (merge) {
  20143. attrs = attrs === model ? model.attributes : attrs;
  20144. if (options.parse) attrs = existing.parse(attrs, options);
  20145. existing.set(attrs, options);
  20146. if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
  20147. }
  20148. models[i] = existing;
  20149. // If this is a new, valid model, push it to the `toAdd` list.
  20150. } else if (add) {
  20151. model = models[i] = this._prepareModel(attrs, options);
  20152. if (!model) continue;
  20153. toAdd.push(model);
  20154. this._addReference(model, options);
  20155. }
  20156. // Do not add multiple models with the same `id`.
  20157. model = existing || model;
  20158. if (order && (model.isNew() || !modelMap[model.id])) order.push(model);
  20159. modelMap[model.id] = true;
  20160. }
  20161. // Remove nonexistent models if appropriate.
  20162. if (remove) {
  20163. for (i = 0, l = this.length; i < l; ++i) {
  20164. if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model);
  20165. }
  20166. if (toRemove.length) this.remove(toRemove, options);
  20167. }
  20168. // See if sorting is needed, update `length` and splice in new models.
  20169. if (toAdd.length || (order && order.length)) {
  20170. if (sortable) sort = true;
  20171. this.length += toAdd.length;
  20172. if (at != null) {
  20173. for (i = 0, l = toAdd.length; i < l; i++) {
  20174. this.models.splice(at + i, 0, toAdd[i]);
  20175. }
  20176. } else {
  20177. if (order) this.models.length = 0;
  20178. var orderedModels = order || toAdd;
  20179. for (i = 0, l = orderedModels.length; i < l; i++) {
  20180. this.models.push(orderedModels[i]);
  20181. }
  20182. }
  20183. }
  20184. // Silently sort the collection if appropriate.
  20185. if (sort) this.sort({silent: true});
  20186. // Unless silenced, it's time to fire all appropriate add/sort events.
  20187. if (!options.silent) {
  20188. for (i = 0, l = toAdd.length; i < l; i++) {
  20189. (model = toAdd[i]).trigger('add', model, this, options);
  20190. }
  20191. if (sort || (order && order.length)) this.trigger('sort', this, options);
  20192. }
  20193. // Return the added (or merged) model (or models).
  20194. return singular ? models[0] : models;
  20195. },
  20196. // When you have more items than you want to add or remove individually,
  20197. // you can reset the entire set with a new list of models, without firing
  20198. // any granular `add` or `remove` events. Fires `reset` when finished.
  20199. // Useful for bulk operations and optimizations.
  20200. reset: function(models, options) {
  20201. options || (options = {});
  20202. for (var i = 0, l = this.models.length; i < l; i++) {
  20203. this._removeReference(this.models[i], options);
  20204. }
  20205. options.previousModels = this.models;
  20206. this._reset();
  20207. models = this.add(models, _.extend({silent: true}, options));
  20208. if (!options.silent) this.trigger('reset', this, options);
  20209. return models;
  20210. },
  20211. // Add a model to the end of the collection.
  20212. push: function(model, options) {
  20213. return this.add(model, _.extend({at: this.length}, options));
  20214. },
  20215. // Remove a model from the end of the collection.
  20216. pop: function(options) {
  20217. var model = this.at(this.length - 1);
  20218. this.remove(model, options);
  20219. return model;
  20220. },
  20221. // Add a model to the beginning of the collection.
  20222. unshift: function(model, options) {
  20223. return this.add(model, _.extend({at: 0}, options));
  20224. },
  20225. // Remove a model from the beginning of the collection.
  20226. shift: function(options) {
  20227. var model = this.at(0);
  20228. this.remove(model, options);
  20229. return model;
  20230. },
  20231. // Slice out a sub-array of models from the collection.
  20232. slice: function() {
  20233. return slice.apply(this.models, arguments);
  20234. },
  20235. // Get a model from the set by id.
  20236. get: function(obj) {
  20237. if (obj == null) return void 0;
  20238. return this._byId[obj] || this._byId[obj.id] || this._byId[obj.cid];
  20239. },
  20240. // Get the model at the given index.
  20241. at: function(index) {
  20242. return this.models[index];
  20243. },
  20244. // Return models with matching attributes. Useful for simple cases of
  20245. // `filter`.
  20246. where: function(attrs, first) {
  20247. if (_.isEmpty(attrs)) return first ? void 0 : [];
  20248. return this[first ? 'find' : 'filter'](function(model) {
  20249. for (var key in attrs) {
  20250. if (attrs[key] !== model.get(key)) return false;
  20251. }
  20252. return true;
  20253. });
  20254. },
  20255. // Return the first model with matching attributes. Useful for simple cases
  20256. // of `find`.
  20257. findWhere: function(attrs) {
  20258. return this.where(attrs, true);
  20259. },
  20260. // Force the collection to re-sort itself. You don't need to call this under
  20261. // normal circumstances, as the set will maintain sort order as each item
  20262. // is added.
  20263. sort: function(options) {
  20264. if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
  20265. options || (options = {});
  20266. // Run sort based on type of `comparator`.
  20267. if (_.isString(this.comparator) || this.comparator.length === 1) {
  20268. this.models = this.sortBy(this.comparator, this);
  20269. } else {
  20270. this.models.sort(_.bind(this.comparator, this));
  20271. }
  20272. if (!options.silent) this.trigger('sort', this, options);
  20273. return this;
  20274. },
  20275. // Pluck an attribute from each model in the collection.
  20276. pluck: function(attr) {
  20277. return _.invoke(this.models, 'get', attr);
  20278. },
  20279. // Fetch the default set of models for this collection, resetting the
  20280. // collection when they arrive. If `reset: true` is passed, the response
  20281. // data will be passed through the `reset` method instead of `set`.
  20282. fetch: function(options) {
  20283. options = options ? _.clone(options) : {};
  20284. if (options.parse === void 0) options.parse = true;
  20285. var success = options.success;
  20286. var collection = this;
  20287. options.success = function(resp) {
  20288. var method = options.reset ? 'reset' : 'set';
  20289. collection[method](resp, options);
  20290. if (success) success(collection, resp, options);
  20291. collection.trigger('sync', collection, resp, options);
  20292. };
  20293. wrapError(this, options);
  20294. return this.sync('read', this, options);
  20295. },
  20296. // Create a new instance of a model in this collection. Add the model to the
  20297. // collection immediately, unless `wait: true` is passed, in which case we
  20298. // wait for the server to agree.
  20299. create: function(model, options) {
  20300. options = options ? _.clone(options) : {};
  20301. if (!(model = this._prepareModel(model, options))) return false;
  20302. if (!options.wait) this.add(model, options);
  20303. var collection = this;
  20304. var success = options.success;
  20305. options.success = function(model, resp) {
  20306. if (options.wait) collection.add(model, options);
  20307. if (success) success(model, resp, options);
  20308. };
  20309. model.save(null, options);
  20310. return model;
  20311. },
  20312. // **parse** converts a response into a list of models to be added to the
  20313. // collection. The default implementation is just to pass it through.
  20314. parse: function(resp, options) {
  20315. return resp;
  20316. },
  20317. // Create a new collection with an identical list of models as this one.
  20318. clone: function() {
  20319. return new this.constructor(this.models);
  20320. },
  20321. // Private method to reset all internal state. Called when the collection
  20322. // is first initialized or reset.
  20323. _reset: function() {
  20324. this.length = 0;
  20325. this.models = [];
  20326. this._byId = {};
  20327. },
  20328. // Prepare a hash of attributes (or other model) to be added to this
  20329. // collection.
  20330. _prepareModel: function(attrs, options) {
  20331. if (attrs instanceof Model) return attrs;
  20332. options = options ? _.clone(options) : {};
  20333. options.collection = this;
  20334. var model = new this.model(attrs, options);
  20335. if (!model.validationError) return model;
  20336. this.trigger('invalid', this, model.validationError, options);
  20337. return false;
  20338. },
  20339. // Internal method to create a model's ties to a collection.
  20340. _addReference: function(model, options) {
  20341. this._byId[model.cid] = model;
  20342. if (model.id != null) this._byId[model.id] = model;
  20343. if (!model.collection) model.collection = this;
  20344. model.on('all', this._onModelEvent, this);
  20345. },
  20346. // Internal method to sever a model's ties to a collection.
  20347. _removeReference: function(model, options) {
  20348. if (this === model.collection) delete model.collection;
  20349. model.off('all', this._onModelEvent, this);
  20350. },
  20351. // Internal method called every time a model in the set fires an event.
  20352. // Sets need to update their indexes when models change ids. All other
  20353. // events simply proxy through. "add" and "remove" events that originate
  20354. // in other collections are ignored.
  20355. _onModelEvent: function(event, model, collection, options) {
  20356. if ((event === 'add' || event === 'remove') && collection !== this) return;
  20357. if (event === 'destroy') this.remove(model, options);
  20358. if (model && event === 'change:' + model.idAttribute) {
  20359. delete this._byId[model.previous(model.idAttribute)];
  20360. if (model.id != null) this._byId[model.id] = model;
  20361. }
  20362. this.trigger.apply(this, arguments);
  20363. }
  20364. });
  20365. // Underscore methods that we want to implement on the Collection.
  20366. // 90% of the core usefulness of Backbone Collections is actually implemented
  20367. // right here:
  20368. var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
  20369. 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
  20370. 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
  20371. 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
  20372. 'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle',
  20373. 'lastIndexOf', 'isEmpty', 'chain', 'sample'];
  20374. // Mix in each Underscore method as a proxy to `Collection#models`.
  20375. _.each(methods, function(method) {
  20376. Collection.prototype[method] = function() {
  20377. var args = slice.call(arguments);
  20378. args.unshift(this.models);
  20379. return _[method].apply(_, args);
  20380. };
  20381. });
  20382. // Underscore methods that take a property name as an argument.
  20383. var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy'];
  20384. // Use attributes instead of properties.
  20385. _.each(attributeMethods, function(method) {
  20386. Collection.prototype[method] = function(value, context) {
  20387. var iterator = _.isFunction(value) ? value : function(model) {
  20388. return model.get(value);
  20389. };
  20390. return _[method](this.models, iterator, context);
  20391. };
  20392. });
  20393. // Backbone.View
  20394. // -------------
  20395. // Backbone Views are almost more convention than they are actual code. A View
  20396. // is simply a JavaScript object that represents a logical chunk of UI in the
  20397. // DOM. This might be a single item, an entire list, a sidebar or panel, or
  20398. // even the surrounding frame which wraps your whole app. Defining a chunk of
  20399. // UI as a **View** allows you to define your DOM events declaratively, without
  20400. // having to worry about render order ... and makes it easy for the view to
  20401. // react to specific changes in the state of your models.
  20402. // Creating a Backbone.View creates its initial element outside of the DOM,
  20403. // if an existing element is not provided...
  20404. var View = Backbone.View = function(options) {
  20405. this.cid = _.uniqueId('view');
  20406. options || (options = {});
  20407. _.extend(this, _.pick(options, viewOptions));
  20408. this._ensureElement();
  20409. this.initialize.apply(this, arguments);
  20410. this.delegateEvents();
  20411. };
  20412. // Cached regex to split keys for `delegate`.
  20413. var delegateEventSplitter = /^(\S+)\s*(.*)$/;
  20414. // List of view options to be merged as properties.
  20415. var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
  20416. // Set up all inheritable **Backbone.View** properties and methods.
  20417. _.extend(View.prototype, Events, {
  20418. // The default `tagName` of a View's element is `"div"`.
  20419. tagName: 'div',
  20420. // jQuery delegate for element lookup, scoped to DOM elements within the
  20421. // current view. This should be preferred to global lookups where possible.
  20422. $: function(selector) {
  20423. return this.$el.find(selector);
  20424. },
  20425. // Initialize is an empty function by default. Override it with your own
  20426. // initialization logic.
  20427. initialize: function(){},
  20428. // **render** is the core function that your view should override, in order
  20429. // to populate its element (`this.el`), with the appropriate HTML. The
  20430. // convention is for **render** to always return `this`.
  20431. render: function() {
  20432. return this;
  20433. },
  20434. // Remove this view by taking the element out of the DOM, and removing any
  20435. // applicable Backbone.Events listeners.
  20436. remove: function() {
  20437. this.$el.remove();
  20438. this.stopListening();
  20439. return this;
  20440. },
  20441. // Change the view's element (`this.el` property), including event
  20442. // re-delegation.
  20443. setElement: function(element, delegate) {
  20444. if (this.$el) this.undelegateEvents();
  20445. this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
  20446. this.el = this.$el[0];
  20447. if (delegate !== false) this.delegateEvents();
  20448. return this;
  20449. },
  20450. // Set callbacks, where `this.events` is a hash of
  20451. //
  20452. // *{"event selector": "callback"}*
  20453. //
  20454. // {
  20455. // 'mousedown .title': 'edit',
  20456. // 'click .button': 'save',
  20457. // 'click .open': function(e) { ... }
  20458. // }
  20459. //
  20460. // pairs. Callbacks will be bound to the view, with `this` set properly.
  20461. // Uses event delegation for efficiency.
  20462. // Omitting the selector binds the event to `this.el`.
  20463. // This only works for delegate-able events: not `focus`, `blur`, and
  20464. // not `change`, `submit`, and `reset` in Internet Explorer.
  20465. delegateEvents: function(events) {
  20466. if (!(events || (events = _.result(this, 'events')))) return this;
  20467. this.undelegateEvents();
  20468. for (var key in events) {
  20469. var method = events[key];
  20470. if (!_.isFunction(method)) method = this[events[key]];
  20471. if (!method) continue;
  20472. var match = key.match(delegateEventSplitter);
  20473. var eventName = match[1], selector = match[2];
  20474. method = _.bind(method, this);
  20475. eventName += '.delegateEvents' + this.cid;
  20476. if (selector === '') {
  20477. this.$el.on(eventName, method);
  20478. } else {
  20479. this.$el.on(eventName, selector, method);
  20480. }
  20481. }
  20482. return this;
  20483. },
  20484. // Clears all callbacks previously bound to the view with `delegateEvents`.
  20485. // You usually don't need to use this, but may wish to if you have multiple
  20486. // Backbone views attached to the same DOM element.
  20487. undelegateEvents: function() {
  20488. this.$el.off('.delegateEvents' + this.cid);
  20489. return this;
  20490. },
  20491. // Ensure that the View has a DOM element to render into.
  20492. // If `this.el` is a string, pass it through `$()`, take the first
  20493. // matching element, and re-assign it to `el`. Otherwise, create
  20494. // an element from the `id`, `className` and `tagName` properties.
  20495. _ensureElement: function() {
  20496. if (!this.el) {
  20497. var attrs = _.extend({}, _.result(this, 'attributes'));
  20498. if (this.id) attrs.id = _.result(this, 'id');
  20499. if (this.className) attrs['class'] = _.result(this, 'className');
  20500. var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
  20501. this.setElement($el, false);
  20502. } else {
  20503. this.setElement(_.result(this, 'el'), false);
  20504. }
  20505. }
  20506. });
  20507. // Backbone.sync
  20508. // -------------
  20509. // Override this function to change the manner in which Backbone persists
  20510. // models to the server. You will be passed the type of request, and the
  20511. // model in question. By default, makes a RESTful Ajax request
  20512. // to the model's `url()`. Some possible customizations could be:
  20513. //
  20514. // * Use `setTimeout` to batch rapid-fire updates into a single request.
  20515. // * Send up the models as XML instead of JSON.
  20516. // * Persist models via WebSockets instead of Ajax.
  20517. //
  20518. // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
  20519. // as `POST`, with a `_method` parameter containing the true HTTP method,
  20520. // as well as all requests with the body as `application/x-www-form-urlencoded`
  20521. // instead of `application/json` with the model in a param named `model`.
  20522. // Useful when interfacing with server-side languages like **PHP** that make
  20523. // it difficult to read the body of `PUT` requests.
  20524. Backbone.sync = function(method, model, options) {
  20525. var type = methodMap[method];
  20526. // Default options, unless specified.
  20527. _.defaults(options || (options = {}), {
  20528. emulateHTTP: Backbone.emulateHTTP,
  20529. emulateJSON: Backbone.emulateJSON
  20530. });
  20531. // Default JSON-request options.
  20532. var params = {type: type, dataType: 'json'};
  20533. // Ensure that we have a URL.
  20534. if (!options.url) {
  20535. params.url = _.result(model, 'url') || urlError();
  20536. }
  20537. // Ensure that we have the appropriate request data.
  20538. if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
  20539. params.contentType = 'application/json';
  20540. params.data = JSON.stringify(options.attrs || model.toJSON(options));
  20541. }
  20542. // For older servers, emulate JSON by encoding the request into an HTML-form.
  20543. if (options.emulateJSON) {
  20544. params.contentType = 'application/x-www-form-urlencoded';
  20545. params.data = params.data ? {model: params.data} : {};
  20546. }
  20547. // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
  20548. // And an `X-HTTP-Method-Override` header.
  20549. if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
  20550. params.type = 'POST';
  20551. if (options.emulateJSON) params.data._method = type;
  20552. var beforeSend = options.beforeSend;
  20553. options.beforeSend = function(xhr) {
  20554. xhr.setRequestHeader('X-HTTP-Method-Override', type);
  20555. if (beforeSend) return beforeSend.apply(this, arguments);
  20556. };
  20557. }
  20558. // Don't process data on a non-GET request.
  20559. if (params.type !== 'GET' && !options.emulateJSON) {
  20560. params.processData = false;
  20561. }
  20562. // If we're sending a `PATCH` request, and we're in an old Internet Explorer
  20563. // that still has ActiveX enabled by default, override jQuery to use that
  20564. // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8.
  20565. if (params.type === 'PATCH' && noXhrPatch) {
  20566. params.xhr = function() {
  20567. return new ActiveXObject("Microsoft.XMLHTTP");
  20568. };
  20569. }
  20570. // Make the request, allowing the user to override any Ajax options.
  20571. var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
  20572. model.trigger('request', model, xhr, options);
  20573. return xhr;
  20574. };
  20575. var noXhrPatch =
  20576. typeof window !== 'undefined' && !!window.ActiveXObject &&
  20577. !(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent);
  20578. // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
  20579. var methodMap = {
  20580. 'create': 'POST',
  20581. 'update': 'PUT',
  20582. 'patch': 'PATCH',
  20583. 'delete': 'DELETE',
  20584. 'read': 'GET'
  20585. };
  20586. // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
  20587. // Override this if you'd like to use a different library.
  20588. Backbone.ajax = function() {
  20589. return Backbone.$.ajax.apply(Backbone.$, arguments);
  20590. };
  20591. // Backbone.Router
  20592. // ---------------
  20593. // Routers map faux-URLs to actions, and fire events when routes are
  20594. // matched. Creating a new one sets its `routes` hash, if not set statically.
  20595. var Router = Backbone.Router = function(options) {
  20596. options || (options = {});
  20597. if (options.routes) this.routes = options.routes;
  20598. this._bindRoutes();
  20599. this.initialize.apply(this, arguments);
  20600. };
  20601. // Cached regular expressions for matching named param parts and splatted
  20602. // parts of route strings.
  20603. var optionalParam = /\((.*?)\)/g;
  20604. var namedParam = /(\(\?)?:\w+/g;
  20605. var splatParam = /\*\w+/g;
  20606. var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
  20607. // Set up all inheritable **Backbone.Router** properties and methods.
  20608. _.extend(Router.prototype, Events, {
  20609. // Initialize is an empty function by default. Override it with your own
  20610. // initialization logic.
  20611. initialize: function(){},
  20612. // Manually bind a single named route to a callback. For example:
  20613. //
  20614. // this.route('search/:query/p:num', 'search', function(query, num) {
  20615. // ...
  20616. // });
  20617. //
  20618. route: function(route, name, callback) {
  20619. if (!_.isRegExp(route)) route = this._routeToRegExp(route);
  20620. if (_.isFunction(name)) {
  20621. callback = name;
  20622. name = '';
  20623. }
  20624. if (!callback) callback = this[name];
  20625. var router = this;
  20626. Backbone.history.route(route, function(fragment) {
  20627. var args = router._extractParameters(route, fragment);
  20628. router.execute(callback, args);
  20629. router.trigger.apply(router, ['route:' + name].concat(args));
  20630. router.trigger('route', name, args);
  20631. Backbone.history.trigger('route', router, name, args);
  20632. });
  20633. return this;
  20634. },
  20635. // Execute a route handler with the provided parameters. This is an
  20636. // excellent place to do pre-route setup or post-route cleanup.
  20637. execute: function(callback, args) {
  20638. if (callback) callback.apply(this, args);
  20639. },
  20640. // Simple proxy to `Backbone.history` to save a fragment into the history.
  20641. navigate: function(fragment, options) {
  20642. Backbone.history.navigate(fragment, options);
  20643. return this;
  20644. },
  20645. // Bind all defined routes to `Backbone.history`. We have to reverse the
  20646. // order of the routes here to support behavior where the most general
  20647. // routes can be defined at the bottom of the route map.
  20648. _bindRoutes: function() {
  20649. if (!this.routes) return;
  20650. this.routes = _.result(this, 'routes');
  20651. var route, routes = _.keys(this.routes);
  20652. while ((route = routes.pop()) != null) {
  20653. this.route(route, this.routes[route]);
  20654. }
  20655. },
  20656. // Convert a route string into a regular expression, suitable for matching
  20657. // against the current location hash.
  20658. _routeToRegExp: function(route) {
  20659. route = route.replace(escapeRegExp, '\\$&')
  20660. .replace(optionalParam, '(?:$1)?')
  20661. .replace(namedParam, function(match, optional) {
  20662. return optional ? match : '([^/?]+)';
  20663. })
  20664. .replace(splatParam, '([^?]*?)');
  20665. return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
  20666. },
  20667. // Given a route, and a URL fragment that it matches, return the array of
  20668. // extracted decoded parameters. Empty or unmatched parameters will be
  20669. // treated as `null` to normalize cross-browser behavior.
  20670. _extractParameters: function(route, fragment) {
  20671. var params = route.exec(fragment).slice(1);
  20672. return _.map(params, function(param, i) {
  20673. // Don't decode the search params.
  20674. if (i === params.length - 1) return param || null;
  20675. return param ? decodeURIComponent(param) : null;
  20676. });
  20677. }
  20678. });
  20679. // Backbone.History
  20680. // ----------------
  20681. // Handles cross-browser history management, based on either
  20682. // [pushState](http://diveintohtml5.info/history.html) and real URLs, or
  20683. // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
  20684. // and URL fragments. If the browser supports neither (old IE, natch),
  20685. // falls back to polling.
  20686. var History = Backbone.History = function() {
  20687. this.handlers = [];
  20688. _.bindAll(this, 'checkUrl');
  20689. // Ensure that `History` can be used outside of the browser.
  20690. if (typeof window !== 'undefined') {
  20691. this.location = window.location;
  20692. this.history = window.history;
  20693. }
  20694. };
  20695. // Cached regex for stripping a leading hash/slash and trailing space.
  20696. var routeStripper = /^[#\/]|\s+$/g;
  20697. // Cached regex for stripping leading and trailing slashes.
  20698. var rootStripper = /^\/+|\/+$/g;
  20699. // Cached regex for detecting MSIE.
  20700. var isExplorer = /msie [\w.]+/;
  20701. // Cached regex for removing a trailing slash.
  20702. var trailingSlash = /\/$/;
  20703. // Cached regex for stripping urls of hash.
  20704. var pathStripper = /#.*$/;
  20705. // Has the history handling already been started?
  20706. History.started = false;
  20707. // Set up all inheritable **Backbone.History** properties and methods.
  20708. _.extend(History.prototype, Events, {
  20709. // The default interval to poll for hash changes, if necessary, is
  20710. // twenty times a second.
  20711. interval: 50,
  20712. // Are we at the app root?
  20713. atRoot: function() {
  20714. return this.location.pathname.replace(/[^\/]$/, '$&/') === this.root;
  20715. },
  20716. // Gets the true hash value. Cannot use location.hash directly due to bug
  20717. // in Firefox where location.hash will always be decoded.
  20718. getHash: function(window) {
  20719. var match = (window || this).location.href.match(/#(.*)$/);
  20720. return match ? match[1] : '';
  20721. },
  20722. // Get the cross-browser normalized URL fragment, either from the URL,
  20723. // the hash, or the override.
  20724. getFragment: function(fragment, forcePushState) {
  20725. if (fragment == null) {
  20726. if (this._hasPushState || !this._wantsHashChange || forcePushState) {
  20727. fragment = decodeURI(this.location.pathname + this.location.search);
  20728. var root = this.root.replace(trailingSlash, '');
  20729. if (!fragment.indexOf(root)) fragment = fragment.slice(root.length);
  20730. } else {
  20731. fragment = this.getHash();
  20732. }
  20733. }
  20734. return fragment.replace(routeStripper, '');
  20735. },
  20736. // Start the hash change handling, returning `true` if the current URL matches
  20737. // an existing route, and `false` otherwise.
  20738. start: function(options) {
  20739. if (History.started) throw new Error("Backbone.history has already been started");
  20740. History.started = true;
  20741. // Figure out the initial configuration. Do we need an iframe?
  20742. // Is pushState desired ... is it available?
  20743. this.options = _.extend({root: '/'}, this.options, options);
  20744. this.root = this.options.root;
  20745. this._wantsHashChange = this.options.hashChange !== false;
  20746. this._wantsPushState = !!this.options.pushState;
  20747. this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState);
  20748. var fragment = this.getFragment();
  20749. var docMode = document.documentMode;
  20750. var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
  20751. // Normalize root to always include a leading and trailing slash.
  20752. this.root = ('/' + this.root + '/').replace(rootStripper, '/');
  20753. if (oldIE && this._wantsHashChange) {
  20754. var frame = Backbone.$('<iframe src="javascript:0" tabindex="-1">');
  20755. this.iframe = frame.hide().appendTo('body')[0].contentWindow;
  20756. this.navigate(fragment);
  20757. }
  20758. // Depending on whether we're using pushState or hashes, and whether
  20759. // 'onhashchange' is supported, determine how we check the URL state.
  20760. if (this._hasPushState) {
  20761. Backbone.$(window).on('popstate', this.checkUrl);
  20762. } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
  20763. Backbone.$(window).on('hashchange', this.checkUrl);
  20764. } else if (this._wantsHashChange) {
  20765. this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
  20766. }
  20767. // Determine if we need to change the base url, for a pushState link
  20768. // opened by a non-pushState browser.
  20769. this.fragment = fragment;
  20770. var loc = this.location;
  20771. // Transition from hashChange to pushState or vice versa if both are
  20772. // requested.
  20773. if (this._wantsHashChange && this._wantsPushState) {
  20774. // If we've started off with a route from a `pushState`-enabled
  20775. // browser, but we're currently in a browser that doesn't support it...
  20776. if (!this._hasPushState && !this.atRoot()) {
  20777. this.fragment = this.getFragment(null, true);
  20778. this.location.replace(this.root + '#' + this.fragment);
  20779. // Return immediately as browser will do redirect to new url
  20780. return true;
  20781. // Or if we've started out with a hash-based route, but we're currently
  20782. // in a browser where it could be `pushState`-based instead...
  20783. } else if (this._hasPushState && this.atRoot() && loc.hash) {
  20784. this.fragment = this.getHash().replace(routeStripper, '');
  20785. this.history.replaceState({}, document.title, this.root + this.fragment);
  20786. }
  20787. }
  20788. if (!this.options.silent) return this.loadUrl();
  20789. },
  20790. // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
  20791. // but possibly useful for unit testing Routers.
  20792. stop: function() {
  20793. Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
  20794. if (this._checkUrlInterval) clearInterval(this._checkUrlInterval);
  20795. History.started = false;
  20796. },
  20797. // Add a route to be tested when the fragment changes. Routes added later
  20798. // may override previous routes.
  20799. route: function(route, callback) {
  20800. this.handlers.unshift({route: route, callback: callback});
  20801. },
  20802. // Checks the current URL to see if it has changed, and if it has,
  20803. // calls `loadUrl`, normalizing across the hidden iframe.
  20804. checkUrl: function(e) {
  20805. var current = this.getFragment();
  20806. if (current === this.fragment && this.iframe) {
  20807. current = this.getFragment(this.getHash(this.iframe));
  20808. }
  20809. if (current === this.fragment) return false;
  20810. if (this.iframe) this.navigate(current);
  20811. this.loadUrl();
  20812. },
  20813. // Attempt to load the current URL fragment. If a route succeeds with a
  20814. // match, returns `true`. If no defined routes matches the fragment,
  20815. // returns `false`.
  20816. loadUrl: function(fragment) {
  20817. fragment = this.fragment = this.getFragment(fragment);
  20818. return _.any(this.handlers, function(handler) {
  20819. if (handler.route.test(fragment)) {
  20820. handler.callback(fragment);
  20821. return true;
  20822. }
  20823. });
  20824. },
  20825. // Save a fragment into the hash history, or replace the URL state if the
  20826. // 'replace' option is passed. You are responsible for properly URL-encoding
  20827. // the fragment in advance.
  20828. //
  20829. // The options object can contain `trigger: true` if you wish to have the
  20830. // route callback be fired (not usually desirable), or `replace: true`, if
  20831. // you wish to modify the current URL without adding an entry to the history.
  20832. navigate: function(fragment, options) {
  20833. if (!History.started) return false;
  20834. if (!options || options === true) options = {trigger: !!options};
  20835. var url = this.root + (fragment = this.getFragment(fragment || ''));
  20836. // Strip the hash for matching.
  20837. fragment = fragment.replace(pathStripper, '');
  20838. if (this.fragment === fragment) return;
  20839. this.fragment = fragment;
  20840. // Don't include a trailing slash on the root.
  20841. if (fragment === '' && url !== '/') url = url.slice(0, -1);
  20842. // If pushState is available, we use it to set the fragment as a real URL.
  20843. if (this._hasPushState) {
  20844. this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
  20845. // If hash changes haven't been explicitly disabled, update the hash
  20846. // fragment to store history.
  20847. } else if (this._wantsHashChange) {
  20848. this._updateHash(this.location, fragment, options.replace);
  20849. if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
  20850. // Opening and closing the iframe tricks IE7 and earlier to push a
  20851. // history entry on hash-tag change. When replace is true, we don't
  20852. // want this.
  20853. if(!options.replace) this.iframe.document.open().close();
  20854. this._updateHash(this.iframe.location, fragment, options.replace);
  20855. }
  20856. // If you've told us that you explicitly don't want fallback hashchange-
  20857. // based history, then `navigate` becomes a page refresh.
  20858. } else {
  20859. return this.location.assign(url);
  20860. }
  20861. if (options.trigger) return this.loadUrl(fragment);
  20862. },
  20863. // Update the hash location, either replacing the current entry, or adding
  20864. // a new one to the browser history.
  20865. _updateHash: function(location, fragment, replace) {
  20866. if (replace) {
  20867. var href = location.href.replace(/(javascript:|#).*$/, '');
  20868. location.replace(href + '#' + fragment);
  20869. } else {
  20870. // Some browsers require that `hash` contains a leading #.
  20871. location.hash = '#' + fragment;
  20872. }
  20873. }
  20874. });
  20875. // Create the default Backbone.history.
  20876. Backbone.history = new History;
  20877. // Helpers
  20878. // -------
  20879. // Helper function to correctly set up the prototype chain, for subclasses.
  20880. // Similar to `goog.inherits`, but uses a hash of prototype properties and
  20881. // class properties to be extended.
  20882. var extend = function(protoProps, staticProps) {
  20883. var parent = this;
  20884. var child;
  20885. // The constructor function for the new subclass is either defined by you
  20886. // (the "constructor" property in your `extend` definition), or defaulted
  20887. // by us to simply call the parent's constructor.
  20888. if (protoProps && _.has(protoProps, 'constructor')) {
  20889. child = protoProps.constructor;
  20890. } else {
  20891. child = function(){ return parent.apply(this, arguments); };
  20892. }
  20893. // Add static properties to the constructor function, if supplied.
  20894. _.extend(child, parent, staticProps);
  20895. // Set the prototype chain to inherit from `parent`, without calling
  20896. // `parent`'s constructor function.
  20897. var Surrogate = function(){ this.constructor = child; };
  20898. Surrogate.prototype = parent.prototype;
  20899. child.prototype = new Surrogate;
  20900. // Add prototype properties (instance properties) to the subclass,
  20901. // if supplied.
  20902. if (protoProps) _.extend(child.prototype, protoProps);
  20903. // Set a convenience property in case the parent's prototype is needed
  20904. // later.
  20905. child.__super__ = parent.prototype;
  20906. return child;
  20907. };
  20908. // Set up inheritance for the model, collection, router, view and history.
  20909. Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
  20910. // Throw an error when a URL is needed, and none is supplied.
  20911. var urlError = function() {
  20912. throw new Error('A "url" property or function must be specified');
  20913. };
  20914. // Wrap an optional error callback with a fallback error event.
  20915. var wrapError = function(model, options) {
  20916. var error = options.error;
  20917. options.error = function(resp) {
  20918. if (error) error(model, resp, options);
  20919. model.trigger('error', model, resp, options);
  20920. };
  20921. };
  20922. return Backbone;
  20923. }));
  20924. /**
  20925. * Backbone localStorage and sessionStorage Adapter
  20926. * Version 0.0.1
  20927. *
  20928. * https://github.com/jcbrand/Backbone.browserStorage
  20929. */
  20930. (function (root, factory) {
  20931. if (typeof exports === 'object' && typeof require === 'function') {
  20932. module.exports = factory(require("backbone"), require('underscore'));
  20933. } else if (typeof define === "function" && define.amd) {
  20934. // AMD. Register as an anonymous module.
  20935. define('backbone.browserStorage',["backbone", "underscore"], function(Backbone, _) {
  20936. // Use global variables if the locals are undefined.
  20937. return factory(Backbone || root.Backbone, _ || root._);
  20938. });
  20939. } else {
  20940. factory(Backbone, _);
  20941. }
  20942. }(this, function(Backbone, _) {
  20943. // A simple module to replace `Backbone.sync` with *browser storage*-based
  20944. // persistence. Models are given GUIDS, and saved into a JSON object. Simple
  20945. // as that.
  20946. // Hold reference to Underscore.js and Backbone.js in the closure in order
  20947. // to make things work even if they are removed from the global namespace
  20948. // Generate four random hex digits.
  20949. function S4() {
  20950. return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
  20951. }
  20952. // Generate a pseudo-GUID by concatenating random hexadecimal.
  20953. function guid() {
  20954. return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
  20955. }
  20956. function contains(array, item) {
  20957. var i = array.length;
  20958. while (i--) if (array[i] === item) return true;
  20959. return false;
  20960. }
  20961. function extend(obj, props) {
  20962. for (var key in props) { obj[key] = props[key]; }
  20963. return obj;
  20964. }
  20965. function _browserStorage (name, serializer, type) {
  20966. var _store;
  20967. if (type === 'local' && !window.localStorage ) {
  20968. throw "Backbone.browserStorage: Environment does not support localStorage.";
  20969. } else if (type === 'session' && !window.sessionStorage ) {
  20970. throw "Backbone.browserStorage: Environment does not support sessionStorage.";
  20971. }
  20972. this.name = name;
  20973. this.serializer = serializer || {
  20974. serialize: function(item) {
  20975. return _.isObject(item) ? JSON.stringify(item) : item;
  20976. },
  20977. // fix for "illegal access" error on Android when JSON.parse is passed null
  20978. deserialize: function (data) {
  20979. return data && JSON.parse(data);
  20980. }
  20981. };
  20982. if (type === 'session') {
  20983. this.store = window.sessionStorage;
  20984. } else if (type === 'local') {
  20985. this.store = window.localStorage;
  20986. } else {
  20987. throw "Backbone.browserStorage: No storage type was specified";
  20988. }
  20989. _store = this.store.getItem(this.name);
  20990. this.records = (_store && _store.split(",")) || [];
  20991. }
  20992. // Our Store is represented by a single JS object in *localStorage* or *sessionStorage*.
  20993. // Create it with a meaningful name, like the name you'd give a table.
  20994. Backbone.BrowserStorage = {
  20995. local: function (name, serializer) {
  20996. return _browserStorage.bind(this, name, serializer, 'local')();
  20997. },
  20998. session: function (name, serializer) {
  20999. return _browserStorage.bind(this, name, serializer, 'session')();
  21000. }
  21001. };
  21002. // The browser's local and session stores will be extended with this obj.
  21003. var _extension = {
  21004. // Save the current state of the **Store**
  21005. save: function() {
  21006. this.store.setItem(this.name, this.records.join(","));
  21007. },
  21008. // Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
  21009. // have an id of it's own.
  21010. create: function(model) {
  21011. if (!model.id) {
  21012. model.id = guid();
  21013. model.set(model.idAttribute, model.id);
  21014. }
  21015. this.store.setItem(this._itemName(model.id), this.serializer.serialize(model));
  21016. this.records.push(model.id.toString());
  21017. this.save();
  21018. return this.find(model) !== false;
  21019. },
  21020. // Update a model by replacing its copy in `this.data`.
  21021. update: function(model) {
  21022. this.store.setItem(this._itemName(model.id), this.serializer.serialize(model));
  21023. var modelId = model.id.toString();
  21024. if (!contains(this.records, modelId)) {
  21025. this.records.push(modelId);
  21026. this.save();
  21027. }
  21028. return this.find(model) !== false;
  21029. },
  21030. // Retrieve a model from `this.data` by id.
  21031. find: function(model) {
  21032. return this.serializer.deserialize(this.store.getItem(this._itemName(model.id)));
  21033. },
  21034. // Return the array of all models currently in storage.
  21035. findAll: function() {
  21036. var result = [];
  21037. for (var i = 0, id, data; i < this.records.length; i++) {
  21038. id = this.records[i];
  21039. data = this.serializer.deserialize(this.store.getItem(this._itemName(id)));
  21040. if (data !== null) result.push(data);
  21041. }
  21042. return result;
  21043. },
  21044. // Delete a model from `this.data`, returning it.
  21045. destroy: function(model) {
  21046. this.store.removeItem(this._itemName(model.id));
  21047. var modelId = model.id.toString();
  21048. for (var i = 0, id; i < this.records.length; i++) {
  21049. if (this.records[i] === modelId) {
  21050. this.records.splice(i, 1);
  21051. }
  21052. }
  21053. this.save();
  21054. return model;
  21055. },
  21056. browserStorage: function() {
  21057. return {
  21058. session: sessionStorage,
  21059. local: localStorage
  21060. };
  21061. },
  21062. // Clear browserStorage for specific collection.
  21063. _clear: function() {
  21064. var local = this.store,
  21065. itemRe = new RegExp("^" + this.name + "-");
  21066. // Remove id-tracking item (e.g., "foo").
  21067. local.removeItem(this.name);
  21068. // Match all data items (e.g., "foo-ID") and remove.
  21069. for (var k in local) {
  21070. if (itemRe.test(k)) {
  21071. local.removeItem(k);
  21072. }
  21073. }
  21074. this.records.length = 0;
  21075. },
  21076. // Size of browserStorage.
  21077. _storageSize: function() {
  21078. return this.store.length;
  21079. },
  21080. _itemName: function(id) {
  21081. return this.name+"-"+id;
  21082. }
  21083. };
  21084. extend(Backbone.BrowserStorage.session.prototype, _extension);
  21085. extend(Backbone.BrowserStorage.local.prototype, _extension);
  21086. // localSync delegate to the model or collection's
  21087. // *browserStorage* property, which should be an instance of `Store`.
  21088. // window.Store.sync and Backbone.localSync is deprecated, use Backbone.BrowserStorage.sync instead
  21089. Backbone.BrowserStorage.sync = Backbone.localSync = function(method, model, options) {
  21090. var store = model.browserStorage || model.collection.browserStorage;
  21091. var resp, errorMessage;
  21092. //If $ is having Deferred - use it.
  21093. var syncDfd = Backbone.$ ?
  21094. (Backbone.$.Deferred && Backbone.$.Deferred()) :
  21095. (Backbone.Deferred && Backbone.Deferred());
  21096. try {
  21097. switch (method) {
  21098. case "read":
  21099. resp = model.id !== undefined ? store.find(model) : store.findAll();
  21100. break;
  21101. case "create":
  21102. resp = store.create(model);
  21103. break;
  21104. case "update":
  21105. resp = store.update(model);
  21106. break;
  21107. case "delete":
  21108. resp = store.destroy(model);
  21109. break;
  21110. }
  21111. } catch(error) {
  21112. if (error.code === 22 && store._storageSize() === 0)
  21113. errorMessage = "Private browsing is unsupported";
  21114. else
  21115. errorMessage = error.message;
  21116. }
  21117. if (resp) {
  21118. if (options && options.success) {
  21119. if (Backbone.VERSION === "0.9.10") {
  21120. options.success(model, resp, options);
  21121. } else {
  21122. options.success(resp);
  21123. }
  21124. }
  21125. if (syncDfd) {
  21126. syncDfd.resolve(resp);
  21127. }
  21128. } else {
  21129. errorMessage = errorMessage ? errorMessage
  21130. : "Record Not Found";
  21131. if (options && options.error)
  21132. if (Backbone.VERSION === "0.9.10") {
  21133. options.error(model, errorMessage, options);
  21134. } else {
  21135. options.error(errorMessage);
  21136. }
  21137. if (syncDfd)
  21138. syncDfd.reject(errorMessage);
  21139. }
  21140. // add compatibility with $.ajax
  21141. // always execute callback for success and error
  21142. if (options && options.complete) options.complete(resp);
  21143. return syncDfd && syncDfd.promise();
  21144. };
  21145. Backbone.ajaxSync = Backbone.sync;
  21146. Backbone.getSyncMethod = function(model) {
  21147. if(model.browserStorage || (model.collection && model.collection.browserStorage)) {
  21148. return Backbone.localSync;
  21149. }
  21150. return Backbone.ajaxSync;
  21151. };
  21152. // Override 'Backbone.sync' to default to localSync,
  21153. // the original 'Backbone.sync' is still available in 'Backbone.ajaxSync'
  21154. Backbone.sync = function(method, model, options) {
  21155. return Backbone.getSyncMethod(model).apply(this, [method, model, options]);
  21156. };
  21157. return Backbone.BrowserStorage;
  21158. }));
  21159. /*!
  21160. * Backbone.Overview
  21161. *
  21162. * Copyright (c) 2014, JC Brand <jc@opkode.com>
  21163. * Licensed under the Mozilla Public License (MPL)
  21164. */
  21165. (function (root, factory) {
  21166. if (typeof define === 'function' && define.amd) {
  21167. define('backbone.overview',["underscore", "backbone"],
  21168. function(_, Backbone) {
  21169. return factory(_ || root._, Backbone || root.Backbone);
  21170. }
  21171. );
  21172. } else {
  21173. // RequireJS isn't being used.
  21174. // Assume underscore and backbone are loaded in <script> tags
  21175. factory(_, Backbone);
  21176. }
  21177. }(this, function (_, Backbone) {
  21178. var Overview = Backbone.Overview = function (options) {
  21179. /* An Overview is a View that contains and keeps track of sub-views.
  21180. * Kind of like what a Collection is to a Model.
  21181. */
  21182. var views = {};
  21183. this.keys = function () { return _.keys(views) };
  21184. this.getAll = function () { return views; };
  21185. this.get = function (id) { return views[id]; };
  21186. this.add = function (id, view) {
  21187. views[id] = view;
  21188. return view;
  21189. };
  21190. this.remove = function (id) {
  21191. var view = views[id];
  21192. if (view) {
  21193. delete views[id];
  21194. view.remove();
  21195. return view;
  21196. }
  21197. };
  21198. this.removeAll = function (id) {
  21199. _.each(_.keys(views), this.remove);
  21200. };
  21201. Backbone.View.apply(this, Array.prototype.slice.apply(arguments));
  21202. };
  21203. _.extend(Overview.prototype, Backbone.View.prototype);
  21204. Overview.extend = Backbone.View.extend;
  21205. return Backbone.Overview;
  21206. }));
  21207. /*!
  21208. * typeahead.js 0.10.5
  21209. * https://github.com/twitter/typeahead.js
  21210. * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT
  21211. */
  21212. (function (root, factory) {
  21213. if (typeof define === 'function' && define.amd) {
  21214. // AMD. Register as an anonymous module.
  21215. define('typeahead',['jquery'], function ($) {
  21216. factory($, root);
  21217. });
  21218. } else {
  21219. // Browser globals
  21220. factory(jQuery, root);
  21221. }
  21222. }(this, function($, window) {
  21223. var _ = function() {
  21224. return {
  21225. isMsie: function() {
  21226. return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
  21227. },
  21228. isBlankString: function(str) {
  21229. return !str || /^\s*$/.test(str);
  21230. },
  21231. escapeRegExChars: function(str) {
  21232. return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
  21233. },
  21234. isString: function(obj) {
  21235. return typeof obj === "string";
  21236. },
  21237. isNumber: function(obj) {
  21238. return typeof obj === "number";
  21239. },
  21240. isArray: $.isArray,
  21241. isFunction: $.isFunction,
  21242. isObject: $.isPlainObject,
  21243. isUndefined: function(obj) {
  21244. return typeof obj === "undefined";
  21245. },
  21246. toStr: function toStr(s) {
  21247. return _.isUndefined(s) || s === null ? "" : s + "";
  21248. },
  21249. bind: $.proxy,
  21250. each: function(collection, cb) {
  21251. $.each(collection, reverseArgs);
  21252. function reverseArgs(index, value) {
  21253. return cb(value, index);
  21254. }
  21255. },
  21256. map: $.map,
  21257. filter: $.grep,
  21258. every: function(obj, test) {
  21259. var result = true;
  21260. if (!obj) {
  21261. return result;
  21262. }
  21263. $.each(obj, function(key, val) {
  21264. if (!(result = test.call(null, val, key, obj))) {
  21265. return false;
  21266. }
  21267. });
  21268. return !!result;
  21269. },
  21270. some: function(obj, test) {
  21271. var result = false;
  21272. if (!obj) {
  21273. return result;
  21274. }
  21275. $.each(obj, function(key, val) {
  21276. if (result = test.call(null, val, key, obj)) {
  21277. return false;
  21278. }
  21279. });
  21280. return !!result;
  21281. },
  21282. mixin: $.extend,
  21283. getUniqueId: function() {
  21284. var counter = 0;
  21285. return function() {
  21286. return counter++;
  21287. };
  21288. }(),
  21289. templatify: function templatify(obj) {
  21290. return $.isFunction(obj) ? obj : template;
  21291. function template() {
  21292. return String(obj);
  21293. }
  21294. },
  21295. defer: function(fn) {
  21296. setTimeout(fn, 0);
  21297. },
  21298. debounce: function(func, wait, immediate) {
  21299. var timeout, result;
  21300. return function() {
  21301. var context = this, args = arguments, later, callNow;
  21302. later = function() {
  21303. timeout = null;
  21304. if (!immediate) {
  21305. result = func.apply(context, args);
  21306. }
  21307. };
  21308. callNow = immediate && !timeout;
  21309. clearTimeout(timeout);
  21310. timeout = setTimeout(later, wait);
  21311. if (callNow) {
  21312. result = func.apply(context, args);
  21313. }
  21314. return result;
  21315. };
  21316. },
  21317. throttle: function(func, wait) {
  21318. var context, args, timeout, result, previous, later;
  21319. previous = 0;
  21320. later = function() {
  21321. previous = new Date();
  21322. timeout = null;
  21323. result = func.apply(context, args);
  21324. };
  21325. return function() {
  21326. var now = new Date(), remaining = wait - (now - previous);
  21327. context = this;
  21328. args = arguments;
  21329. if (remaining <= 0) {
  21330. clearTimeout(timeout);
  21331. timeout = null;
  21332. previous = now;
  21333. result = func.apply(context, args);
  21334. } else if (!timeout) {
  21335. timeout = setTimeout(later, remaining);
  21336. }
  21337. return result;
  21338. };
  21339. },
  21340. noop: function() {}
  21341. };
  21342. }();
  21343. var html = function() {
  21344. return {
  21345. wrapper: '<span class="twitter-typeahead"></span>',
  21346. dropdown: '<span class="tt-dropdown-menu"></span>',
  21347. dataset: '<div class="tt-dataset-%CLASS%"></div>',
  21348. suggestions: '<span class="tt-suggestions"></span>',
  21349. suggestion: '<div class="tt-suggestion"></div>'
  21350. };
  21351. }();
  21352. var css = function() {
  21353. var css = {
  21354. wrapper: {
  21355. position: "relative",
  21356. display: "inline-block"
  21357. },
  21358. hint: {
  21359. position: "absolute",
  21360. top: "0",
  21361. left: "0",
  21362. borderColor: "transparent",
  21363. boxShadow: "none",
  21364. opacity: "1"
  21365. },
  21366. input: {
  21367. position: "relative",
  21368. verticalAlign: "top",
  21369. backgroundColor: "transparent"
  21370. },
  21371. inputWithNoHint: {
  21372. position: "relative",
  21373. verticalAlign: "top"
  21374. },
  21375. dropdown: {
  21376. position: "absolute",
  21377. top: "100%",
  21378. left: "0",
  21379. zIndex: "100",
  21380. display: "none"
  21381. },
  21382. suggestions: {
  21383. display: "block"
  21384. },
  21385. suggestion: {
  21386. whiteSpace: "nowrap",
  21387. cursor: "pointer"
  21388. },
  21389. suggestionChild: {
  21390. whiteSpace: "normal"
  21391. },
  21392. ltr: {
  21393. left: "0",
  21394. right: "auto"
  21395. },
  21396. rtl: {
  21397. left: "auto",
  21398. right: " 0"
  21399. }
  21400. };
  21401. if (_.isMsie()) {
  21402. _.mixin(css.input, {
  21403. backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"
  21404. });
  21405. }
  21406. if (_.isMsie() && _.isMsie() <= 7) {
  21407. _.mixin(css.input, {
  21408. marginTop: "-1px"
  21409. });
  21410. }
  21411. return css;
  21412. }();
  21413. var EventBus = function() {
  21414. var namespace = "typeahead:";
  21415. function EventBus(o) {
  21416. if (!o || !o.el) {
  21417. $.error("EventBus initialized without el");
  21418. }
  21419. this.$el = $(o.el);
  21420. }
  21421. _.mixin(EventBus.prototype, {
  21422. trigger: function(type) {
  21423. var args = [].slice.call(arguments, 1);
  21424. this.$el.trigger(namespace + type, args);
  21425. }
  21426. });
  21427. return EventBus;
  21428. }();
  21429. var EventEmitter = function() {
  21430. var splitter = /\s+/, nextTick = getNextTick();
  21431. return {
  21432. onSync: onSync,
  21433. onAsync: onAsync,
  21434. off: off,
  21435. trigger: trigger
  21436. };
  21437. function on(method, types, cb, context) {
  21438. var type;
  21439. if (!cb) {
  21440. return this;
  21441. }
  21442. types = types.split(splitter);
  21443. cb = context ? bindContext(cb, context) : cb;
  21444. this._callbacks = this._callbacks || {};
  21445. while (type = types.shift()) {
  21446. this._callbacks[type] = this._callbacks[type] || {
  21447. sync: [],
  21448. async: []
  21449. };
  21450. this._callbacks[type][method].push(cb);
  21451. }
  21452. return this;
  21453. }
  21454. function onAsync(types, cb, context) {
  21455. return on.call(this, "async", types, cb, context);
  21456. }
  21457. function onSync(types, cb, context) {
  21458. return on.call(this, "sync", types, cb, context);
  21459. }
  21460. function off(types) {
  21461. var type;
  21462. if (!this._callbacks) {
  21463. return this;
  21464. }
  21465. types = types.split(splitter);
  21466. while (type = types.shift()) {
  21467. delete this._callbacks[type];
  21468. }
  21469. return this;
  21470. }
  21471. function trigger(types) {
  21472. var type, callbacks, args, syncFlush, asyncFlush;
  21473. if (!this._callbacks) {
  21474. return this;
  21475. }
  21476. types = types.split(splitter);
  21477. args = [].slice.call(arguments, 1);
  21478. while ((type = types.shift()) && (callbacks = this._callbacks[type])) {
  21479. syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args));
  21480. asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args));
  21481. syncFlush() && nextTick(asyncFlush);
  21482. }
  21483. return this;
  21484. }
  21485. function getFlush(callbacks, context, args) {
  21486. return flush;
  21487. function flush() {
  21488. var cancelled;
  21489. for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) {
  21490. cancelled = callbacks[i].apply(context, args) === false;
  21491. }
  21492. return !cancelled;
  21493. }
  21494. }
  21495. function getNextTick() {
  21496. var nextTickFn;
  21497. if (window.setImmediate) {
  21498. nextTickFn = function nextTickSetImmediate(fn) {
  21499. setImmediate(function() {
  21500. fn();
  21501. });
  21502. };
  21503. } else {
  21504. nextTickFn = function nextTickSetTimeout(fn) {
  21505. setTimeout(function() {
  21506. fn();
  21507. }, 0);
  21508. };
  21509. }
  21510. return nextTickFn;
  21511. }
  21512. function bindContext(fn, context) {
  21513. return fn.bind ? fn.bind(context) : function() {
  21514. fn.apply(context, [].slice.call(arguments, 0));
  21515. };
  21516. }
  21517. }();
  21518. var highlight = function(doc) {
  21519. var defaults = {
  21520. node: null,
  21521. pattern: null,
  21522. tagName: "strong",
  21523. className: null,
  21524. wordsOnly: false,
  21525. caseSensitive: false
  21526. };
  21527. return function hightlight(o) {
  21528. var regex;
  21529. o = _.mixin({}, defaults, o);
  21530. if (!o.node || !o.pattern) {
  21531. return;
  21532. }
  21533. o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ];
  21534. regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly);
  21535. traverse(o.node, hightlightTextNode);
  21536. function hightlightTextNode(textNode) {
  21537. var match, patternNode, wrapperNode;
  21538. if (match = regex.exec(textNode.data)) {
  21539. wrapperNode = doc.createElement(o.tagName);
  21540. o.className && (wrapperNode.className = o.className);
  21541. patternNode = textNode.splitText(match.index);
  21542. patternNode.splitText(match[0].length);
  21543. wrapperNode.appendChild(patternNode.cloneNode(true));
  21544. textNode.parentNode.replaceChild(wrapperNode, patternNode);
  21545. }
  21546. return !!match;
  21547. }
  21548. function traverse(el, hightlightTextNode) {
  21549. var childNode, TEXT_NODE_TYPE = 3;
  21550. for (var i = 0; i < el.childNodes.length; i++) {
  21551. childNode = el.childNodes[i];
  21552. if (childNode.nodeType === TEXT_NODE_TYPE) {
  21553. i += hightlightTextNode(childNode) ? 1 : 0;
  21554. } else {
  21555. traverse(childNode, hightlightTextNode);
  21556. }
  21557. }
  21558. }
  21559. };
  21560. function getRegex(patterns, caseSensitive, wordsOnly) {
  21561. var escapedPatterns = [], regexStr;
  21562. for (var i = 0, len = patterns.length; i < len; i++) {
  21563. escapedPatterns.push(_.escapeRegExChars(patterns[i]));
  21564. }
  21565. regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")";
  21566. return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i");
  21567. }
  21568. }(window.document);
  21569. var Input = function() {
  21570. var specialKeyCodeMap;
  21571. specialKeyCodeMap = {
  21572. 9: "tab",
  21573. 27: "esc",
  21574. 37: "left",
  21575. 39: "right",
  21576. 13: "enter",
  21577. 38: "up",
  21578. 40: "down"
  21579. };
  21580. function Input(o) {
  21581. var that = this, onBlur, onFocus, onKeydown, onInput;
  21582. o = o || {};
  21583. if (!o.input) {
  21584. $.error("input is missing");
  21585. }
  21586. onBlur = _.bind(this._onBlur, this);
  21587. onFocus = _.bind(this._onFocus, this);
  21588. onKeydown = _.bind(this._onKeydown, this);
  21589. onInput = _.bind(this._onInput, this);
  21590. this.$hint = $(o.hint);
  21591. this.$input = $(o.input).on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown);
  21592. if (this.$hint.length === 0) {
  21593. this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop;
  21594. }
  21595. if (!_.isMsie()) {
  21596. this.$input.on("input.tt", onInput);
  21597. } else {
  21598. this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) {
  21599. if (specialKeyCodeMap[$e.which || $e.keyCode]) {
  21600. return;
  21601. }
  21602. _.defer(_.bind(that._onInput, that, $e));
  21603. });
  21604. }
  21605. this.query = this.$input.val();
  21606. this.$overflowHelper = buildOverflowHelper(this.$input);
  21607. }
  21608. Input.normalizeQuery = function(str) {
  21609. return (str || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
  21610. };
  21611. _.mixin(Input.prototype, EventEmitter, {
  21612. _onBlur: function onBlur() {
  21613. this.resetInputValue();
  21614. this.trigger("blurred");
  21615. },
  21616. _onFocus: function onFocus() {
  21617. this.trigger("focused");
  21618. },
  21619. _onKeydown: function onKeydown($e) {
  21620. var keyName = specialKeyCodeMap[$e.which || $e.keyCode];
  21621. this._managePreventDefault(keyName, $e);
  21622. if (keyName && this._shouldTrigger(keyName, $e)) {
  21623. this.trigger(keyName + "Keyed", $e);
  21624. }
  21625. },
  21626. _onInput: function onInput() {
  21627. this._checkInputValue();
  21628. },
  21629. _managePreventDefault: function managePreventDefault(keyName, $e) {
  21630. var preventDefault, hintValue, inputValue;
  21631. switch (keyName) {
  21632. case "tab":
  21633. hintValue = this.getHint();
  21634. inputValue = this.getInputValue();
  21635. preventDefault = hintValue && hintValue !== inputValue && !withModifier($e);
  21636. break;
  21637. case "up":
  21638. case "down":
  21639. preventDefault = !withModifier($e);
  21640. break;
  21641. default:
  21642. preventDefault = false;
  21643. }
  21644. preventDefault && $e.preventDefault();
  21645. },
  21646. _shouldTrigger: function shouldTrigger(keyName, $e) {
  21647. var trigger;
  21648. switch (keyName) {
  21649. case "tab":
  21650. trigger = !withModifier($e);
  21651. break;
  21652. default:
  21653. trigger = true;
  21654. }
  21655. return trigger;
  21656. },
  21657. _checkInputValue: function checkInputValue() {
  21658. var inputValue, areEquivalent, hasDifferentWhitespace;
  21659. inputValue = this.getInputValue();
  21660. areEquivalent = areQueriesEquivalent(inputValue, this.query);
  21661. hasDifferentWhitespace = areEquivalent ? this.query.length !== inputValue.length : false;
  21662. this.query = inputValue;
  21663. if (!areEquivalent) {
  21664. this.trigger("queryChanged", this.query);
  21665. } else if (hasDifferentWhitespace) {
  21666. this.trigger("whitespaceChanged", this.query);
  21667. }
  21668. },
  21669. focus: function focus() {
  21670. this.$input.focus();
  21671. },
  21672. blur: function blur() {
  21673. this.$input.blur();
  21674. },
  21675. getQuery: function getQuery() {
  21676. return this.query;
  21677. },
  21678. setQuery: function setQuery(query) {
  21679. this.query = query;
  21680. },
  21681. getInputValue: function getInputValue() {
  21682. return this.$input.val();
  21683. },
  21684. setInputValue: function setInputValue(value, silent) {
  21685. this.$input.val(value);
  21686. silent ? this.clearHint() : this._checkInputValue();
  21687. },
  21688. resetInputValue: function resetInputValue() {
  21689. this.setInputValue(this.query, true);
  21690. },
  21691. getHint: function getHint() {
  21692. return this.$hint.val();
  21693. },
  21694. setHint: function setHint(value) {
  21695. this.$hint.val(value);
  21696. },
  21697. clearHint: function clearHint() {
  21698. this.setHint("");
  21699. },
  21700. clearHintIfInvalid: function clearHintIfInvalid() {
  21701. var val, hint, valIsPrefixOfHint, isValid;
  21702. val = this.getInputValue();
  21703. hint = this.getHint();
  21704. valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0;
  21705. isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow();
  21706. !isValid && this.clearHint();
  21707. },
  21708. getLanguageDirection: function getLanguageDirection() {
  21709. return (this.$input.css("direction") || "ltr").toLowerCase();
  21710. },
  21711. hasOverflow: function hasOverflow() {
  21712. var constraint = this.$input.width() - 2;
  21713. this.$overflowHelper.text(this.getInputValue());
  21714. return this.$overflowHelper.width() >= constraint;
  21715. },
  21716. isCursorAtEnd: function() {
  21717. var valueLength, selectionStart, range;
  21718. valueLength = this.$input.val().length;
  21719. selectionStart = this.$input[0].selectionStart;
  21720. if (_.isNumber(selectionStart)) {
  21721. return selectionStart === valueLength;
  21722. } else if (document.selection) {
  21723. range = document.selection.createRange();
  21724. range.moveStart("character", -valueLength);
  21725. return valueLength === range.text.length;
  21726. }
  21727. return true;
  21728. },
  21729. destroy: function destroy() {
  21730. this.$hint.off(".tt");
  21731. this.$input.off(".tt");
  21732. this.$hint = this.$input = this.$overflowHelper = null;
  21733. }
  21734. });
  21735. return Input;
  21736. function buildOverflowHelper($input) {
  21737. return $('<pre aria-hidden="true"></pre>').css({
  21738. position: "absolute",
  21739. visibility: "hidden",
  21740. whiteSpace: "pre",
  21741. fontFamily: $input.css("font-family"),
  21742. fontSize: $input.css("font-size"),
  21743. fontStyle: $input.css("font-style"),
  21744. fontVariant: $input.css("font-variant"),
  21745. fontWeight: $input.css("font-weight"),
  21746. wordSpacing: $input.css("word-spacing"),
  21747. letterSpacing: $input.css("letter-spacing"),
  21748. textIndent: $input.css("text-indent"),
  21749. textRendering: $input.css("text-rendering"),
  21750. textTransform: $input.css("text-transform")
  21751. }).insertAfter($input);
  21752. }
  21753. function areQueriesEquivalent(a, b) {
  21754. return Input.normalizeQuery(a) === Input.normalizeQuery(b);
  21755. }
  21756. function withModifier($e) {
  21757. return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey;
  21758. }
  21759. }();
  21760. var Dataset = function() {
  21761. var datasetKey = "ttDataset", valueKey = "ttValue", datumKey = "ttDatum";
  21762. function Dataset(o) {
  21763. o = o || {};
  21764. o.templates = o.templates || {};
  21765. if (!o.source) {
  21766. $.error("missing source");
  21767. }
  21768. if (o.name && !isValidName(o.name)) {
  21769. $.error("invalid dataset name: " + o.name);
  21770. }
  21771. this.query = null;
  21772. this.highlight = !!o.highlight;
  21773. this.name = o.name || _.getUniqueId();
  21774. this.source = o.source;
  21775. this.displayFn = getDisplayFn(o.display || o.displayKey);
  21776. this.templates = getTemplates(o.templates, this.displayFn);
  21777. this.$el = $(html.dataset.replace("%CLASS%", this.name));
  21778. }
  21779. Dataset.extractDatasetName = function extractDatasetName(el) {
  21780. return $(el).data(datasetKey);
  21781. };
  21782. Dataset.extractValue = function extractDatum(el) {
  21783. return $(el).data(valueKey);
  21784. };
  21785. Dataset.extractDatum = function extractDatum(el) {
  21786. return $(el).data(datumKey);
  21787. };
  21788. _.mixin(Dataset.prototype, EventEmitter, {
  21789. _render: function render(query, suggestions) {
  21790. if (!this.$el) {
  21791. return;
  21792. }
  21793. var that = this, hasSuggestions;
  21794. this.$el.empty();
  21795. hasSuggestions = suggestions && suggestions.length;
  21796. if (!hasSuggestions && this.templates.empty) {
  21797. this.$el.html(getEmptyHtml()).prepend(that.templates.header ? getHeaderHtml() : null).append(that.templates.footer ? getFooterHtml() : null);
  21798. } else if (hasSuggestions) {
  21799. this.$el.html(getSuggestionsHtml()).prepend(that.templates.header ? getHeaderHtml() : null).append(that.templates.footer ? getFooterHtml() : null);
  21800. }
  21801. this.trigger("rendered");
  21802. function getEmptyHtml() {
  21803. return that.templates.empty({
  21804. query: query,
  21805. isEmpty: true
  21806. });
  21807. }
  21808. function getSuggestionsHtml() {
  21809. var $suggestions, nodes;
  21810. $suggestions = $(html.suggestions).css(css.suggestions);
  21811. nodes = _.map(suggestions, getSuggestionNode);
  21812. $suggestions.append.apply($suggestions, nodes);
  21813. that.highlight && highlight({
  21814. className: "tt-highlight",
  21815. node: $suggestions[0],
  21816. pattern: query
  21817. });
  21818. return $suggestions;
  21819. function getSuggestionNode(suggestion) {
  21820. var $el;
  21821. $el = $(html.suggestion).append(that.templates.suggestion(suggestion)).data(datasetKey, that.name).data(valueKey, that.displayFn(suggestion)).data(datumKey, suggestion);
  21822. $el.children().each(function() {
  21823. $(this).css(css.suggestionChild);
  21824. });
  21825. return $el;
  21826. }
  21827. }
  21828. function getHeaderHtml() {
  21829. return that.templates.header({
  21830. query: query,
  21831. isEmpty: !hasSuggestions
  21832. });
  21833. }
  21834. function getFooterHtml() {
  21835. return that.templates.footer({
  21836. query: query,
  21837. isEmpty: !hasSuggestions
  21838. });
  21839. }
  21840. },
  21841. getRoot: function getRoot() {
  21842. return this.$el;
  21843. },
  21844. update: function update(query) {
  21845. var that = this;
  21846. this.query = query;
  21847. this.canceled = false;
  21848. this.source(query, render);
  21849. function render(suggestions) {
  21850. if (!that.canceled && query === that.query) {
  21851. that._render(query, suggestions);
  21852. }
  21853. }
  21854. },
  21855. cancel: function cancel() {
  21856. this.canceled = true;
  21857. },
  21858. clear: function clear() {
  21859. this.cancel();
  21860. this.$el.empty();
  21861. this.trigger("rendered");
  21862. },
  21863. isEmpty: function isEmpty() {
  21864. return this.$el.is(":empty");
  21865. },
  21866. destroy: function destroy() {
  21867. this.$el = null;
  21868. }
  21869. });
  21870. return Dataset;
  21871. function getDisplayFn(display) {
  21872. display = display || "value";
  21873. return _.isFunction(display) ? display : displayFn;
  21874. function displayFn(obj) {
  21875. return obj[display];
  21876. }
  21877. }
  21878. function getTemplates(templates, displayFn) {
  21879. return {
  21880. empty: templates.empty && _.templatify(templates.empty),
  21881. header: templates.header && _.templatify(templates.header),
  21882. footer: templates.footer && _.templatify(templates.footer),
  21883. suggestion: templates.suggestion || suggestionTemplate
  21884. };
  21885. function suggestionTemplate(context) {
  21886. return "<p>" + displayFn(context) + "</p>";
  21887. }
  21888. }
  21889. function isValidName(str) {
  21890. return /^[_a-zA-Z0-9-]+$/.test(str);
  21891. }
  21892. }();
  21893. var Dropdown = function() {
  21894. function Dropdown(o) {
  21895. var that = this, onSuggestionClick, onSuggestionMouseEnter, onSuggestionMouseLeave;
  21896. o = o || {};
  21897. if (!o.menu) {
  21898. $.error("menu is required");
  21899. }
  21900. this.isOpen = false;
  21901. this.isEmpty = true;
  21902. this.datasets = _.map(o.datasets, initializeDataset);
  21903. onSuggestionClick = _.bind(this._onSuggestionClick, this);
  21904. onSuggestionMouseEnter = _.bind(this._onSuggestionMouseEnter, this);
  21905. onSuggestionMouseLeave = _.bind(this._onSuggestionMouseLeave, this);
  21906. this.$menu = $(o.menu).on("click.tt", ".tt-suggestion", onSuggestionClick).on("mouseenter.tt", ".tt-suggestion", onSuggestionMouseEnter).on("mouseleave.tt", ".tt-suggestion", onSuggestionMouseLeave);
  21907. _.each(this.datasets, function(dataset) {
  21908. that.$menu.append(dataset.getRoot());
  21909. dataset.onSync("rendered", that._onRendered, that);
  21910. });
  21911. }
  21912. _.mixin(Dropdown.prototype, EventEmitter, {
  21913. _onSuggestionClick: function onSuggestionClick($e) {
  21914. this.trigger("suggestionClicked", $($e.currentTarget));
  21915. },
  21916. _onSuggestionMouseEnter: function onSuggestionMouseEnter($e) {
  21917. this._removeCursor();
  21918. this._setCursor($($e.currentTarget), true);
  21919. },
  21920. _onSuggestionMouseLeave: function onSuggestionMouseLeave() {
  21921. this._removeCursor();
  21922. },
  21923. _onRendered: function onRendered() {
  21924. this.isEmpty = _.every(this.datasets, isDatasetEmpty);
  21925. this.isEmpty ? this._hide() : this.isOpen && this._show();
  21926. this.trigger("datasetRendered");
  21927. function isDatasetEmpty(dataset) {
  21928. return dataset.isEmpty();
  21929. }
  21930. },
  21931. _hide: function() {
  21932. this.$menu.hide();
  21933. },
  21934. _show: function() {
  21935. this.$menu.css("display", "block");
  21936. },
  21937. _getSuggestions: function getSuggestions() {
  21938. return this.$menu.find(".tt-suggestion");
  21939. },
  21940. _getCursor: function getCursor() {
  21941. return this.$menu.find(".tt-cursor").first();
  21942. },
  21943. _setCursor: function setCursor($el, silent) {
  21944. $el.first().addClass("tt-cursor");
  21945. !silent && this.trigger("cursorMoved");
  21946. },
  21947. _removeCursor: function removeCursor() {
  21948. this._getCursor().removeClass("tt-cursor");
  21949. },
  21950. _moveCursor: function moveCursor(increment) {
  21951. var $suggestions, $oldCursor, newCursorIndex, $newCursor;
  21952. if (!this.isOpen) {
  21953. return;
  21954. }
  21955. $oldCursor = this._getCursor();
  21956. $suggestions = this._getSuggestions();
  21957. this._removeCursor();
  21958. newCursorIndex = $suggestions.index($oldCursor) + increment;
  21959. newCursorIndex = (newCursorIndex + 1) % ($suggestions.length + 1) - 1;
  21960. if (newCursorIndex === -1) {
  21961. this.trigger("cursorRemoved");
  21962. return;
  21963. } else if (newCursorIndex < -1) {
  21964. newCursorIndex = $suggestions.length - 1;
  21965. }
  21966. this._setCursor($newCursor = $suggestions.eq(newCursorIndex));
  21967. this._ensureVisible($newCursor);
  21968. },
  21969. _ensureVisible: function ensureVisible($el) {
  21970. var elTop, elBottom, menuScrollTop, menuHeight;
  21971. elTop = $el.position().top;
  21972. elBottom = elTop + $el.outerHeight(true);
  21973. menuScrollTop = this.$menu.scrollTop();
  21974. menuHeight = this.$menu.height() + parseInt(this.$menu.css("paddingTop"), 10) + parseInt(this.$menu.css("paddingBottom"), 10);
  21975. if (elTop < 0) {
  21976. this.$menu.scrollTop(menuScrollTop + elTop);
  21977. } else if (menuHeight < elBottom) {
  21978. this.$menu.scrollTop(menuScrollTop + (elBottom - menuHeight));
  21979. }
  21980. },
  21981. close: function close() {
  21982. if (this.isOpen) {
  21983. this.isOpen = false;
  21984. this._removeCursor();
  21985. this._hide();
  21986. this.trigger("closed");
  21987. }
  21988. },
  21989. open: function open() {
  21990. if (!this.isOpen) {
  21991. this.isOpen = true;
  21992. !this.isEmpty && this._show();
  21993. this.trigger("opened");
  21994. }
  21995. },
  21996. setLanguageDirection: function setLanguageDirection(dir) {
  21997. this.$menu.css(dir === "ltr" ? css.ltr : css.rtl);
  21998. },
  21999. moveCursorUp: function moveCursorUp() {
  22000. this._moveCursor(-1);
  22001. },
  22002. moveCursorDown: function moveCursorDown() {
  22003. this._moveCursor(+1);
  22004. },
  22005. getDatumForSuggestion: function getDatumForSuggestion($el) {
  22006. var datum = null;
  22007. if ($el.length) {
  22008. datum = {
  22009. raw: Dataset.extractDatum($el),
  22010. value: Dataset.extractValue($el),
  22011. datasetName: Dataset.extractDatasetName($el)
  22012. };
  22013. }
  22014. return datum;
  22015. },
  22016. getDatumForCursor: function getDatumForCursor() {
  22017. return this.getDatumForSuggestion(this._getCursor().first());
  22018. },
  22019. getDatumForTopSuggestion: function getDatumForTopSuggestion() {
  22020. return this.getDatumForSuggestion(this._getSuggestions().first());
  22021. },
  22022. update: function update(query) {
  22023. _.each(this.datasets, updateDataset);
  22024. function updateDataset(dataset) {
  22025. dataset.update(query);
  22026. }
  22027. },
  22028. empty: function empty() {
  22029. _.each(this.datasets, clearDataset);
  22030. this.isEmpty = true;
  22031. function clearDataset(dataset) {
  22032. dataset.clear();
  22033. }
  22034. },
  22035. isVisible: function isVisible() {
  22036. return this.isOpen && !this.isEmpty;
  22037. },
  22038. destroy: function destroy() {
  22039. this.$menu.off(".tt");
  22040. this.$menu = null;
  22041. _.each(this.datasets, destroyDataset);
  22042. function destroyDataset(dataset) {
  22043. dataset.destroy();
  22044. }
  22045. }
  22046. });
  22047. return Dropdown;
  22048. function initializeDataset(oDataset) {
  22049. return new Dataset(oDataset);
  22050. }
  22051. }();
  22052. var Typeahead = function() {
  22053. var attrsKey = "ttAttrs";
  22054. function Typeahead(o) {
  22055. var $menu, $input, $hint;
  22056. o = o || {};
  22057. if (!o.input) {
  22058. $.error("missing input");
  22059. }
  22060. this.isActivated = false;
  22061. this.autoselect = !!o.autoselect;
  22062. this.minLength = _.isNumber(o.minLength) ? o.minLength : 1;
  22063. this.$node = buildDom(o.input, o.withHint);
  22064. $menu = this.$node.find(".tt-dropdown-menu");
  22065. $input = this.$node.find(".tt-input");
  22066. $hint = this.$node.find(".tt-hint");
  22067. $input.on("blur.tt", function($e) {
  22068. var active, isActive, hasActive;
  22069. active = document.activeElement;
  22070. isActive = $menu.is(active);
  22071. hasActive = $menu.has(active).length > 0;
  22072. if (_.isMsie() && (isActive || hasActive)) {
  22073. $e.preventDefault();
  22074. $e.stopImmediatePropagation();
  22075. _.defer(function() {
  22076. $input.focus();
  22077. });
  22078. }
  22079. });
  22080. $menu.on("mousedown.tt", function($e) {
  22081. $e.preventDefault();
  22082. });
  22083. this.eventBus = o.eventBus || new EventBus({
  22084. el: $input
  22085. });
  22086. this.dropdown = new Dropdown({
  22087. menu: $menu,
  22088. datasets: o.datasets
  22089. }).onSync("suggestionClicked", this._onSuggestionClicked, this).onSync("cursorMoved", this._onCursorMoved, this).onSync("cursorRemoved", this._onCursorRemoved, this).onSync("opened", this._onOpened, this).onSync("closed", this._onClosed, this).onAsync("datasetRendered", this._onDatasetRendered, this);
  22090. this.input = new Input({
  22091. input: $input,
  22092. hint: $hint
  22093. }).onSync("focused", this._onFocused, this).onSync("blurred", this._onBlurred, this).onSync("enterKeyed", this._onEnterKeyed, this).onSync("tabKeyed", this._onTabKeyed, this).onSync("escKeyed", this._onEscKeyed, this).onSync("upKeyed", this._onUpKeyed, this).onSync("downKeyed", this._onDownKeyed, this).onSync("leftKeyed", this._onLeftKeyed, this).onSync("rightKeyed", this._onRightKeyed, this).onSync("queryChanged", this._onQueryChanged, this).onSync("whitespaceChanged", this._onWhitespaceChanged, this);
  22094. this._setLanguageDirection();
  22095. }
  22096. _.mixin(Typeahead.prototype, {
  22097. _onSuggestionClicked: function onSuggestionClicked(type, $el) {
  22098. var datum;
  22099. if (datum = this.dropdown.getDatumForSuggestion($el)) {
  22100. this._select(datum);
  22101. }
  22102. },
  22103. _onCursorMoved: function onCursorMoved() {
  22104. var datum = this.dropdown.getDatumForCursor();
  22105. this.input.setInputValue(datum.value, true);
  22106. this.eventBus.trigger("cursorchanged", datum.raw, datum.datasetName);
  22107. },
  22108. _onCursorRemoved: function onCursorRemoved() {
  22109. this.input.resetInputValue();
  22110. this._updateHint();
  22111. },
  22112. _onDatasetRendered: function onDatasetRendered() {
  22113. this._updateHint();
  22114. },
  22115. _onOpened: function onOpened() {
  22116. this._updateHint();
  22117. this.eventBus.trigger("opened");
  22118. },
  22119. _onClosed: function onClosed() {
  22120. this.input.clearHint();
  22121. this.eventBus.trigger("closed");
  22122. },
  22123. _onFocused: function onFocused() {
  22124. this.isActivated = true;
  22125. this.dropdown.open();
  22126. },
  22127. _onBlurred: function onBlurred() {
  22128. this.isActivated = false;
  22129. this.dropdown.empty();
  22130. this.dropdown.close();
  22131. },
  22132. _onEnterKeyed: function onEnterKeyed(type, $e) {
  22133. var cursorDatum, topSuggestionDatum;
  22134. cursorDatum = this.dropdown.getDatumForCursor();
  22135. topSuggestionDatum = this.dropdown.getDatumForTopSuggestion();
  22136. if (cursorDatum) {
  22137. this._select(cursorDatum);
  22138. $e.preventDefault();
  22139. } else if (this.autoselect && topSuggestionDatum) {
  22140. this._select(topSuggestionDatum);
  22141. $e.preventDefault();
  22142. }
  22143. },
  22144. _onTabKeyed: function onTabKeyed(type, $e) {
  22145. var datum;
  22146. if (datum = this.dropdown.getDatumForCursor()) {
  22147. this._select(datum);
  22148. $e.preventDefault();
  22149. } else {
  22150. this._autocomplete(true);
  22151. }
  22152. },
  22153. _onEscKeyed: function onEscKeyed() {
  22154. this.dropdown.close();
  22155. this.input.resetInputValue();
  22156. },
  22157. _onUpKeyed: function onUpKeyed() {
  22158. var query = this.input.getQuery();
  22159. this.dropdown.isEmpty && query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.moveCursorUp();
  22160. this.dropdown.open();
  22161. },
  22162. _onDownKeyed: function onDownKeyed() {
  22163. var query = this.input.getQuery();
  22164. this.dropdown.isEmpty && query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.moveCursorDown();
  22165. this.dropdown.open();
  22166. },
  22167. _onLeftKeyed: function onLeftKeyed() {
  22168. this.dir === "rtl" && this._autocomplete();
  22169. },
  22170. _onRightKeyed: function onRightKeyed() {
  22171. this.dir === "ltr" && this._autocomplete();
  22172. },
  22173. _onQueryChanged: function onQueryChanged(e, query) {
  22174. this.input.clearHintIfInvalid();
  22175. query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.empty();
  22176. this.dropdown.open();
  22177. this._setLanguageDirection();
  22178. },
  22179. _onWhitespaceChanged: function onWhitespaceChanged() {
  22180. this._updateHint();
  22181. this.dropdown.open();
  22182. },
  22183. _setLanguageDirection: function setLanguageDirection() {
  22184. var dir;
  22185. if (this.dir !== (dir = this.input.getLanguageDirection())) {
  22186. this.dir = dir;
  22187. this.$node.css("direction", dir);
  22188. this.dropdown.setLanguageDirection(dir);
  22189. }
  22190. },
  22191. _updateHint: function updateHint() {
  22192. var datum, val, query, escapedQuery, frontMatchRegEx, match;
  22193. datum = this.dropdown.getDatumForTopSuggestion();
  22194. if (datum && this.dropdown.isVisible() && !this.input.hasOverflow()) {
  22195. val = this.input.getInputValue();
  22196. query = Input.normalizeQuery(val);
  22197. escapedQuery = _.escapeRegExChars(query);
  22198. frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i");
  22199. match = frontMatchRegEx.exec(datum.value);
  22200. match ? this.input.setHint(val + match[1]) : this.input.clearHint();
  22201. } else {
  22202. this.input.clearHint();
  22203. }
  22204. },
  22205. _autocomplete: function autocomplete(laxCursor) {
  22206. var hint, query, isCursorAtEnd, datum;
  22207. hint = this.input.getHint();
  22208. query = this.input.getQuery();
  22209. isCursorAtEnd = laxCursor || this.input.isCursorAtEnd();
  22210. if (hint && query !== hint && isCursorAtEnd) {
  22211. datum = this.dropdown.getDatumForTopSuggestion();
  22212. datum && this.input.setInputValue(datum.value);
  22213. this.eventBus.trigger("autocompleted", datum.raw, datum.datasetName);
  22214. }
  22215. },
  22216. _select: function select(datum) {
  22217. this.input.setQuery(datum.value);
  22218. this.input.setInputValue(datum.value, true);
  22219. this._setLanguageDirection();
  22220. this.eventBus.trigger("selected", datum.raw, datum.datasetName);
  22221. this.dropdown.close();
  22222. _.defer(_.bind(this.dropdown.empty, this.dropdown));
  22223. },
  22224. open: function open() {
  22225. this.dropdown.open();
  22226. },
  22227. close: function close() {
  22228. this.dropdown.close();
  22229. },
  22230. setVal: function setVal(val) {
  22231. val = _.toStr(val);
  22232. if (this.isActivated) {
  22233. this.input.setInputValue(val);
  22234. } else {
  22235. this.input.setQuery(val);
  22236. this.input.setInputValue(val, true);
  22237. }
  22238. this._setLanguageDirection();
  22239. },
  22240. getVal: function getVal() {
  22241. return this.input.getQuery();
  22242. },
  22243. destroy: function destroy() {
  22244. this.input.destroy();
  22245. this.dropdown.destroy();
  22246. destroyDomStructure(this.$node);
  22247. this.$node = null;
  22248. }
  22249. });
  22250. return Typeahead;
  22251. function buildDom(input, withHint) {
  22252. var $input, $wrapper, $dropdown, $hint;
  22253. $input = $(input);
  22254. $wrapper = $(html.wrapper).css(css.wrapper);
  22255. $dropdown = $(html.dropdown).css(css.dropdown);
  22256. $hint = $input.clone().css(css.hint).css(getBackgroundStyles($input));
  22257. $hint.val("").removeData().addClass("tt-hint").removeAttr("id name placeholder required").prop("readonly", true).attr({
  22258. autocomplete: "off",
  22259. spellcheck: "false",
  22260. tabindex: -1
  22261. });
  22262. $input.data(attrsKey, {
  22263. dir: $input.attr("dir"),
  22264. autocomplete: $input.attr("autocomplete"),
  22265. spellcheck: $input.attr("spellcheck"),
  22266. style: $input.attr("style")
  22267. });
  22268. $input.addClass("tt-input").attr({
  22269. autocomplete: "off",
  22270. spellcheck: false
  22271. }).css(withHint ? css.input : css.inputWithNoHint);
  22272. try {
  22273. !$input.attr("dir") && $input.attr("dir", "auto");
  22274. } catch (e) {}
  22275. return $input.wrap($wrapper).parent().prepend(withHint ? $hint : null).append($dropdown);
  22276. }
  22277. function getBackgroundStyles($el) {
  22278. return {
  22279. backgroundAttachment: $el.css("background-attachment"),
  22280. backgroundClip: $el.css("background-clip"),
  22281. backgroundColor: $el.css("background-color"),
  22282. backgroundImage: $el.css("background-image"),
  22283. backgroundOrigin: $el.css("background-origin"),
  22284. backgroundPosition: $el.css("background-position"),
  22285. backgroundRepeat: $el.css("background-repeat"),
  22286. backgroundSize: $el.css("background-size")
  22287. };
  22288. }
  22289. function destroyDomStructure($node) {
  22290. var $input = $node.find(".tt-input");
  22291. _.each($input.data(attrsKey), function(val, key) {
  22292. _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);
  22293. });
  22294. $input.detach().removeData(attrsKey).removeClass("tt-input").insertAfter($node);
  22295. $node.remove();
  22296. }
  22297. }();
  22298. (function() {
  22299. var old, typeaheadKey, methods;
  22300. old = $.fn.typeahead;
  22301. typeaheadKey = "ttTypeahead";
  22302. methods = {
  22303. initialize: function initialize(o, datasets) {
  22304. datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1);
  22305. o = o || {};
  22306. return this.each(attach);
  22307. function attach() {
  22308. var $input = $(this), eventBus, typeahead;
  22309. _.each(datasets, function(d) {
  22310. d.highlight = !!o.highlight;
  22311. });
  22312. typeahead = new Typeahead({
  22313. input: $input,
  22314. eventBus: eventBus = new EventBus({
  22315. el: $input
  22316. }),
  22317. withHint: _.isUndefined(o.hint) ? true : !!o.hint,
  22318. minLength: o.minLength,
  22319. autoselect: o.autoselect,
  22320. datasets: datasets
  22321. });
  22322. $input.data(typeaheadKey, typeahead);
  22323. }
  22324. },
  22325. open: function open() {
  22326. return this.each(openTypeahead);
  22327. function openTypeahead() {
  22328. var $input = $(this), typeahead;
  22329. if (typeahead = $input.data(typeaheadKey)) {
  22330. typeahead.open();
  22331. }
  22332. }
  22333. },
  22334. close: function close() {
  22335. return this.each(closeTypeahead);
  22336. function closeTypeahead() {
  22337. var $input = $(this), typeahead;
  22338. if (typeahead = $input.data(typeaheadKey)) {
  22339. typeahead.close();
  22340. }
  22341. }
  22342. },
  22343. val: function val(newVal) {
  22344. return !arguments.length ? getVal(this.first()) : this.each(setVal);
  22345. function setVal() {
  22346. var $input = $(this), typeahead;
  22347. if (typeahead = $input.data(typeaheadKey)) {
  22348. typeahead.setVal(newVal);
  22349. }
  22350. }
  22351. function getVal($input) {
  22352. var typeahead, query;
  22353. if (typeahead = $input.data(typeaheadKey)) {
  22354. query = typeahead.getVal();
  22355. }
  22356. return query;
  22357. }
  22358. },
  22359. destroy: function destroy() {
  22360. return this.each(unattach);
  22361. function unattach() {
  22362. var $input = $(this), typeahead;
  22363. if (typeahead = $input.data(typeaheadKey)) {
  22364. typeahead.destroy();
  22365. $input.removeData(typeaheadKey);
  22366. }
  22367. }
  22368. }
  22369. };
  22370. $.fn.typeahead = function(method) {
  22371. var tts;
  22372. if (methods[method] && method !== "initialize") {
  22373. tts = this.filter(function() {
  22374. return !!$(this).data(typeaheadKey);
  22375. });
  22376. return methods[method].apply(tts, [].slice.call(arguments, 1));
  22377. } else {
  22378. return methods.initialize.apply(this, arguments);
  22379. }
  22380. };
  22381. $.fn.typeahead.noConflict = function noConflict() {
  22382. $.fn.typeahead = old;
  22383. return this;
  22384. };
  22385. })();
  22386. return {};
  22387. }));
  22388. // This code was written by Tyler Akins and has been placed in the
  22389. // public domain. It would be nice if you left this header intact.
  22390. // Base64 code from Tyler Akins -- http://rumkin.com
  22391. var Base64 = (function () {
  22392. var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  22393. var obj = {
  22394. /**
  22395. * Encodes a string in base64
  22396. * @param {String} input The string to encode in base64.
  22397. */
  22398. encode: function (input) {
  22399. var output = "";
  22400. var chr1, chr2, chr3;
  22401. var enc1, enc2, enc3, enc4;
  22402. var i = 0;
  22403. do {
  22404. chr1 = input.charCodeAt(i++);
  22405. chr2 = input.charCodeAt(i++);
  22406. chr3 = input.charCodeAt(i++);
  22407. enc1 = chr1 >> 2;
  22408. enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
  22409. enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
  22410. enc4 = chr3 & 63;
  22411. if (isNaN(chr2)) {
  22412. enc3 = enc4 = 64;
  22413. } else if (isNaN(chr3)) {
  22414. enc4 = 64;
  22415. }
  22416. output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) +
  22417. keyStr.charAt(enc3) + keyStr.charAt(enc4);
  22418. } while (i < input.length);
  22419. return output;
  22420. },
  22421. /**
  22422. * Decodes a base64 string.
  22423. * @param {String} input The string to decode.
  22424. */
  22425. decode: function (input) {
  22426. var output = "";
  22427. var chr1, chr2, chr3;
  22428. var enc1, enc2, enc3, enc4;
  22429. var i = 0;
  22430. // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
  22431. input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
  22432. do {
  22433. enc1 = keyStr.indexOf(input.charAt(i++));
  22434. enc2 = keyStr.indexOf(input.charAt(i++));
  22435. enc3 = keyStr.indexOf(input.charAt(i++));
  22436. enc4 = keyStr.indexOf(input.charAt(i++));
  22437. chr1 = (enc1 << 2) | (enc2 >> 4);
  22438. chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
  22439. chr3 = ((enc3 & 3) << 6) | enc4;
  22440. output = output + String.fromCharCode(chr1);
  22441. if (enc3 != 64) {
  22442. output = output + String.fromCharCode(chr2);
  22443. }
  22444. if (enc4 != 64) {
  22445. output = output + String.fromCharCode(chr3);
  22446. }
  22447. } while (i < input.length);
  22448. return output;
  22449. }
  22450. };
  22451. return obj;
  22452. })();
  22453. /*
  22454. * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
  22455. * in FIPS PUB 180-1
  22456. * Version 2.1a Copyright Paul Johnston 2000 - 2002.
  22457. * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
  22458. * Distributed under the BSD License
  22459. * See http://pajhome.org.uk/crypt/md5 for details.
  22460. */
  22461. /* Some functions and variables have been stripped for use with Strophe */
  22462. /*
  22463. * These are the functions you'll usually want to call
  22464. * They take string arguments and return either hex or base-64 encoded strings
  22465. */
  22466. function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * 8));}
  22467. function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * 8));}
  22468. function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}
  22469. function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));}
  22470. /*
  22471. * Calculate the SHA-1 of an array of big-endian words, and a bit length
  22472. */
  22473. function core_sha1(x, len)
  22474. {
  22475. /* append padding */
  22476. x[len >> 5] |= 0x80 << (24 - len % 32);
  22477. x[((len + 64 >> 9) << 4) + 15] = len;
  22478. var w = new Array(80);
  22479. var a = 1732584193;
  22480. var b = -271733879;
  22481. var c = -1732584194;
  22482. var d = 271733878;
  22483. var e = -1009589776;
  22484. var i, j, t, olda, oldb, oldc, oldd, olde;
  22485. for (i = 0; i < x.length; i += 16)
  22486. {
  22487. olda = a;
  22488. oldb = b;
  22489. oldc = c;
  22490. oldd = d;
  22491. olde = e;
  22492. for (j = 0; j < 80; j++)
  22493. {
  22494. if (j < 16) { w[j] = x[i + j]; }
  22495. else { w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); }
  22496. t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
  22497. safe_add(safe_add(e, w[j]), sha1_kt(j)));
  22498. e = d;
  22499. d = c;
  22500. c = rol(b, 30);
  22501. b = a;
  22502. a = t;
  22503. }
  22504. a = safe_add(a, olda);
  22505. b = safe_add(b, oldb);
  22506. c = safe_add(c, oldc);
  22507. d = safe_add(d, oldd);
  22508. e = safe_add(e, olde);
  22509. }
  22510. return [a, b, c, d, e];
  22511. }
  22512. /*
  22513. * Perform the appropriate triplet combination function for the current
  22514. * iteration
  22515. */
  22516. function sha1_ft(t, b, c, d)
  22517. {
  22518. if (t < 20) { return (b & c) | ((~b) & d); }
  22519. if (t < 40) { return b ^ c ^ d; }
  22520. if (t < 60) { return (b & c) | (b & d) | (c & d); }
  22521. return b ^ c ^ d;
  22522. }
  22523. /*
  22524. * Determine the appropriate additive constant for the current iteration
  22525. */
  22526. function sha1_kt(t)
  22527. {
  22528. return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
  22529. (t < 60) ? -1894007588 : -899497514;
  22530. }
  22531. /*
  22532. * Calculate the HMAC-SHA1 of a key and some data
  22533. */
  22534. function core_hmac_sha1(key, data)
  22535. {
  22536. var bkey = str2binb(key);
  22537. if (bkey.length > 16) { bkey = core_sha1(bkey, key.length * 8); }
  22538. var ipad = new Array(16), opad = new Array(16);
  22539. for (var i = 0; i < 16; i++)
  22540. {
  22541. ipad[i] = bkey[i] ^ 0x36363636;
  22542. opad[i] = bkey[i] ^ 0x5C5C5C5C;
  22543. }
  22544. var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * 8);
  22545. return core_sha1(opad.concat(hash), 512 + 160);
  22546. }
  22547. /*
  22548. * Add integers, wrapping at 2^32. This uses 16-bit operations internally
  22549. * to work around bugs in some JS interpreters.
  22550. */
  22551. function safe_add(x, y)
  22552. {
  22553. var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  22554. var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  22555. return (msw << 16) | (lsw & 0xFFFF);
  22556. }
  22557. /*
  22558. * Bitwise rotate a 32-bit number to the left.
  22559. */
  22560. function rol(num, cnt)
  22561. {
  22562. return (num << cnt) | (num >>> (32 - cnt));
  22563. }
  22564. /*
  22565. * Convert an 8-bit or 16-bit string to an array of big-endian words
  22566. * In 8-bit function, characters >255 have their hi-byte silently ignored.
  22567. */
  22568. function str2binb(str)
  22569. {
  22570. var bin = [];
  22571. var mask = 255;
  22572. for (var i = 0; i < str.length * 8; i += 8)
  22573. {
  22574. bin[i>>5] |= (str.charCodeAt(i / 8) & mask) << (24 - i%32);
  22575. }
  22576. return bin;
  22577. }
  22578. /*
  22579. * Convert an array of big-endian words to a string
  22580. */
  22581. function binb2str(bin)
  22582. {
  22583. var str = "";
  22584. var mask = 255;
  22585. for (var i = 0; i < bin.length * 32; i += 8)
  22586. {
  22587. str += String.fromCharCode((bin[i>>5] >>> (24 - i%32)) & mask);
  22588. }
  22589. return str;
  22590. }
  22591. /*
  22592. * Convert an array of big-endian words to a base-64 string
  22593. */
  22594. function binb2b64(binarray)
  22595. {
  22596. var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  22597. var str = "";
  22598. var triplet, j;
  22599. for (var i = 0; i < binarray.length * 4; i += 3)
  22600. {
  22601. triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) |
  22602. (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) |
  22603. ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
  22604. for (j = 0; j < 4; j++)
  22605. {
  22606. if (i * 8 + j * 6 > binarray.length * 32) { str += "="; }
  22607. else { str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); }
  22608. }
  22609. }
  22610. return str;
  22611. }
  22612. /*
  22613. * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
  22614. * Digest Algorithm, as defined in RFC 1321.
  22615. * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
  22616. * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
  22617. * Distributed under the BSD License
  22618. * See http://pajhome.org.uk/crypt/md5 for more info.
  22619. */
  22620. /*
  22621. * Everything that isn't used by Strophe has been stripped here!
  22622. */
  22623. var MD5 = (function () {
  22624. /*
  22625. * Add integers, wrapping at 2^32. This uses 16-bit operations internally
  22626. * to work around bugs in some JS interpreters.
  22627. */
  22628. var safe_add = function (x, y) {
  22629. var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  22630. var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  22631. return (msw << 16) | (lsw & 0xFFFF);
  22632. };
  22633. /*
  22634. * Bitwise rotate a 32-bit number to the left.
  22635. */
  22636. var bit_rol = function (num, cnt) {
  22637. return (num << cnt) | (num >>> (32 - cnt));
  22638. };
  22639. /*
  22640. * Convert a string to an array of little-endian words
  22641. */
  22642. var str2binl = function (str) {
  22643. var bin = [];
  22644. for(var i = 0; i < str.length * 8; i += 8)
  22645. {
  22646. bin[i>>5] |= (str.charCodeAt(i / 8) & 255) << (i%32);
  22647. }
  22648. return bin;
  22649. };
  22650. /*
  22651. * Convert an array of little-endian words to a string
  22652. */
  22653. var binl2str = function (bin) {
  22654. var str = "";
  22655. for(var i = 0; i < bin.length * 32; i += 8)
  22656. {
  22657. str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & 255);
  22658. }
  22659. return str;
  22660. };
  22661. /*
  22662. * Convert an array of little-endian words to a hex string.
  22663. */
  22664. var binl2hex = function (binarray) {
  22665. var hex_tab = "0123456789abcdef";
  22666. var str = "";
  22667. for(var i = 0; i < binarray.length * 4; i++)
  22668. {
  22669. str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
  22670. hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
  22671. }
  22672. return str;
  22673. };
  22674. /*
  22675. * These functions implement the four basic operations the algorithm uses.
  22676. */
  22677. var md5_cmn = function (q, a, b, x, s, t) {
  22678. return safe_add(bit_rol(safe_add(safe_add(a, q),safe_add(x, t)), s),b);
  22679. };
  22680. var md5_ff = function (a, b, c, d, x, s, t) {
  22681. return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
  22682. };
  22683. var md5_gg = function (a, b, c, d, x, s, t) {
  22684. return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
  22685. };
  22686. var md5_hh = function (a, b, c, d, x, s, t) {
  22687. return md5_cmn(b ^ c ^ d, a, b, x, s, t);
  22688. };
  22689. var md5_ii = function (a, b, c, d, x, s, t) {
  22690. return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
  22691. };
  22692. /*
  22693. * Calculate the MD5 of an array of little-endian words, and a bit length
  22694. */
  22695. var core_md5 = function (x, len) {
  22696. /* append padding */
  22697. x[len >> 5] |= 0x80 << ((len) % 32);
  22698. x[(((len + 64) >>> 9) << 4) + 14] = len;
  22699. var a = 1732584193;
  22700. var b = -271733879;
  22701. var c = -1732584194;
  22702. var d = 271733878;
  22703. var olda, oldb, oldc, oldd;
  22704. for (var i = 0; i < x.length; i += 16)
  22705. {
  22706. olda = a;
  22707. oldb = b;
  22708. oldc = c;
  22709. oldd = d;
  22710. a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
  22711. d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
  22712. c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
  22713. b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
  22714. a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
  22715. d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
  22716. c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
  22717. b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
  22718. a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
  22719. d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
  22720. c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
  22721. b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
  22722. a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
  22723. d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
  22724. c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
  22725. b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
  22726. a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
  22727. d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
  22728. c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
  22729. b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
  22730. a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
  22731. d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
  22732. c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
  22733. b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
  22734. a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
  22735. d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
  22736. c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
  22737. b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
  22738. a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
  22739. d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
  22740. c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
  22741. b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
  22742. a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
  22743. d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
  22744. c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
  22745. b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
  22746. a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
  22747. d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
  22748. c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
  22749. b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
  22750. a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
  22751. d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
  22752. c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
  22753. b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
  22754. a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
  22755. d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
  22756. c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
  22757. b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
  22758. a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
  22759. d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
  22760. c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
  22761. b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
  22762. a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
  22763. d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
  22764. c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
  22765. b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
  22766. a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
  22767. d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
  22768. c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
  22769. b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
  22770. a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
  22771. d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
  22772. c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
  22773. b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
  22774. a = safe_add(a, olda);
  22775. b = safe_add(b, oldb);
  22776. c = safe_add(c, oldc);
  22777. d = safe_add(d, oldd);
  22778. }
  22779. return [a, b, c, d];
  22780. };
  22781. var obj = {
  22782. /*
  22783. * These are the functions you'll usually want to call.
  22784. * They take string arguments and return either hex or base-64 encoded
  22785. * strings.
  22786. */
  22787. hexdigest: function (s) {
  22788. return binl2hex(core_md5(str2binl(s), s.length * 8));
  22789. },
  22790. hash: function (s) {
  22791. return binl2str(core_md5(str2binl(s), s.length * 8));
  22792. }
  22793. };
  22794. return obj;
  22795. })();
  22796. /*
  22797. This program is distributed under the terms of the MIT license.
  22798. Please see the LICENSE file for details.
  22799. Copyright 2006-2008, OGG, LLC
  22800. */
  22801. /* jshint undef: true, unused: true:, noarg: true, latedef: true */
  22802. /*global document, window, setTimeout, clearTimeout, console,
  22803. ActiveXObject, Base64, MD5, DOMParser */
  22804. // from sha1.js
  22805. /*global core_hmac_sha1, binb2str, str_hmac_sha1, str_sha1, b64_hmac_sha1*/
  22806. /** File: strophe.js
  22807. * A JavaScript library for XMPP BOSH/XMPP over Websocket.
  22808. *
  22809. * This is the JavaScript version of the Strophe library. Since JavaScript
  22810. * had no facilities for persistent TCP connections, this library uses
  22811. * Bidirectional-streams Over Synchronous HTTP (BOSH) to emulate
  22812. * a persistent, stateful, two-way connection to an XMPP server. More
  22813. * information on BOSH can be found in XEP 124.
  22814. *
  22815. * This version of Strophe also works with WebSockets.
  22816. * For more information on XMPP-over WebSocket see this RFC draft:
  22817. * http://tools.ietf.org/html/draft-ietf-xmpp-websocket-00
  22818. */
  22819. /** PrivateFunction: Function.prototype.bind
  22820. * Bind a function to an instance.
  22821. *
  22822. * This Function object extension method creates a bound method similar
  22823. * to those in Python. This means that the 'this' object will point
  22824. * to the instance you want. See
  22825. * <a href='https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind'>MDC's bind() documentation</a> and
  22826. * <a href='http://benjamin.smedbergs.us/blog/2007-01-03/bound-functions-and-function-imports-in-javascript/'>Bound Functions and Function Imports in JavaScript</a>
  22827. * for a complete explanation.
  22828. *
  22829. * This extension already exists in some browsers (namely, Firefox 3), but
  22830. * we provide it to support those that don't.
  22831. *
  22832. * Parameters:
  22833. * (Object) obj - The object that will become 'this' in the bound function.
  22834. * (Object) argN - An option argument that will be prepended to the
  22835. * arguments given for the function call
  22836. *
  22837. * Returns:
  22838. * The bound function.
  22839. */
  22840. if (!Function.prototype.bind) {
  22841. Function.prototype.bind = function (obj /*, arg1, arg2, ... */)
  22842. {
  22843. var func = this;
  22844. var _slice = Array.prototype.slice;
  22845. var _concat = Array.prototype.concat;
  22846. var _args = _slice.call(arguments, 1);
  22847. return function () {
  22848. return func.apply(obj ? obj : this,
  22849. _concat.call(_args,
  22850. _slice.call(arguments, 0)));
  22851. };
  22852. };
  22853. }
  22854. /** PrivateFunction: Array.prototype.indexOf
  22855. * Return the index of an object in an array.
  22856. *
  22857. * This function is not supplied by some JavaScript implementations, so
  22858. * we provide it if it is missing. This code is from:
  22859. * http://developer.mozilla.org/En/Core_JavaScript_1.5_Reference:Objects:Array:indexOf
  22860. *
  22861. * Parameters:
  22862. * (Object) elt - The object to look for.
  22863. * (Integer) from - The index from which to start looking. (optional).
  22864. *
  22865. * Returns:
  22866. * The index of elt in the array or -1 if not found.
  22867. */
  22868. if (!Array.prototype.indexOf)
  22869. {
  22870. Array.prototype.indexOf = function(elt /*, from*/)
  22871. {
  22872. var len = this.length;
  22873. var from = Number(arguments[1]) || 0;
  22874. from = (from < 0) ? Math.ceil(from) : Math.floor(from);
  22875. if (from < 0) {
  22876. from += len;
  22877. }
  22878. for (; from < len; from++) {
  22879. if (from in this && this[from] === elt) {
  22880. return from;
  22881. }
  22882. }
  22883. return -1;
  22884. };
  22885. }
  22886. /* All of the Strophe globals are defined in this special function below so
  22887. * that references to the globals become closures. This will ensure that
  22888. * on page reload, these references will still be available to callbacks
  22889. * that are still executing.
  22890. */
  22891. (function (callback) {
  22892. var Strophe;
  22893. /** Function: $build
  22894. * Create a Strophe.Builder.
  22895. * This is an alias for 'new Strophe.Builder(name, attrs)'.
  22896. *
  22897. * Parameters:
  22898. * (String) name - The root element name.
  22899. * (Object) attrs - The attributes for the root element in object notation.
  22900. *
  22901. * Returns:
  22902. * A new Strophe.Builder object.
  22903. */
  22904. function $build(name, attrs) { return new Strophe.Builder(name, attrs); }
  22905. /** Function: $msg
  22906. * Create a Strophe.Builder with a <message/> element as the root.
  22907. *
  22908. * Parmaeters:
  22909. * (Object) attrs - The <message/> element attributes in object notation.
  22910. *
  22911. * Returns:
  22912. * A new Strophe.Builder object.
  22913. */
  22914. function $msg(attrs) { return new Strophe.Builder("message", attrs); }
  22915. /** Function: $iq
  22916. * Create a Strophe.Builder with an <iq/> element as the root.
  22917. *
  22918. * Parameters:
  22919. * (Object) attrs - The <iq/> element attributes in object notation.
  22920. *
  22921. * Returns:
  22922. * A new Strophe.Builder object.
  22923. */
  22924. function $iq(attrs) { return new Strophe.Builder("iq", attrs); }
  22925. /** Function: $pres
  22926. * Create a Strophe.Builder with a <presence/> element as the root.
  22927. *
  22928. * Parameters:
  22929. * (Object) attrs - The <presence/> element attributes in object notation.
  22930. *
  22931. * Returns:
  22932. * A new Strophe.Builder object.
  22933. */
  22934. function $pres(attrs) { return new Strophe.Builder("presence", attrs); }
  22935. /** Class: Strophe
  22936. * An object container for all Strophe library functions.
  22937. *
  22938. * This class is just a container for all the objects and constants
  22939. * used in the library. It is not meant to be instantiated, but to
  22940. * provide a namespace for library objects, constants, and functions.
  22941. */
  22942. Strophe = {
  22943. /** Constant: VERSION
  22944. * The version of the Strophe library. Unreleased builds will have
  22945. * a version of head-HASH where HASH is a partial revision.
  22946. */
  22947. VERSION: "1.1.3",
  22948. /** Constants: XMPP Namespace Constants
  22949. * Common namespace constants from the XMPP RFCs and XEPs.
  22950. *
  22951. * NS.HTTPBIND - HTTP BIND namespace from XEP 124.
  22952. * NS.BOSH - BOSH namespace from XEP 206.
  22953. * NS.CLIENT - Main XMPP client namespace.
  22954. * NS.AUTH - Legacy authentication namespace.
  22955. * NS.ROSTER - Roster operations namespace.
  22956. * NS.PROFILE - Profile namespace.
  22957. * NS.DISCO_INFO - Service discovery info namespace from XEP 30.
  22958. * NS.DISCO_ITEMS - Service discovery items namespace from XEP 30.
  22959. * NS.MUC - Multi-User Chat namespace from XEP 45.
  22960. * NS.SASL - XMPP SASL namespace from RFC 3920.
  22961. * NS.STREAM - XMPP Streams namespace from RFC 3920.
  22962. * NS.BIND - XMPP Binding namespace from RFC 3920.
  22963. * NS.SESSION - XMPP Session namespace from RFC 3920.
  22964. * NS.XHTML_IM - XHTML-IM namespace from XEP 71.
  22965. * NS.XHTML - XHTML body namespace from XEP 71.
  22966. */
  22967. NS: {
  22968. HTTPBIND: "http://jabber.org/protocol/httpbind",
  22969. BOSH: "urn:xmpp:xbosh",
  22970. CLIENT: "jabber:client",
  22971. AUTH: "jabber:iq:auth",
  22972. ROSTER: "jabber:iq:roster",
  22973. PROFILE: "jabber:iq:profile",
  22974. DISCO_INFO: "http://jabber.org/protocol/disco#info",
  22975. DISCO_ITEMS: "http://jabber.org/protocol/disco#items",
  22976. MUC: "http://jabber.org/protocol/muc",
  22977. SASL: "urn:ietf:params:xml:ns:xmpp-sasl",
  22978. STREAM: "http://etherx.jabber.org/streams",
  22979. BIND: "urn:ietf:params:xml:ns:xmpp-bind",
  22980. SESSION: "urn:ietf:params:xml:ns:xmpp-session",
  22981. VERSION: "jabber:iq:version",
  22982. STANZAS: "urn:ietf:params:xml:ns:xmpp-stanzas",
  22983. XHTML_IM: "http://jabber.org/protocol/xhtml-im",
  22984. XHTML: "http://www.w3.org/1999/xhtml"
  22985. },
  22986. /** Constants: XHTML_IM Namespace
  22987. * contains allowed tags, tag attributes, and css properties.
  22988. * Used in the createHtml function to filter incoming html into the allowed XHTML-IM subset.
  22989. * See http://xmpp.org/extensions/xep-0071.html#profile-summary for the list of recommended
  22990. * allowed tags and their attributes.
  22991. */
  22992. XHTML: {
  22993. tags: ['a','blockquote','br','cite','em','img','li','ol','p','span','strong','ul','body'],
  22994. attributes: {
  22995. 'a': ['href'],
  22996. 'blockquote': ['style'],
  22997. 'br': [],
  22998. 'cite': ['style'],
  22999. 'em': [],
  23000. 'img': ['src', 'alt', 'style', 'height', 'width'],
  23001. 'li': ['style'],
  23002. 'ol': ['style'],
  23003. 'p': ['style'],
  23004. 'span': ['style'],
  23005. 'strong': [],
  23006. 'ul': ['style'],
  23007. 'body': []
  23008. },
  23009. css: ['background-color','color','font-family','font-size','font-style','font-weight','margin-left','margin-right','text-align','text-decoration'],
  23010. validTag: function(tag)
  23011. {
  23012. for(var i = 0; i < Strophe.XHTML.tags.length; i++) {
  23013. if(tag == Strophe.XHTML.tags[i]) {
  23014. return true;
  23015. }
  23016. }
  23017. return false;
  23018. },
  23019. validAttribute: function(tag, attribute)
  23020. {
  23021. if(typeof Strophe.XHTML.attributes[tag] !== 'undefined' && Strophe.XHTML.attributes[tag].length > 0) {
  23022. for(var i = 0; i < Strophe.XHTML.attributes[tag].length; i++) {
  23023. if(attribute == Strophe.XHTML.attributes[tag][i]) {
  23024. return true;
  23025. }
  23026. }
  23027. }
  23028. return false;
  23029. },
  23030. validCSS: function(style)
  23031. {
  23032. for(var i = 0; i < Strophe.XHTML.css.length; i++) {
  23033. if(style == Strophe.XHTML.css[i]) {
  23034. return true;
  23035. }
  23036. }
  23037. return false;
  23038. }
  23039. },
  23040. /** Constants: Connection Status Constants
  23041. * Connection status constants for use by the connection handler
  23042. * callback.
  23043. *
  23044. * Status.ERROR - An error has occurred
  23045. * Status.CONNECTING - The connection is currently being made
  23046. * Status.CONNFAIL - The connection attempt failed
  23047. * Status.AUTHENTICATING - The connection is authenticating
  23048. * Status.AUTHFAIL - The authentication attempt failed
  23049. * Status.CONNECTED - The connection has succeeded
  23050. * Status.DISCONNECTED - The connection has been terminated
  23051. * Status.DISCONNECTING - The connection is currently being terminated
  23052. * Status.ATTACHED - The connection has been attached
  23053. */
  23054. Status: {
  23055. ERROR: 0,
  23056. CONNECTING: 1,
  23057. CONNFAIL: 2,
  23058. AUTHENTICATING: 3,
  23059. AUTHFAIL: 4,
  23060. CONNECTED: 5,
  23061. DISCONNECTED: 6,
  23062. DISCONNECTING: 7,
  23063. ATTACHED: 8
  23064. },
  23065. /** Constants: Log Level Constants
  23066. * Logging level indicators.
  23067. *
  23068. * LogLevel.DEBUG - Debug output
  23069. * LogLevel.INFO - Informational output
  23070. * LogLevel.WARN - Warnings
  23071. * LogLevel.ERROR - Errors
  23072. * LogLevel.FATAL - Fatal errors
  23073. */
  23074. LogLevel: {
  23075. DEBUG: 0,
  23076. INFO: 1,
  23077. WARN: 2,
  23078. ERROR: 3,
  23079. FATAL: 4
  23080. },
  23081. /** PrivateConstants: DOM Element Type Constants
  23082. * DOM element types.
  23083. *
  23084. * ElementType.NORMAL - Normal element.
  23085. * ElementType.TEXT - Text data element.
  23086. * ElementType.FRAGMENT - XHTML fragment element.
  23087. */
  23088. ElementType: {
  23089. NORMAL: 1,
  23090. TEXT: 3,
  23091. CDATA: 4,
  23092. FRAGMENT: 11
  23093. },
  23094. /** PrivateConstants: Timeout Values
  23095. * Timeout values for error states. These values are in seconds.
  23096. * These should not be changed unless you know exactly what you are
  23097. * doing.
  23098. *
  23099. * TIMEOUT - Timeout multiplier. A waiting request will be considered
  23100. * failed after Math.floor(TIMEOUT * wait) seconds have elapsed.
  23101. * This defaults to 1.1, and with default wait, 66 seconds.
  23102. * SECONDARY_TIMEOUT - Secondary timeout multiplier. In cases where
  23103. * Strophe can detect early failure, it will consider the request
  23104. * failed if it doesn't return after
  23105. * Math.floor(SECONDARY_TIMEOUT * wait) seconds have elapsed.
  23106. * This defaults to 0.1, and with default wait, 6 seconds.
  23107. */
  23108. TIMEOUT: 1.1,
  23109. SECONDARY_TIMEOUT: 0.1,
  23110. /** Function: addNamespace
  23111. * This function is used to extend the current namespaces in
  23112. * Strophe.NS. It takes a key and a value with the key being the
  23113. * name of the new namespace, with its actual value.
  23114. * For example:
  23115. * Strophe.addNamespace('PUBSUB', "http://jabber.org/protocol/pubsub");
  23116. *
  23117. * Parameters:
  23118. * (String) name - The name under which the namespace will be
  23119. * referenced under Strophe.NS
  23120. * (String) value - The actual namespace.
  23121. */
  23122. addNamespace: function (name, value)
  23123. {
  23124. Strophe.NS[name] = value;
  23125. },
  23126. /** Function: forEachChild
  23127. * Map a function over some or all child elements of a given element.
  23128. *
  23129. * This is a small convenience function for mapping a function over
  23130. * some or all of the children of an element. If elemName is null, all
  23131. * children will be passed to the function, otherwise only children
  23132. * whose tag names match elemName will be passed.
  23133. *
  23134. * Parameters:
  23135. * (XMLElement) elem - The element to operate on.
  23136. * (String) elemName - The child element tag name filter.
  23137. * (Function) func - The function to apply to each child. This
  23138. * function should take a single argument, a DOM element.
  23139. */
  23140. forEachChild: function (elem, elemName, func)
  23141. {
  23142. var i, childNode;
  23143. for (i = 0; i < elem.childNodes.length; i++) {
  23144. childNode = elem.childNodes[i];
  23145. if (childNode.nodeType == Strophe.ElementType.NORMAL &&
  23146. (!elemName || this.isTagEqual(childNode, elemName))) {
  23147. func(childNode);
  23148. }
  23149. }
  23150. },
  23151. /** Function: isTagEqual
  23152. * Compare an element's tag name with a string.
  23153. *
  23154. * This function is case insensitive.
  23155. *
  23156. * Parameters:
  23157. * (XMLElement) el - A DOM element.
  23158. * (String) name - The element name.
  23159. *
  23160. * Returns:
  23161. * true if the element's tag name matches _el_, and false
  23162. * otherwise.
  23163. */
  23164. isTagEqual: function (el, name)
  23165. {
  23166. return el.tagName.toLowerCase() == name.toLowerCase();
  23167. },
  23168. /** PrivateVariable: _xmlGenerator
  23169. * _Private_ variable that caches a DOM document to
  23170. * generate elements.
  23171. */
  23172. _xmlGenerator: null,
  23173. /** PrivateFunction: _makeGenerator
  23174. * _Private_ function that creates a dummy XML DOM document to serve as
  23175. * an element and text node generator.
  23176. */
  23177. _makeGenerator: function () {
  23178. var doc;
  23179. // IE9 does implement createDocument(); however, using it will cause the browser to leak memory on page unload.
  23180. // Here, we test for presence of createDocument() plus IE's proprietary documentMode attribute, which would be
  23181. // less than 10 in the case of IE9 and below.
  23182. if (document.implementation.createDocument === undefined ||
  23183. document.implementation.createDocument && document.documentMode && document.documentMode < 10) {
  23184. doc = this._getIEXmlDom();
  23185. doc.appendChild(doc.createElement('strophe'));
  23186. } else {
  23187. doc = document.implementation
  23188. .createDocument('jabber:client', 'strophe', null);
  23189. }
  23190. return doc;
  23191. },
  23192. /** Function: xmlGenerator
  23193. * Get the DOM document to generate elements.
  23194. *
  23195. * Returns:
  23196. * The currently used DOM document.
  23197. */
  23198. xmlGenerator: function () {
  23199. if (!Strophe._xmlGenerator) {
  23200. Strophe._xmlGenerator = Strophe._makeGenerator();
  23201. }
  23202. return Strophe._xmlGenerator;
  23203. },
  23204. /** PrivateFunction: _getIEXmlDom
  23205. * Gets IE xml doc object
  23206. *
  23207. * Returns:
  23208. * A Microsoft XML DOM Object
  23209. * See Also:
  23210. * http://msdn.microsoft.com/en-us/library/ms757837%28VS.85%29.aspx
  23211. */
  23212. _getIEXmlDom : function() {
  23213. var doc = null;
  23214. var docStrings = [
  23215. "Msxml2.DOMDocument.6.0",
  23216. "Msxml2.DOMDocument.5.0",
  23217. "Msxml2.DOMDocument.4.0",
  23218. "MSXML2.DOMDocument.3.0",
  23219. "MSXML2.DOMDocument",
  23220. "MSXML.DOMDocument",
  23221. "Microsoft.XMLDOM"
  23222. ];
  23223. for (var d = 0; d < docStrings.length; d++) {
  23224. if (doc === null) {
  23225. try {
  23226. doc = new ActiveXObject(docStrings[d]);
  23227. } catch (e) {
  23228. doc = null;
  23229. }
  23230. } else {
  23231. break;
  23232. }
  23233. }
  23234. return doc;
  23235. },
  23236. /** Function: xmlElement
  23237. * Create an XML DOM element.
  23238. *
  23239. * This function creates an XML DOM element correctly across all
  23240. * implementations. Note that these are not HTML DOM elements, which
  23241. * aren't appropriate for XMPP stanzas.
  23242. *
  23243. * Parameters:
  23244. * (String) name - The name for the element.
  23245. * (Array|Object) attrs - An optional array or object containing
  23246. * key/value pairs to use as element attributes. The object should
  23247. * be in the format {'key': 'value'} or {key: 'value'}. The array
  23248. * should have the format [['key1', 'value1'], ['key2', 'value2']].
  23249. * (String) text - The text child data for the element.
  23250. *
  23251. * Returns:
  23252. * A new XML DOM element.
  23253. */
  23254. xmlElement: function (name)
  23255. {
  23256. if (!name) { return null; }
  23257. var node = Strophe.xmlGenerator().createElement(name);
  23258. // FIXME: this should throw errors if args are the wrong type or
  23259. // there are more than two optional args
  23260. var a, i, k;
  23261. for (a = 1; a < arguments.length; a++) {
  23262. if (!arguments[a]) { continue; }
  23263. if (typeof(arguments[a]) == "string" ||
  23264. typeof(arguments[a]) == "number") {
  23265. node.appendChild(Strophe.xmlTextNode(arguments[a]));
  23266. } else if (typeof(arguments[a]) == "object" &&
  23267. typeof(arguments[a].sort) == "function") {
  23268. for (i = 0; i < arguments[a].length; i++) {
  23269. if (typeof(arguments[a][i]) == "object" &&
  23270. typeof(arguments[a][i].sort) == "function") {
  23271. node.setAttribute(arguments[a][i][0],
  23272. arguments[a][i][1]);
  23273. }
  23274. }
  23275. } else if (typeof(arguments[a]) == "object") {
  23276. for (k in arguments[a]) {
  23277. if (arguments[a].hasOwnProperty(k)) {
  23278. node.setAttribute(k, arguments[a][k]);
  23279. }
  23280. }
  23281. }
  23282. }
  23283. return node;
  23284. },
  23285. /* Function: xmlescape
  23286. * Excapes invalid xml characters.
  23287. *
  23288. * Parameters:
  23289. * (String) text - text to escape.
  23290. *
  23291. * Returns:
  23292. * Escaped text.
  23293. */
  23294. xmlescape: function(text)
  23295. {
  23296. text = text.replace(/\&/g, "&amp;");
  23297. text = text.replace(/</g, "&lt;");
  23298. text = text.replace(/>/g, "&gt;");
  23299. text = text.replace(/'/g, "&apos;");
  23300. text = text.replace(/"/g, "&quot;");
  23301. return text;
  23302. },
  23303. /** Function: xmlTextNode
  23304. * Creates an XML DOM text node.
  23305. *
  23306. * Provides a cross implementation version of document.createTextNode.
  23307. *
  23308. * Parameters:
  23309. * (String) text - The content of the text node.
  23310. *
  23311. * Returns:
  23312. * A new XML DOM text node.
  23313. */
  23314. xmlTextNode: function (text)
  23315. {
  23316. return Strophe.xmlGenerator().createTextNode(text);
  23317. },
  23318. /** Function: xmlHtmlNode
  23319. * Creates an XML DOM html node.
  23320. *
  23321. * Parameters:
  23322. * (String) html - The content of the html node.
  23323. *
  23324. * Returns:
  23325. * A new XML DOM text node.
  23326. */
  23327. xmlHtmlNode: function (html)
  23328. {
  23329. var node;
  23330. //ensure text is escaped
  23331. if (window.DOMParser) {
  23332. var parser = new DOMParser();
  23333. node = parser.parseFromString(html, "text/xml");
  23334. } else {
  23335. node = new ActiveXObject("Microsoft.XMLDOM");
  23336. node.async="false";
  23337. node.loadXML(html);
  23338. }
  23339. return node;
  23340. },
  23341. /** Function: getText
  23342. * Get the concatenation of all text children of an element.
  23343. *
  23344. * Parameters:
  23345. * (XMLElement) elem - A DOM element.
  23346. *
  23347. * Returns:
  23348. * A String with the concatenated text of all text element children.
  23349. */
  23350. getText: function (elem)
  23351. {
  23352. if (!elem) { return null; }
  23353. var str = "";
  23354. if (elem.childNodes.length === 0 && elem.nodeType ==
  23355. Strophe.ElementType.TEXT) {
  23356. str += elem.nodeValue;
  23357. }
  23358. for (var i = 0; i < elem.childNodes.length; i++) {
  23359. if (elem.childNodes[i].nodeType == Strophe.ElementType.TEXT) {
  23360. str += elem.childNodes[i].nodeValue;
  23361. }
  23362. }
  23363. return Strophe.xmlescape(str);
  23364. },
  23365. /** Function: copyElement
  23366. * Copy an XML DOM element.
  23367. *
  23368. * This function copies a DOM element and all its descendants and returns
  23369. * the new copy.
  23370. *
  23371. * Parameters:
  23372. * (XMLElement) elem - A DOM element.
  23373. *
  23374. * Returns:
  23375. * A new, copied DOM element tree.
  23376. */
  23377. copyElement: function (elem)
  23378. {
  23379. var i, el;
  23380. if (elem.nodeType == Strophe.ElementType.NORMAL) {
  23381. el = Strophe.xmlElement(elem.tagName);
  23382. for (i = 0; i < elem.attributes.length; i++) {
  23383. el.setAttribute(elem.attributes[i].nodeName.toLowerCase(),
  23384. elem.attributes[i].value);
  23385. }
  23386. for (i = 0; i < elem.childNodes.length; i++) {
  23387. el.appendChild(Strophe.copyElement(elem.childNodes[i]));
  23388. }
  23389. } else if (elem.nodeType == Strophe.ElementType.TEXT) {
  23390. el = Strophe.xmlGenerator().createTextNode(elem.nodeValue);
  23391. }
  23392. return el;
  23393. },
  23394. /** Function: createHtml
  23395. * Copy an HTML DOM element into an XML DOM.
  23396. *
  23397. * This function copies a DOM element and all its descendants and returns
  23398. * the new copy.
  23399. *
  23400. * Parameters:
  23401. * (HTMLElement) elem - A DOM element.
  23402. *
  23403. * Returns:
  23404. * A new, copied DOM element tree.
  23405. */
  23406. createHtml: function (elem)
  23407. {
  23408. var i, el, j, tag, attribute, value, css, cssAttrs, attr, cssName, cssValue;
  23409. if (elem.nodeType == Strophe.ElementType.NORMAL) {
  23410. tag = elem.nodeName.toLowerCase();
  23411. if(Strophe.XHTML.validTag(tag)) {
  23412. try {
  23413. el = Strophe.xmlElement(tag);
  23414. for(i = 0; i < Strophe.XHTML.attributes[tag].length; i++) {
  23415. attribute = Strophe.XHTML.attributes[tag][i];
  23416. value = elem.getAttribute(attribute);
  23417. if(typeof value == 'undefined' || value === null || value === '' || value === false || value === 0) {
  23418. continue;
  23419. }
  23420. if(attribute == 'style' && typeof value == 'object') {
  23421. if(typeof value.cssText != 'undefined') {
  23422. value = value.cssText; // we're dealing with IE, need to get CSS out
  23423. }
  23424. }
  23425. // filter out invalid css styles
  23426. if(attribute == 'style') {
  23427. css = [];
  23428. cssAttrs = value.split(';');
  23429. for(j = 0; j < cssAttrs.length; j++) {
  23430. attr = cssAttrs[j].split(':');
  23431. cssName = attr[0].replace(/^\s*/, "").replace(/\s*$/, "").toLowerCase();
  23432. if(Strophe.XHTML.validCSS(cssName)) {
  23433. cssValue = attr[1].replace(/^\s*/, "").replace(/\s*$/, "");
  23434. css.push(cssName + ': ' + cssValue);
  23435. }
  23436. }
  23437. if(css.length > 0) {
  23438. value = css.join('; ');
  23439. el.setAttribute(attribute, value);
  23440. }
  23441. } else {
  23442. el.setAttribute(attribute, value);
  23443. }
  23444. }
  23445. for (i = 0; i < elem.childNodes.length; i++) {
  23446. el.appendChild(Strophe.createHtml(elem.childNodes[i]));
  23447. }
  23448. } catch(e) { // invalid elements
  23449. el = Strophe.xmlTextNode('');
  23450. }
  23451. } else {
  23452. el = Strophe.xmlGenerator().createDocumentFragment();
  23453. for (i = 0; i < elem.childNodes.length; i++) {
  23454. el.appendChild(Strophe.createHtml(elem.childNodes[i]));
  23455. }
  23456. }
  23457. } else if (elem.nodeType == Strophe.ElementType.FRAGMENT) {
  23458. el = Strophe.xmlGenerator().createDocumentFragment();
  23459. for (i = 0; i < elem.childNodes.length; i++) {
  23460. el.appendChild(Strophe.createHtml(elem.childNodes[i]));
  23461. }
  23462. } else if (elem.nodeType == Strophe.ElementType.TEXT) {
  23463. el = Strophe.xmlTextNode(elem.nodeValue);
  23464. }
  23465. return el;
  23466. },
  23467. /** Function: escapeNode
  23468. * Escape the node part (also called local part) of a JID.
  23469. *
  23470. * Parameters:
  23471. * (String) node - A node (or local part).
  23472. *
  23473. * Returns:
  23474. * An escaped node (or local part).
  23475. */
  23476. escapeNode: function (node)
  23477. {
  23478. return node.replace(/^\s+|\s+$/g, '')
  23479. .replace(/\\/g, "\\5c")
  23480. .replace(/ /g, "\\20")
  23481. .replace(/\"/g, "\\22")
  23482. .replace(/\&/g, "\\26")
  23483. .replace(/\'/g, "\\27")
  23484. .replace(/\//g, "\\2f")
  23485. .replace(/:/g, "\\3a")
  23486. .replace(/</g, "\\3c")
  23487. .replace(/>/g, "\\3e")
  23488. .replace(/@/g, "\\40");
  23489. },
  23490. /** Function: unescapeNode
  23491. * Unescape a node part (also called local part) of a JID.
  23492. *
  23493. * Parameters:
  23494. * (String) node - A node (or local part).
  23495. *
  23496. * Returns:
  23497. * An unescaped node (or local part).
  23498. */
  23499. unescapeNode: function (node)
  23500. {
  23501. return node.replace(/\\20/g, " ")
  23502. .replace(/\\22/g, '"')
  23503. .replace(/\\26/g, "&")
  23504. .replace(/\\27/g, "'")
  23505. .replace(/\\2f/g, "/")
  23506. .replace(/\\3a/g, ":")
  23507. .replace(/\\3c/g, "<")
  23508. .replace(/\\3e/g, ">")
  23509. .replace(/\\40/g, "@")
  23510. .replace(/\\5c/g, "\\");
  23511. },
  23512. /** Function: getNodeFromJid
  23513. * Get the node portion of a JID String.
  23514. *
  23515. * Parameters:
  23516. * (String) jid - A JID.
  23517. *
  23518. * Returns:
  23519. * A String containing the node.
  23520. */
  23521. getNodeFromJid: function (jid)
  23522. {
  23523. if (jid.indexOf("@") < 0) { return null; }
  23524. return jid.split("@")[0];
  23525. },
  23526. /** Function: getDomainFromJid
  23527. * Get the domain portion of a JID String.
  23528. *
  23529. * Parameters:
  23530. * (String) jid - A JID.
  23531. *
  23532. * Returns:
  23533. * A String containing the domain.
  23534. */
  23535. getDomainFromJid: function (jid)
  23536. {
  23537. var bare = Strophe.getBareJidFromJid(jid);
  23538. if (bare.indexOf("@") < 0) {
  23539. return bare;
  23540. } else {
  23541. var parts = bare.split("@");
  23542. parts.splice(0, 1);
  23543. return parts.join('@');
  23544. }
  23545. },
  23546. /** Function: getResourceFromJid
  23547. * Get the resource portion of a JID String.
  23548. *
  23549. * Parameters:
  23550. * (String) jid - A JID.
  23551. *
  23552. * Returns:
  23553. * A String containing the resource.
  23554. */
  23555. getResourceFromJid: function (jid)
  23556. {
  23557. var s = jid.split("/");
  23558. if (s.length < 2) { return null; }
  23559. s.splice(0, 1);
  23560. return s.join('/');
  23561. },
  23562. /** Function: getBareJidFromJid
  23563. * Get the bare JID from a JID String.
  23564. *
  23565. * Parameters:
  23566. * (String) jid - A JID.
  23567. *
  23568. * Returns:
  23569. * A String containing the bare JID.
  23570. */
  23571. getBareJidFromJid: function (jid)
  23572. {
  23573. return jid ? jid.split("/")[0] : null;
  23574. },
  23575. /** Function: log
  23576. * User overrideable logging function.
  23577. *
  23578. * This function is called whenever the Strophe library calls any
  23579. * of the logging functions. The default implementation of this
  23580. * function does nothing. If client code wishes to handle the logging
  23581. * messages, it should override this with
  23582. * > Strophe.log = function (level, msg) {
  23583. * > (user code here)
  23584. * > };
  23585. *
  23586. * Please note that data sent and received over the wire is logged
  23587. * via Strophe.Connection.rawInput() and Strophe.Connection.rawOutput().
  23588. *
  23589. * The different levels and their meanings are
  23590. *
  23591. * DEBUG - Messages useful for debugging purposes.
  23592. * INFO - Informational messages. This is mostly information like
  23593. * 'disconnect was called' or 'SASL auth succeeded'.
  23594. * WARN - Warnings about potential problems. This is mostly used
  23595. * to report transient connection errors like request timeouts.
  23596. * ERROR - Some error occurred.
  23597. * FATAL - A non-recoverable fatal error occurred.
  23598. *
  23599. * Parameters:
  23600. * (Integer) level - The log level of the log message. This will
  23601. * be one of the values in Strophe.LogLevel.
  23602. * (String) msg - The log message.
  23603. */
  23604. /* jshint ignore:start */
  23605. log: function (level, msg)
  23606. {
  23607. return;
  23608. },
  23609. /* jshint ignore:end */
  23610. /** Function: debug
  23611. * Log a message at the Strophe.LogLevel.DEBUG level.
  23612. *
  23613. * Parameters:
  23614. * (String) msg - The log message.
  23615. */
  23616. debug: function(msg)
  23617. {
  23618. this.log(this.LogLevel.DEBUG, msg);
  23619. },
  23620. /** Function: info
  23621. * Log a message at the Strophe.LogLevel.INFO level.
  23622. *
  23623. * Parameters:
  23624. * (String) msg - The log message.
  23625. */
  23626. info: function (msg)
  23627. {
  23628. this.log(this.LogLevel.INFO, msg);
  23629. },
  23630. /** Function: warn
  23631. * Log a message at the Strophe.LogLevel.WARN level.
  23632. *
  23633. * Parameters:
  23634. * (String) msg - The log message.
  23635. */
  23636. warn: function (msg)
  23637. {
  23638. this.log(this.LogLevel.WARN, msg);
  23639. },
  23640. /** Function: error
  23641. * Log a message at the Strophe.LogLevel.ERROR level.
  23642. *
  23643. * Parameters:
  23644. * (String) msg - The log message.
  23645. */
  23646. error: function (msg)
  23647. {
  23648. this.log(this.LogLevel.ERROR, msg);
  23649. },
  23650. /** Function: fatal
  23651. * Log a message at the Strophe.LogLevel.FATAL level.
  23652. *
  23653. * Parameters:
  23654. * (String) msg - The log message.
  23655. */
  23656. fatal: function (msg)
  23657. {
  23658. this.log(this.LogLevel.FATAL, msg);
  23659. },
  23660. /** Function: serialize
  23661. * Render a DOM element and all descendants to a String.
  23662. *
  23663. * Parameters:
  23664. * (XMLElement) elem - A DOM element.
  23665. *
  23666. * Returns:
  23667. * The serialized element tree as a String.
  23668. */
  23669. serialize: function (elem)
  23670. {
  23671. var result;
  23672. if (!elem) { return null; }
  23673. if (typeof(elem.tree) === "function") {
  23674. elem = elem.tree();
  23675. }
  23676. var nodeName = elem.nodeName;
  23677. var i, child;
  23678. if (elem.getAttribute("_realname")) {
  23679. nodeName = elem.getAttribute("_realname");
  23680. }
  23681. result = "<" + nodeName;
  23682. for (i = 0; i < elem.attributes.length; i++) {
  23683. if(elem.attributes[i].nodeName != "_realname") {
  23684. result += " " + elem.attributes[i].nodeName.toLowerCase() +
  23685. "='" + elem.attributes[i].value
  23686. .replace(/&/g, "&amp;")
  23687. .replace(/\'/g, "&apos;")
  23688. .replace(/>/g, "&gt;")
  23689. .replace(/</g, "&lt;") + "'";
  23690. }
  23691. }
  23692. if (elem.childNodes.length > 0) {
  23693. result += ">";
  23694. for (i = 0; i < elem.childNodes.length; i++) {
  23695. child = elem.childNodes[i];
  23696. switch( child.nodeType ){
  23697. case Strophe.ElementType.NORMAL:
  23698. // normal element, so recurse
  23699. result += Strophe.serialize(child);
  23700. break;
  23701. case Strophe.ElementType.TEXT:
  23702. // text element to escape values
  23703. result += Strophe.xmlescape(child.nodeValue);
  23704. break;
  23705. case Strophe.ElementType.CDATA:
  23706. // cdata section so don't escape values
  23707. result += "<![CDATA["+child.nodeValue+"]]>";
  23708. }
  23709. }
  23710. result += "</" + nodeName + ">";
  23711. } else {
  23712. result += "/>";
  23713. }
  23714. return result;
  23715. },
  23716. /** PrivateVariable: _requestId
  23717. * _Private_ variable that keeps track of the request ids for
  23718. * connections.
  23719. */
  23720. _requestId: 0,
  23721. /** PrivateVariable: Strophe.connectionPlugins
  23722. * _Private_ variable Used to store plugin names that need
  23723. * initialization on Strophe.Connection construction.
  23724. */
  23725. _connectionPlugins: {},
  23726. /** Function: addConnectionPlugin
  23727. * Extends the Strophe.Connection object with the given plugin.
  23728. *
  23729. * Parameters:
  23730. * (String) name - The name of the extension.
  23731. * (Object) ptype - The plugin's prototype.
  23732. */
  23733. addConnectionPlugin: function (name, ptype)
  23734. {
  23735. Strophe._connectionPlugins[name] = ptype;
  23736. }
  23737. };
  23738. /** Class: Strophe.Builder
  23739. * XML DOM builder.
  23740. *
  23741. * This object provides an interface similar to JQuery but for building
  23742. * DOM element easily and rapidly. All the functions except for toString()
  23743. * and tree() return the object, so calls can be chained. Here's an
  23744. * example using the $iq() builder helper.
  23745. * > $iq({to: 'you', from: 'me', type: 'get', id: '1'})
  23746. * > .c('query', {xmlns: 'strophe:example'})
  23747. * > .c('example')
  23748. * > .toString()
  23749. * The above generates this XML fragment
  23750. * > <iq to='you' from='me' type='get' id='1'>
  23751. * > <query xmlns='strophe:example'>
  23752. * > <example/>
  23753. * > </query>
  23754. * > </iq>
  23755. * The corresponding DOM manipulations to get a similar fragment would be
  23756. * a lot more tedious and probably involve several helper variables.
  23757. *
  23758. * Since adding children makes new operations operate on the child, up()
  23759. * is provided to traverse up the tree. To add two children, do
  23760. * > builder.c('child1', ...).up().c('child2', ...)
  23761. * The next operation on the Builder will be relative to the second child.
  23762. */
  23763. /** Constructor: Strophe.Builder
  23764. * Create a Strophe.Builder object.
  23765. *
  23766. * The attributes should be passed in object notation. For example
  23767. * > var b = new Builder('message', {to: 'you', from: 'me'});
  23768. * or
  23769. * > var b = new Builder('messsage', {'xml:lang': 'en'});
  23770. *
  23771. * Parameters:
  23772. * (String) name - The name of the root element.
  23773. * (Object) attrs - The attributes for the root element in object notation.
  23774. *
  23775. * Returns:
  23776. * A new Strophe.Builder.
  23777. */
  23778. Strophe.Builder = function (name, attrs)
  23779. {
  23780. // Set correct namespace for jabber:client elements
  23781. if (name == "presence" || name == "message" || name == "iq") {
  23782. if (attrs && !attrs.xmlns) {
  23783. attrs.xmlns = Strophe.NS.CLIENT;
  23784. } else if (!attrs) {
  23785. attrs = {xmlns: Strophe.NS.CLIENT};
  23786. }
  23787. }
  23788. // Holds the tree being built.
  23789. this.nodeTree = Strophe.xmlElement(name, attrs);
  23790. // Points to the current operation node.
  23791. this.node = this.nodeTree;
  23792. };
  23793. Strophe.Builder.prototype = {
  23794. /** Function: tree
  23795. * Return the DOM tree.
  23796. *
  23797. * This function returns the current DOM tree as an element object. This
  23798. * is suitable for passing to functions like Strophe.Connection.send().
  23799. *
  23800. * Returns:
  23801. * The DOM tree as a element object.
  23802. */
  23803. tree: function ()
  23804. {
  23805. return this.nodeTree;
  23806. },
  23807. /** Function: toString
  23808. * Serialize the DOM tree to a String.
  23809. *
  23810. * This function returns a string serialization of the current DOM
  23811. * tree. It is often used internally to pass data to a
  23812. * Strophe.Request object.
  23813. *
  23814. * Returns:
  23815. * The serialized DOM tree in a String.
  23816. */
  23817. toString: function ()
  23818. {
  23819. return Strophe.serialize(this.nodeTree);
  23820. },
  23821. /** Function: up
  23822. * Make the current parent element the new current element.
  23823. *
  23824. * This function is often used after c() to traverse back up the tree.
  23825. * For example, to add two children to the same element
  23826. * > builder.c('child1', {}).up().c('child2', {});
  23827. *
  23828. * Returns:
  23829. * The Stophe.Builder object.
  23830. */
  23831. up: function ()
  23832. {
  23833. this.node = this.node.parentNode;
  23834. return this;
  23835. },
  23836. /** Function: attrs
  23837. * Add or modify attributes of the current element.
  23838. *
  23839. * The attributes should be passed in object notation. This function
  23840. * does not move the current element pointer.
  23841. *
  23842. * Parameters:
  23843. * (Object) moreattrs - The attributes to add/modify in object notation.
  23844. *
  23845. * Returns:
  23846. * The Strophe.Builder object.
  23847. */
  23848. attrs: function (moreattrs)
  23849. {
  23850. for (var k in moreattrs) {
  23851. if (moreattrs.hasOwnProperty(k)) {
  23852. this.node.setAttribute(k, moreattrs[k]);
  23853. }
  23854. }
  23855. return this;
  23856. },
  23857. /** Function: c
  23858. * Add a child to the current element and make it the new current
  23859. * element.
  23860. *
  23861. * This function moves the current element pointer to the child,
  23862. * unless text is provided. If you need to add another child, it
  23863. * is necessary to use up() to go back to the parent in the tree.
  23864. *
  23865. * Parameters:
  23866. * (String) name - The name of the child.
  23867. * (Object) attrs - The attributes of the child in object notation.
  23868. * (String) text - The text to add to the child.
  23869. *
  23870. * Returns:
  23871. * The Strophe.Builder object.
  23872. */
  23873. c: function (name, attrs, text)
  23874. {
  23875. var child = Strophe.xmlElement(name, attrs, text);
  23876. this.node.appendChild(child);
  23877. if (!text) {
  23878. this.node = child;
  23879. }
  23880. return this;
  23881. },
  23882. /** Function: cnode
  23883. * Add a child to the current element and make it the new current
  23884. * element.
  23885. *
  23886. * This function is the same as c() except that instead of using a
  23887. * name and an attributes object to create the child it uses an
  23888. * existing DOM element object.
  23889. *
  23890. * Parameters:
  23891. * (XMLElement) elem - A DOM element.
  23892. *
  23893. * Returns:
  23894. * The Strophe.Builder object.
  23895. */
  23896. cnode: function (elem)
  23897. {
  23898. var impNode;
  23899. var xmlGen = Strophe.xmlGenerator();
  23900. try {
  23901. impNode = (xmlGen.importNode !== undefined);
  23902. }
  23903. catch (e) {
  23904. impNode = false;
  23905. }
  23906. var newElem = impNode ?
  23907. xmlGen.importNode(elem, true) :
  23908. Strophe.copyElement(elem);
  23909. this.node.appendChild(newElem);
  23910. this.node = newElem;
  23911. return this;
  23912. },
  23913. /** Function: t
  23914. * Add a child text element.
  23915. *
  23916. * This *does not* make the child the new current element since there
  23917. * are no children of text elements.
  23918. *
  23919. * Parameters:
  23920. * (String) text - The text data to append to the current element.
  23921. *
  23922. * Returns:
  23923. * The Strophe.Builder object.
  23924. */
  23925. t: function (text)
  23926. {
  23927. var child = Strophe.xmlTextNode(text);
  23928. this.node.appendChild(child);
  23929. return this;
  23930. },
  23931. /** Function: h
  23932. * Replace current element contents with the HTML passed in.
  23933. *
  23934. * This *does not* make the child the new current element
  23935. *
  23936. * Parameters:
  23937. * (String) html - The html to insert as contents of current element.
  23938. *
  23939. * Returns:
  23940. * The Strophe.Builder object.
  23941. */
  23942. h: function (html)
  23943. {
  23944. var fragment = document.createElement('body');
  23945. // force the browser to try and fix any invalid HTML tags
  23946. fragment.innerHTML = html;
  23947. // copy cleaned html into an xml dom
  23948. var xhtml = Strophe.createHtml(fragment);
  23949. while(xhtml.childNodes.length > 0) {
  23950. this.node.appendChild(xhtml.childNodes[0]);
  23951. }
  23952. return this;
  23953. }
  23954. };
  23955. /** PrivateClass: Strophe.Handler
  23956. * _Private_ helper class for managing stanza handlers.
  23957. *
  23958. * A Strophe.Handler encapsulates a user provided callback function to be
  23959. * executed when matching stanzas are received by the connection.
  23960. * Handlers can be either one-off or persistant depending on their
  23961. * return value. Returning true will cause a Handler to remain active, and
  23962. * returning false will remove the Handler.
  23963. *
  23964. * Users will not use Strophe.Handler objects directly, but instead they
  23965. * will use Strophe.Connection.addHandler() and
  23966. * Strophe.Connection.deleteHandler().
  23967. */
  23968. /** PrivateConstructor: Strophe.Handler
  23969. * Create and initialize a new Strophe.Handler.
  23970. *
  23971. * Parameters:
  23972. * (Function) handler - A function to be executed when the handler is run.
  23973. * (String) ns - The namespace to match.
  23974. * (String) name - The element name to match.
  23975. * (String) type - The element type to match.
  23976. * (String) id - The element id attribute to match.
  23977. * (String) from - The element from attribute to match.
  23978. * (Object) options - Handler options
  23979. *
  23980. * Returns:
  23981. * A new Strophe.Handler object.
  23982. */
  23983. Strophe.Handler = function (handler, ns, name, type, id, from, options)
  23984. {
  23985. this.handler = handler;
  23986. this.ns = ns;
  23987. this.name = name;
  23988. this.type = type;
  23989. this.id = id;
  23990. this.options = options || {matchBare: false};
  23991. // default matchBare to false if undefined
  23992. if (!this.options.matchBare) {
  23993. this.options.matchBare = false;
  23994. }
  23995. if (this.options.matchBare) {
  23996. this.from = from ? Strophe.getBareJidFromJid(from) : null;
  23997. } else {
  23998. this.from = from;
  23999. }
  24000. // whether the handler is a user handler or a system handler
  24001. this.user = true;
  24002. };
  24003. Strophe.Handler.prototype = {
  24004. /** PrivateFunction: isMatch
  24005. * Tests if a stanza matches the Strophe.Handler.
  24006. *
  24007. * Parameters:
  24008. * (XMLElement) elem - The XML element to test.
  24009. *
  24010. * Returns:
  24011. * true if the stanza matches and false otherwise.
  24012. */
  24013. isMatch: function (elem)
  24014. {
  24015. var nsMatch;
  24016. var from = null;
  24017. if (this.options.matchBare) {
  24018. from = Strophe.getBareJidFromJid(elem.getAttribute('from'));
  24019. } else {
  24020. from = elem.getAttribute('from');
  24021. }
  24022. nsMatch = false;
  24023. if (!this.ns) {
  24024. nsMatch = true;
  24025. } else {
  24026. var that = this;
  24027. Strophe.forEachChild(elem, null, function (elem) {
  24028. if (elem.getAttribute("xmlns") == that.ns) {
  24029. nsMatch = true;
  24030. }
  24031. });
  24032. nsMatch = nsMatch || elem.getAttribute("xmlns") == this.ns;
  24033. }
  24034. if (nsMatch &&
  24035. (!this.name || Strophe.isTagEqual(elem, this.name)) &&
  24036. (!this.type || elem.getAttribute("type") == this.type) &&
  24037. (!this.id || elem.getAttribute("id") == this.id) &&
  24038. (!this.from || from == this.from)) {
  24039. return true;
  24040. }
  24041. return false;
  24042. },
  24043. /** PrivateFunction: run
  24044. * Run the callback on a matching stanza.
  24045. *
  24046. * Parameters:
  24047. * (XMLElement) elem - The DOM element that triggered the
  24048. * Strophe.Handler.
  24049. *
  24050. * Returns:
  24051. * A boolean indicating if the handler should remain active.
  24052. */
  24053. run: function (elem)
  24054. {
  24055. var result = null;
  24056. try {
  24057. result = this.handler(elem);
  24058. } catch (e) {
  24059. if (e.sourceURL) {
  24060. Strophe.fatal("error: " + this.handler +
  24061. " " + e.sourceURL + ":" +
  24062. e.line + " - " + e.name + ": " + e.message);
  24063. } else if (e.fileName) {
  24064. if (typeof(console) != "undefined") {
  24065. console.trace();
  24066. console.error(this.handler, " - error - ", e, e.message);
  24067. }
  24068. Strophe.fatal("error: " + this.handler + " " +
  24069. e.fileName + ":" + e.lineNumber + " - " +
  24070. e.name + ": " + e.message);
  24071. } else {
  24072. Strophe.fatal("error: " + e.message + "\n" + e.stack);
  24073. }
  24074. throw e;
  24075. }
  24076. return result;
  24077. },
  24078. /** PrivateFunction: toString
  24079. * Get a String representation of the Strophe.Handler object.
  24080. *
  24081. * Returns:
  24082. * A String.
  24083. */
  24084. toString: function ()
  24085. {
  24086. return "{Handler: " + this.handler + "(" + this.name + "," +
  24087. this.id + "," + this.ns + ")}";
  24088. }
  24089. };
  24090. /** PrivateClass: Strophe.TimedHandler
  24091. * _Private_ helper class for managing timed handlers.
  24092. *
  24093. * A Strophe.TimedHandler encapsulates a user provided callback that
  24094. * should be called after a certain period of time or at regular
  24095. * intervals. The return value of the callback determines whether the
  24096. * Strophe.TimedHandler will continue to fire.
  24097. *
  24098. * Users will not use Strophe.TimedHandler objects directly, but instead
  24099. * they will use Strophe.Connection.addTimedHandler() and
  24100. * Strophe.Connection.deleteTimedHandler().
  24101. */
  24102. /** PrivateConstructor: Strophe.TimedHandler
  24103. * Create and initialize a new Strophe.TimedHandler object.
  24104. *
  24105. * Parameters:
  24106. * (Integer) period - The number of milliseconds to wait before the
  24107. * handler is called.
  24108. * (Function) handler - The callback to run when the handler fires. This
  24109. * function should take no arguments.
  24110. *
  24111. * Returns:
  24112. * A new Strophe.TimedHandler object.
  24113. */
  24114. Strophe.TimedHandler = function (period, handler)
  24115. {
  24116. this.period = period;
  24117. this.handler = handler;
  24118. this.lastCalled = new Date().getTime();
  24119. this.user = true;
  24120. };
  24121. Strophe.TimedHandler.prototype = {
  24122. /** PrivateFunction: run
  24123. * Run the callback for the Strophe.TimedHandler.
  24124. *
  24125. * Returns:
  24126. * true if the Strophe.TimedHandler should be called again, and false
  24127. * otherwise.
  24128. */
  24129. run: function ()
  24130. {
  24131. this.lastCalled = new Date().getTime();
  24132. return this.handler();
  24133. },
  24134. /** PrivateFunction: reset
  24135. * Reset the last called time for the Strophe.TimedHandler.
  24136. */
  24137. reset: function ()
  24138. {
  24139. this.lastCalled = new Date().getTime();
  24140. },
  24141. /** PrivateFunction: toString
  24142. * Get a string representation of the Strophe.TimedHandler object.
  24143. *
  24144. * Returns:
  24145. * The string representation.
  24146. */
  24147. toString: function ()
  24148. {
  24149. return "{TimedHandler: " + this.handler + "(" + this.period +")}";
  24150. }
  24151. };
  24152. /** Class: Strophe.Connection
  24153. * XMPP Connection manager.
  24154. *
  24155. * This class is the main part of Strophe. It manages a BOSH connection
  24156. * to an XMPP server and dispatches events to the user callbacks as
  24157. * data arrives. It supports SASL PLAIN, SASL DIGEST-MD5, SASL SCRAM-SHA1
  24158. * and legacy authentication.
  24159. *
  24160. * After creating a Strophe.Connection object, the user will typically
  24161. * call connect() with a user supplied callback to handle connection level
  24162. * events like authentication failure, disconnection, or connection
  24163. * complete.
  24164. *
  24165. * The user will also have several event handlers defined by using
  24166. * addHandler() and addTimedHandler(). These will allow the user code to
  24167. * respond to interesting stanzas or do something periodically with the
  24168. * connection. These handlers will be active once authentication is
  24169. * finished.
  24170. *
  24171. * To send data to the connection, use send().
  24172. */
  24173. /** Constructor: Strophe.Connection
  24174. * Create and initialize a Strophe.Connection object.
  24175. *
  24176. * The transport-protocol for this connection will be chosen automatically
  24177. * based on the given service parameter. URLs starting with "ws://" or
  24178. * "wss://" will use WebSockets, URLs starting with "http://", "https://"
  24179. * or without a protocol will use BOSH.
  24180. *
  24181. * To make Strophe connect to the current host you can leave out the protocol
  24182. * and host part and just pass the path, e.g.
  24183. *
  24184. * > var conn = new Strophe.Connection("/http-bind/");
  24185. *
  24186. * WebSocket options:
  24187. *
  24188. * If you want to connect to the current host with a WebSocket connection you
  24189. * can tell Strophe to use WebSockets through a "protocol" attribute in the
  24190. * optional options parameter. Valid values are "ws" for WebSocket and "wss"
  24191. * for Secure WebSocket.
  24192. * So to connect to "wss://CURRENT_HOSTNAME/xmpp-websocket" you would call
  24193. *
  24194. * > var conn = new Strophe.Connection("/xmpp-websocket/", {protocol: "wss"});
  24195. *
  24196. * Note that relative URLs _NOT_ starting with a "/" will also include the path
  24197. * of the current site.
  24198. *
  24199. * Also because downgrading security is not permitted by browsers, when using
  24200. * relative URLs both BOSH and WebSocket connections will use their secure
  24201. * variants if the current connection to the site is also secure (https).
  24202. *
  24203. * BOSH options:
  24204. *
  24205. * by adding "sync" to the options, you can control if requests will
  24206. * be made synchronously or not. The default behaviour is asynchronous.
  24207. * If you want to make requests synchronous, make "sync" evaluate to true:
  24208. * > var conn = new Strophe.Connection("/http-bind/", {sync: true});
  24209. * You can also toggle this on an already established connection:
  24210. * > conn.options.sync = true;
  24211. *
  24212. *
  24213. * Parameters:
  24214. * (String) service - The BOSH or WebSocket service URL.
  24215. * (Object) options - A hash of configuration options
  24216. *
  24217. * Returns:
  24218. * A new Strophe.Connection object.
  24219. */
  24220. Strophe.Connection = function (service, options)
  24221. {
  24222. // The service URL
  24223. this.service = service;
  24224. // Configuration options
  24225. this.options = options || {};
  24226. var proto = this.options.protocol || "";
  24227. // Select protocal based on service or options
  24228. if (service.indexOf("ws:") === 0 || service.indexOf("wss:") === 0 ||
  24229. proto.indexOf("ws") === 0) {
  24230. this._proto = new Strophe.Websocket(this);
  24231. } else {
  24232. this._proto = new Strophe.Bosh(this);
  24233. }
  24234. /* The connected JID. */
  24235. this.jid = "";
  24236. /* the JIDs domain */
  24237. this.domain = null;
  24238. /* stream:features */
  24239. this.features = null;
  24240. // SASL
  24241. this._sasl_data = {};
  24242. this.do_session = false;
  24243. this.do_bind = false;
  24244. // handler lists
  24245. this.timedHandlers = [];
  24246. this.handlers = [];
  24247. this.removeTimeds = [];
  24248. this.removeHandlers = [];
  24249. this.addTimeds = [];
  24250. this.addHandlers = [];
  24251. this._authentication = {};
  24252. this._idleTimeout = null;
  24253. this._disconnectTimeout = null;
  24254. this.do_authentication = true;
  24255. this.authenticated = false;
  24256. this.disconnecting = false;
  24257. this.connected = false;
  24258. this.errors = 0;
  24259. this.paused = false;
  24260. this._data = [];
  24261. this._uniqueId = 0;
  24262. this._sasl_success_handler = null;
  24263. this._sasl_failure_handler = null;
  24264. this._sasl_challenge_handler = null;
  24265. // Max retries before disconnecting
  24266. this.maxRetries = 5;
  24267. // setup onIdle callback every 1/10th of a second
  24268. this._idleTimeout = setTimeout(this._onIdle.bind(this), 100);
  24269. // initialize plugins
  24270. for (var k in Strophe._connectionPlugins) {
  24271. if (Strophe._connectionPlugins.hasOwnProperty(k)) {
  24272. var ptype = Strophe._connectionPlugins[k];
  24273. // jslint complaints about the below line, but this is fine
  24274. var F = function () {}; // jshint ignore:line
  24275. F.prototype = ptype;
  24276. this[k] = new F();
  24277. this[k].init(this);
  24278. }
  24279. }
  24280. };
  24281. Strophe.Connection.prototype = {
  24282. /** Function: reset
  24283. * Reset the connection.
  24284. *
  24285. * This function should be called after a connection is disconnected
  24286. * before that connection is reused.
  24287. */
  24288. reset: function ()
  24289. {
  24290. this._proto._reset();
  24291. // SASL
  24292. this.do_session = false;
  24293. this.do_bind = false;
  24294. // handler lists
  24295. this.timedHandlers = [];
  24296. this.handlers = [];
  24297. this.removeTimeds = [];
  24298. this.removeHandlers = [];
  24299. this.addTimeds = [];
  24300. this.addHandlers = [];
  24301. this._authentication = {};
  24302. this.authenticated = false;
  24303. this.disconnecting = false;
  24304. this.connected = false;
  24305. this.errors = 0;
  24306. this._requests = [];
  24307. this._uniqueId = 0;
  24308. },
  24309. /** Function: pause
  24310. * Pause the request manager.
  24311. *
  24312. * This will prevent Strophe from sending any more requests to the
  24313. * server. This is very useful for temporarily pausing
  24314. * BOSH-Connections while a lot of send() calls are happening quickly.
  24315. * This causes Strophe to send the data in a single request, saving
  24316. * many request trips.
  24317. */
  24318. pause: function ()
  24319. {
  24320. this.paused = true;
  24321. },
  24322. /** Function: resume
  24323. * Resume the request manager.
  24324. *
  24325. * This resumes after pause() has been called.
  24326. */
  24327. resume: function ()
  24328. {
  24329. this.paused = false;
  24330. },
  24331. /** Function: getUniqueId
  24332. * Generate a unique ID for use in <iq/> elements.
  24333. *
  24334. * All <iq/> stanzas are required to have unique id attributes. This
  24335. * function makes creating these easy. Each connection instance has
  24336. * a counter which starts from zero, and the value of this counter
  24337. * plus a colon followed by the suffix becomes the unique id. If no
  24338. * suffix is supplied, the counter is used as the unique id.
  24339. *
  24340. * Suffixes are used to make debugging easier when reading the stream
  24341. * data, and their use is recommended. The counter resets to 0 for
  24342. * every new connection for the same reason. For connections to the
  24343. * same server that authenticate the same way, all the ids should be
  24344. * the same, which makes it easy to see changes. This is useful for
  24345. * automated testing as well.
  24346. *
  24347. * Parameters:
  24348. * (String) suffix - A optional suffix to append to the id.
  24349. *
  24350. * Returns:
  24351. * A unique string to be used for the id attribute.
  24352. */
  24353. getUniqueId: function (suffix)
  24354. {
  24355. if (typeof(suffix) == "string" || typeof(suffix) == "number") {
  24356. return ++this._uniqueId + ":" + suffix;
  24357. } else {
  24358. return ++this._uniqueId + "";
  24359. }
  24360. },
  24361. /** Function: connect
  24362. * Starts the connection process.
  24363. *
  24364. * As the connection process proceeds, the user supplied callback will
  24365. * be triggered multiple times with status updates. The callback
  24366. * should take two arguments - the status code and the error condition.
  24367. *
  24368. * The status code will be one of the values in the Strophe.Status
  24369. * constants. The error condition will be one of the conditions
  24370. * defined in RFC 3920 or the condition 'strophe-parsererror'.
  24371. *
  24372. * The Parameters _wait_, _hold_ and _route_ are optional and only relevant
  24373. * for BOSH connections. Please see XEP 124 for a more detailed explanation
  24374. * of the optional parameters.
  24375. *
  24376. * Parameters:
  24377. * (String) jid - The user's JID. This may be a bare JID,
  24378. * or a full JID. If a node is not supplied, SASL ANONYMOUS
  24379. * authentication will be attempted.
  24380. * (String) pass - The user's password.
  24381. * (Function) callback - The connect callback function.
  24382. * (Integer) wait - The optional HTTPBIND wait value. This is the
  24383. * time the server will wait before returning an empty result for
  24384. * a request. The default setting of 60 seconds is recommended.
  24385. * (Integer) hold - The optional HTTPBIND hold value. This is the
  24386. * number of connections the server will hold at one time. This
  24387. * should almost always be set to 1 (the default).
  24388. * (String) route - The optional route value.
  24389. */
  24390. connect: function (jid, pass, callback, wait, hold, route)
  24391. {
  24392. this.jid = jid;
  24393. /** Variable: authzid
  24394. * Authorization identity.
  24395. */
  24396. this.authzid = Strophe.getBareJidFromJid(this.jid);
  24397. /** Variable: authcid
  24398. * Authentication identity (User name).
  24399. */
  24400. this.authcid = Strophe.getNodeFromJid(this.jid);
  24401. /** Variable: pass
  24402. * Authentication identity (User password).
  24403. */
  24404. this.pass = pass;
  24405. /** Variable: servtype
  24406. * Digest MD5 compatibility.
  24407. */
  24408. this.servtype = "xmpp";
  24409. this.connect_callback = callback;
  24410. this.disconnecting = false;
  24411. this.connected = false;
  24412. this.authenticated = false;
  24413. this.errors = 0;
  24414. // parse jid for domain
  24415. this.domain = Strophe.getDomainFromJid(this.jid);
  24416. this._changeConnectStatus(Strophe.Status.CONNECTING, null);
  24417. this._proto._connect(wait, hold, route);
  24418. },
  24419. /** Function: attach
  24420. * Attach to an already created and authenticated BOSH session.
  24421. *
  24422. * This function is provided to allow Strophe to attach to BOSH
  24423. * sessions which have been created externally, perhaps by a Web
  24424. * application. This is often used to support auto-login type features
  24425. * without putting user credentials into the page.
  24426. *
  24427. * Parameters:
  24428. * (String) jid - The full JID that is bound by the session.
  24429. * (String) sid - The SID of the BOSH session.
  24430. * (String) rid - The current RID of the BOSH session. This RID
  24431. * will be used by the next request.
  24432. * (Function) callback The connect callback function.
  24433. * (Integer) wait - The optional HTTPBIND wait value. This is the
  24434. * time the server will wait before returning an empty result for
  24435. * a request. The default setting of 60 seconds is recommended.
  24436. * Other settings will require tweaks to the Strophe.TIMEOUT value.
  24437. * (Integer) hold - The optional HTTPBIND hold value. This is the
  24438. * number of connections the server will hold at one time. This
  24439. * should almost always be set to 1 (the default).
  24440. * (Integer) wind - The optional HTTBIND window value. This is the
  24441. * allowed range of request ids that are valid. The default is 5.
  24442. */
  24443. attach: function (jid, sid, rid, callback, wait, hold, wind)
  24444. {
  24445. this._proto._attach(jid, sid, rid, callback, wait, hold, wind);
  24446. },
  24447. /** Function: xmlInput
  24448. * User overrideable function that receives XML data coming into the
  24449. * connection.
  24450. *
  24451. * The default function does nothing. User code can override this with
  24452. * > Strophe.Connection.xmlInput = function (elem) {
  24453. * > (user code)
  24454. * > };
  24455. *
  24456. * Due to limitations of current Browsers' XML-Parsers the opening and closing
  24457. * <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.
  24458. *
  24459. * BOSH-Connections will have all stanzas wrapped in a <body> tag. See
  24460. * <Strophe.Bosh.strip> if you want to strip this tag.
  24461. *
  24462. * Parameters:
  24463. * (XMLElement) elem - The XML data received by the connection.
  24464. */
  24465. /* jshint unused:false */
  24466. xmlInput: function (elem)
  24467. {
  24468. return;
  24469. },
  24470. /* jshint unused:true */
  24471. /** Function: xmlOutput
  24472. * User overrideable function that receives XML data sent to the
  24473. * connection.
  24474. *
  24475. * The default function does nothing. User code can override this with
  24476. * > Strophe.Connection.xmlOutput = function (elem) {
  24477. * > (user code)
  24478. * > };
  24479. *
  24480. * Due to limitations of current Browsers' XML-Parsers the opening and closing
  24481. * <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.
  24482. *
  24483. * BOSH-Connections will have all stanzas wrapped in a <body> tag. See
  24484. * <Strophe.Bosh.strip> if you want to strip this tag.
  24485. *
  24486. * Parameters:
  24487. * (XMLElement) elem - The XMLdata sent by the connection.
  24488. */
  24489. /* jshint unused:false */
  24490. xmlOutput: function (elem)
  24491. {
  24492. return;
  24493. },
  24494. /* jshint unused:true */
  24495. /** Function: rawInput
  24496. * User overrideable function that receives raw data coming into the
  24497. * connection.
  24498. *
  24499. * The default function does nothing. User code can override this with
  24500. * > Strophe.Connection.rawInput = function (data) {
  24501. * > (user code)
  24502. * > };
  24503. *
  24504. * Parameters:
  24505. * (String) data - The data received by the connection.
  24506. */
  24507. /* jshint unused:false */
  24508. rawInput: function (data)
  24509. {
  24510. return;
  24511. },
  24512. /* jshint unused:true */
  24513. /** Function: rawOutput
  24514. * User overrideable function that receives raw data sent to the
  24515. * connection.
  24516. *
  24517. * The default function does nothing. User code can override this with
  24518. * > Strophe.Connection.rawOutput = function (data) {
  24519. * > (user code)
  24520. * > };
  24521. *
  24522. * Parameters:
  24523. * (String) data - The data sent by the connection.
  24524. */
  24525. /* jshint unused:false */
  24526. rawOutput: function (data)
  24527. {
  24528. return;
  24529. },
  24530. /* jshint unused:true */
  24531. /** Function: send
  24532. * Send a stanza.
  24533. *
  24534. * This function is called to push data onto the send queue to
  24535. * go out over the wire. Whenever a request is sent to the BOSH
  24536. * server, all pending data is sent and the queue is flushed.
  24537. *
  24538. * Parameters:
  24539. * (XMLElement |
  24540. * [XMLElement] |
  24541. * Strophe.Builder) elem - The stanza to send.
  24542. */
  24543. send: function (elem)
  24544. {
  24545. if (elem === null) { return ; }
  24546. if (typeof(elem.sort) === "function") {
  24547. for (var i = 0; i < elem.length; i++) {
  24548. this._queueData(elem[i]);
  24549. }
  24550. } else if (typeof(elem.tree) === "function") {
  24551. this._queueData(elem.tree());
  24552. } else {
  24553. this._queueData(elem);
  24554. }
  24555. this._proto._send();
  24556. },
  24557. /** Function: flush
  24558. * Immediately send any pending outgoing data.
  24559. *
  24560. * Normally send() queues outgoing data until the next idle period
  24561. * (100ms), which optimizes network use in the common cases when
  24562. * several send()s are called in succession. flush() can be used to
  24563. * immediately send all pending data.
  24564. */
  24565. flush: function ()
  24566. {
  24567. // cancel the pending idle period and run the idle function
  24568. // immediately
  24569. clearTimeout(this._idleTimeout);
  24570. this._onIdle();
  24571. },
  24572. /** Function: sendIQ
  24573. * Helper function to send IQ stanzas.
  24574. *
  24575. * Parameters:
  24576. * (XMLElement) elem - The stanza to send.
  24577. * (Function) callback - The callback function for a successful request.
  24578. * (Function) errback - The callback function for a failed or timed
  24579. * out request. On timeout, the stanza will be null.
  24580. * (Integer) timeout - The time specified in milliseconds for a
  24581. * timeout to occur.
  24582. *
  24583. * Returns:
  24584. * The id used to send the IQ.
  24585. */
  24586. sendIQ: function(elem, callback, errback, timeout) {
  24587. var timeoutHandler = null;
  24588. var that = this;
  24589. if (typeof(elem.tree) === "function") {
  24590. elem = elem.tree();
  24591. }
  24592. var id = elem.getAttribute('id');
  24593. // inject id if not found
  24594. if (!id) {
  24595. id = this.getUniqueId("sendIQ");
  24596. elem.setAttribute("id", id);
  24597. }
  24598. var handler = this.addHandler(function (stanza) {
  24599. // remove timeout handler if there is one
  24600. if (timeoutHandler) {
  24601. that.deleteTimedHandler(timeoutHandler);
  24602. }
  24603. var iqtype = stanza.getAttribute('type');
  24604. if (iqtype == 'result') {
  24605. if (callback) {
  24606. callback(stanza);
  24607. }
  24608. } else if (iqtype == 'error') {
  24609. if (errback) {
  24610. errback(stanza);
  24611. }
  24612. } else {
  24613. throw {
  24614. name: "StropheError",
  24615. message: "Got bad IQ type of " + iqtype
  24616. };
  24617. }
  24618. }, null, 'iq', null, id);
  24619. // if timeout specified, setup timeout handler.
  24620. if (timeout) {
  24621. timeoutHandler = this.addTimedHandler(timeout, function () {
  24622. // get rid of normal handler
  24623. that.deleteHandler(handler);
  24624. // call errback on timeout with null stanza
  24625. if (errback) {
  24626. errback(null);
  24627. }
  24628. return false;
  24629. });
  24630. }
  24631. this.send(elem);
  24632. return id;
  24633. },
  24634. /** PrivateFunction: _queueData
  24635. * Queue outgoing data for later sending. Also ensures that the data
  24636. * is a DOMElement.
  24637. */
  24638. _queueData: function (element) {
  24639. if (element === null ||
  24640. !element.tagName ||
  24641. !element.childNodes) {
  24642. throw {
  24643. name: "StropheError",
  24644. message: "Cannot queue non-DOMElement."
  24645. };
  24646. }
  24647. this._data.push(element);
  24648. },
  24649. /** PrivateFunction: _sendRestart
  24650. * Send an xmpp:restart stanza.
  24651. */
  24652. _sendRestart: function ()
  24653. {
  24654. this._data.push("restart");
  24655. this._proto._sendRestart();
  24656. this._idleTimeout = setTimeout(this._onIdle.bind(this), 100);
  24657. },
  24658. /** Function: addTimedHandler
  24659. * Add a timed handler to the connection.
  24660. *
  24661. * This function adds a timed handler. The provided handler will
  24662. * be called every period milliseconds until it returns false,
  24663. * the connection is terminated, or the handler is removed. Handlers
  24664. * that wish to continue being invoked should return true.
  24665. *
  24666. * Because of method binding it is necessary to save the result of
  24667. * this function if you wish to remove a handler with
  24668. * deleteTimedHandler().
  24669. *
  24670. * Note that user handlers are not active until authentication is
  24671. * successful.
  24672. *
  24673. * Parameters:
  24674. * (Integer) period - The period of the handler.
  24675. * (Function) handler - The callback function.
  24676. *
  24677. * Returns:
  24678. * A reference to the handler that can be used to remove it.
  24679. */
  24680. addTimedHandler: function (period, handler)
  24681. {
  24682. var thand = new Strophe.TimedHandler(period, handler);
  24683. this.addTimeds.push(thand);
  24684. return thand;
  24685. },
  24686. /** Function: deleteTimedHandler
  24687. * Delete a timed handler for a connection.
  24688. *
  24689. * This function removes a timed handler from the connection. The
  24690. * handRef parameter is *not* the function passed to addTimedHandler(),
  24691. * but is the reference returned from addTimedHandler().
  24692. *
  24693. * Parameters:
  24694. * (Strophe.TimedHandler) handRef - The handler reference.
  24695. */
  24696. deleteTimedHandler: function (handRef)
  24697. {
  24698. // this must be done in the Idle loop so that we don't change
  24699. // the handlers during iteration
  24700. this.removeTimeds.push(handRef);
  24701. },
  24702. /** Function: addHandler
  24703. * Add a stanza handler for the connection.
  24704. *
  24705. * This function adds a stanza handler to the connection. The
  24706. * handler callback will be called for any stanza that matches
  24707. * the parameters. Note that if multiple parameters are supplied,
  24708. * they must all match for the handler to be invoked.
  24709. *
  24710. * The handler will receive the stanza that triggered it as its argument.
  24711. * The handler should return true if it is to be invoked again;
  24712. * returning false will remove the handler after it returns.
  24713. *
  24714. * As a convenience, the ns parameters applies to the top level element
  24715. * and also any of its immediate children. This is primarily to make
  24716. * matching /iq/query elements easy.
  24717. *
  24718. * The options argument contains handler matching flags that affect how
  24719. * matches are determined. Currently the only flag is matchBare (a
  24720. * boolean). When matchBare is true, the from parameter and the from
  24721. * attribute on the stanza will be matched as bare JIDs instead of
  24722. * full JIDs. To use this, pass {matchBare: true} as the value of
  24723. * options. The default value for matchBare is false.
  24724. *
  24725. * The return value should be saved if you wish to remove the handler
  24726. * with deleteHandler().
  24727. *
  24728. * Parameters:
  24729. * (Function) handler - The user callback.
  24730. * (String) ns - The namespace to match.
  24731. * (String) name - The stanza name to match.
  24732. * (String) type - The stanza type attribute to match.
  24733. * (String) id - The stanza id attribute to match.
  24734. * (String) from - The stanza from attribute to match.
  24735. * (String) options - The handler options
  24736. *
  24737. * Returns:
  24738. * A reference to the handler that can be used to remove it.
  24739. */
  24740. addHandler: function (handler, ns, name, type, id, from, options)
  24741. {
  24742. var hand = new Strophe.Handler(handler, ns, name, type, id, from, options);
  24743. this.addHandlers.push(hand);
  24744. return hand;
  24745. },
  24746. /** Function: deleteHandler
  24747. * Delete a stanza handler for a connection.
  24748. *
  24749. * This function removes a stanza handler from the connection. The
  24750. * handRef parameter is *not* the function passed to addHandler(),
  24751. * but is the reference returned from addHandler().
  24752. *
  24753. * Parameters:
  24754. * (Strophe.Handler) handRef - The handler reference.
  24755. */
  24756. deleteHandler: function (handRef)
  24757. {
  24758. // this must be done in the Idle loop so that we don't change
  24759. // the handlers during iteration
  24760. this.removeHandlers.push(handRef);
  24761. },
  24762. /** Function: disconnect
  24763. * Start the graceful disconnection process.
  24764. *
  24765. * This function starts the disconnection process. This process starts
  24766. * by sending unavailable presence and sending BOSH body of type
  24767. * terminate. A timeout handler makes sure that disconnection happens
  24768. * even if the BOSH server does not respond.
  24769. *
  24770. * The user supplied connection callback will be notified of the
  24771. * progress as this process happens.
  24772. *
  24773. * Parameters:
  24774. * (String) reason - The reason the disconnect is occuring.
  24775. */
  24776. disconnect: function (reason)
  24777. {
  24778. this._changeConnectStatus(Strophe.Status.DISCONNECTING, reason);
  24779. Strophe.info("Disconnect was called because: " + reason);
  24780. if (this.connected) {
  24781. var pres = false;
  24782. this.disconnecting = true;
  24783. if (this.authenticated) {
  24784. pres = $pres({
  24785. xmlns: Strophe.NS.CLIENT,
  24786. type: 'unavailable'
  24787. });
  24788. }
  24789. // setup timeout handler
  24790. this._disconnectTimeout = this._addSysTimedHandler(
  24791. 3000, this._onDisconnectTimeout.bind(this));
  24792. this._proto._disconnect(pres);
  24793. }
  24794. },
  24795. /** PrivateFunction: _changeConnectStatus
  24796. * _Private_ helper function that makes sure plugins and the user's
  24797. * callback are notified of connection status changes.
  24798. *
  24799. * Parameters:
  24800. * (Integer) status - the new connection status, one of the values
  24801. * in Strophe.Status
  24802. * (String) condition - the error condition or null
  24803. */
  24804. _changeConnectStatus: function (status, condition)
  24805. {
  24806. // notify all plugins listening for status changes
  24807. for (var k in Strophe._connectionPlugins) {
  24808. if (Strophe._connectionPlugins.hasOwnProperty(k)) {
  24809. var plugin = this[k];
  24810. if (plugin.statusChanged) {
  24811. try {
  24812. plugin.statusChanged(status, condition);
  24813. } catch (err) {
  24814. Strophe.error("" + k + " plugin caused an exception " +
  24815. "changing status: " + err);
  24816. }
  24817. }
  24818. }
  24819. }
  24820. // notify the user's callback
  24821. if (this.connect_callback) {
  24822. try {
  24823. this.connect_callback(status, condition);
  24824. } catch (e) {
  24825. Strophe.error("User connection callback caused an " +
  24826. "exception: " + e);
  24827. }
  24828. }
  24829. },
  24830. /** PrivateFunction: _doDisconnect
  24831. * _Private_ function to disconnect.
  24832. *
  24833. * This is the last piece of the disconnection logic. This resets the
  24834. * connection and alerts the user's connection callback.
  24835. */
  24836. _doDisconnect: function ()
  24837. {
  24838. // Cancel Disconnect Timeout
  24839. if (this._disconnectTimeout !== null) {
  24840. this.deleteTimedHandler(this._disconnectTimeout);
  24841. this._disconnectTimeout = null;
  24842. }
  24843. Strophe.info("_doDisconnect was called");
  24844. this._proto._doDisconnect();
  24845. this.authenticated = false;
  24846. this.disconnecting = false;
  24847. // delete handlers
  24848. this.handlers = [];
  24849. this.timedHandlers = [];
  24850. this.removeTimeds = [];
  24851. this.removeHandlers = [];
  24852. this.addTimeds = [];
  24853. this.addHandlers = [];
  24854. // tell the parent we disconnected
  24855. this._changeConnectStatus(Strophe.Status.DISCONNECTED, null);
  24856. this.connected = false;
  24857. },
  24858. /** PrivateFunction: _dataRecv
  24859. * _Private_ handler to processes incoming data from the the connection.
  24860. *
  24861. * Except for _connect_cb handling the initial connection request,
  24862. * this function handles the incoming data for all requests. This
  24863. * function also fires stanza handlers that match each incoming
  24864. * stanza.
  24865. *
  24866. * Parameters:
  24867. * (Strophe.Request) req - The request that has data ready.
  24868. * (string) req - The stanza a raw string (optiona).
  24869. */
  24870. _dataRecv: function (req, raw)
  24871. {
  24872. Strophe.info("_dataRecv called");
  24873. var elem = this._proto._reqToData(req);
  24874. if (elem === null) { return; }
  24875. if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {
  24876. if (elem.nodeName === this._proto.strip && elem.childNodes.length) {
  24877. this.xmlInput(elem.childNodes[0]);
  24878. } else {
  24879. this.xmlInput(elem);
  24880. }
  24881. }
  24882. if (this.rawInput !== Strophe.Connection.prototype.rawInput) {
  24883. if (raw) {
  24884. this.rawInput(raw);
  24885. } else {
  24886. this.rawInput(Strophe.serialize(elem));
  24887. }
  24888. }
  24889. // remove handlers scheduled for deletion
  24890. var i, hand;
  24891. while (this.removeHandlers.length > 0) {
  24892. hand = this.removeHandlers.pop();
  24893. i = this.handlers.indexOf(hand);
  24894. if (i >= 0) {
  24895. this.handlers.splice(i, 1);
  24896. }
  24897. }
  24898. // add handlers scheduled for addition
  24899. while (this.addHandlers.length > 0) {
  24900. this.handlers.push(this.addHandlers.pop());
  24901. }
  24902. // handle graceful disconnect
  24903. if (this.disconnecting && this._proto._emptyQueue()) {
  24904. this._doDisconnect();
  24905. return;
  24906. }
  24907. var typ = elem.getAttribute("type");
  24908. var cond, conflict;
  24909. if (typ !== null && typ == "terminate") {
  24910. // Don't process stanzas that come in after disconnect
  24911. if (this.disconnecting || !this.connected) {
  24912. return;
  24913. }
  24914. // an error occurred
  24915. cond = elem.getAttribute("condition");
  24916. conflict = elem.getElementsByTagName("conflict");
  24917. if (cond !== null) {
  24918. if (cond == "remote-stream-error" && conflict.length > 0) {
  24919. cond = "conflict";
  24920. }
  24921. this._changeConnectStatus(Strophe.Status.CONNFAIL, cond);
  24922. } else {
  24923. this._changeConnectStatus(Strophe.Status.CONNFAIL, "unknown");
  24924. }
  24925. this.disconnect('unknown stream-error');
  24926. return;
  24927. }
  24928. // send each incoming stanza through the handler chain
  24929. var that = this;
  24930. Strophe.forEachChild(elem, null, function (child) {
  24931. var i, newList;
  24932. // process handlers
  24933. newList = that.handlers;
  24934. that.handlers = [];
  24935. for (i = 0; i < newList.length; i++) {
  24936. var hand = newList[i];
  24937. // encapsulate 'handler.run' not to lose the whole handler list if
  24938. // one of the handlers throws an exception
  24939. try {
  24940. if (hand.isMatch(child) &&
  24941. (that.authenticated || !hand.user)) {
  24942. if (hand.run(child)) {
  24943. that.handlers.push(hand);
  24944. }
  24945. } else {
  24946. that.handlers.push(hand);
  24947. }
  24948. } catch(e) {
  24949. // if the handler throws an exception, we consider it as false
  24950. Strophe.warn('Removing Strophe handlers due to uncaught exception: ' + e.message);
  24951. }
  24952. }
  24953. });
  24954. },
  24955. /** Attribute: mechanisms
  24956. * SASL Mechanisms available for Conncection.
  24957. */
  24958. mechanisms: {},
  24959. /** PrivateFunction: _connect_cb
  24960. * _Private_ handler for initial connection request.
  24961. *
  24962. * This handler is used to process the initial connection request
  24963. * response from the BOSH server. It is used to set up authentication
  24964. * handlers and start the authentication process.
  24965. *
  24966. * SASL authentication will be attempted if available, otherwise
  24967. * the code will fall back to legacy authentication.
  24968. *
  24969. * Parameters:
  24970. * (Strophe.Request) req - The current request.
  24971. * (Function) _callback - low level (xmpp) connect callback function.
  24972. * Useful for plugins with their own xmpp connect callback (when their)
  24973. * want to do something special).
  24974. */
  24975. _connect_cb: function (req, _callback, raw)
  24976. {
  24977. Strophe.info("_connect_cb was called");
  24978. this.connected = true;
  24979. var bodyWrap = this._proto._reqToData(req);
  24980. if (!bodyWrap) { return; }
  24981. if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {
  24982. if (bodyWrap.nodeName === this._proto.strip && bodyWrap.childNodes.length) {
  24983. this.xmlInput(bodyWrap.childNodes[0]);
  24984. } else {
  24985. this.xmlInput(bodyWrap);
  24986. }
  24987. }
  24988. if (this.rawInput !== Strophe.Connection.prototype.rawInput) {
  24989. if (raw) {
  24990. this.rawInput(raw);
  24991. } else {
  24992. this.rawInput(Strophe.serialize(bodyWrap));
  24993. }
  24994. }
  24995. var conncheck = this._proto._connect_cb(bodyWrap);
  24996. if (conncheck === Strophe.Status.CONNFAIL) {
  24997. return;
  24998. }
  24999. this._authentication.sasl_scram_sha1 = false;
  25000. this._authentication.sasl_plain = false;
  25001. this._authentication.sasl_digest_md5 = false;
  25002. this._authentication.sasl_anonymous = false;
  25003. this._authentication.legacy_auth = false;
  25004. // Check for the stream:features tag
  25005. var hasFeatures = bodyWrap.getElementsByTagName("stream:features").length > 0;
  25006. if (!hasFeatures) {
  25007. hasFeatures = bodyWrap.getElementsByTagName("features").length > 0;
  25008. }
  25009. var mechanisms = bodyWrap.getElementsByTagName("mechanism");
  25010. var matched = [];
  25011. var i, mech, found_authentication = false;
  25012. if (!hasFeatures) {
  25013. this._proto._no_auth_received(_callback);
  25014. return;
  25015. }
  25016. if (mechanisms.length > 0) {
  25017. for (i = 0; i < mechanisms.length; i++) {
  25018. mech = Strophe.getText(mechanisms[i]);
  25019. if (this.mechanisms[mech]) matched.push(this.mechanisms[mech]);
  25020. }
  25021. }
  25022. this._authentication.legacy_auth =
  25023. bodyWrap.getElementsByTagName("auth").length > 0;
  25024. found_authentication = this._authentication.legacy_auth ||
  25025. matched.length > 0;
  25026. if (!found_authentication) {
  25027. this._proto._no_auth_received(_callback);
  25028. return;
  25029. }
  25030. if (this.do_authentication !== false)
  25031. this.authenticate(matched);
  25032. },
  25033. /** Function: authenticate
  25034. * Set up authentication
  25035. *
  25036. * Contiunues the initial connection request by setting up authentication
  25037. * handlers and start the authentication process.
  25038. *
  25039. * SASL authentication will be attempted if available, otherwise
  25040. * the code will fall back to legacy authentication.
  25041. *
  25042. */
  25043. authenticate: function (matched)
  25044. {
  25045. var i;
  25046. // Sorting matched mechanisms according to priority.
  25047. for (i = 0; i < matched.length - 1; ++i) {
  25048. var higher = i;
  25049. for (var j = i + 1; j < matched.length; ++j) {
  25050. if (matched[j].prototype.priority > matched[higher].prototype.priority) {
  25051. higher = j;
  25052. }
  25053. }
  25054. if (higher != i) {
  25055. var swap = matched[i];
  25056. matched[i] = matched[higher];
  25057. matched[higher] = swap;
  25058. }
  25059. }
  25060. // run each mechanism
  25061. var mechanism_found = false;
  25062. for (i = 0; i < matched.length; ++i) {
  25063. if (!matched[i].test(this)) continue;
  25064. this._sasl_success_handler = this._addSysHandler(
  25065. this._sasl_success_cb.bind(this), null,
  25066. "success", null, null);
  25067. this._sasl_failure_handler = this._addSysHandler(
  25068. this._sasl_failure_cb.bind(this), null,
  25069. "failure", null, null);
  25070. this._sasl_challenge_handler = this._addSysHandler(
  25071. this._sasl_challenge_cb.bind(this), null,
  25072. "challenge", null, null);
  25073. this._sasl_mechanism = new matched[i]();
  25074. this._sasl_mechanism.onStart(this);
  25075. var request_auth_exchange = $build("auth", {
  25076. xmlns: Strophe.NS.SASL,
  25077. mechanism: this._sasl_mechanism.name
  25078. });
  25079. if (this._sasl_mechanism.isClientFirst) {
  25080. var response = this._sasl_mechanism.onChallenge(this, null);
  25081. request_auth_exchange.t(Base64.encode(response));
  25082. }
  25083. this.send(request_auth_exchange.tree());
  25084. mechanism_found = true;
  25085. break;
  25086. }
  25087. if (!mechanism_found) {
  25088. // if none of the mechanism worked
  25089. if (Strophe.getNodeFromJid(this.jid) === null) {
  25090. // we don't have a node, which is required for non-anonymous
  25091. // client connections
  25092. this._changeConnectStatus(Strophe.Status.CONNFAIL,
  25093. 'x-strophe-bad-non-anon-jid');
  25094. this.disconnect('x-strophe-bad-non-anon-jid');
  25095. } else {
  25096. // fall back to legacy authentication
  25097. this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null);
  25098. this._addSysHandler(this._auth1_cb.bind(this), null, null,
  25099. null, "_auth_1");
  25100. this.send($iq({
  25101. type: "get",
  25102. to: this.domain,
  25103. id: "_auth_1"
  25104. }).c("query", {
  25105. xmlns: Strophe.NS.AUTH
  25106. }).c("username", {}).t(Strophe.getNodeFromJid(this.jid)).tree());
  25107. }
  25108. }
  25109. },
  25110. _sasl_challenge_cb: function(elem) {
  25111. var challenge = Base64.decode(Strophe.getText(elem));
  25112. var response = this._sasl_mechanism.onChallenge(this, challenge);
  25113. var stanza = $build('response', {
  25114. xmlns: Strophe.NS.SASL
  25115. });
  25116. if (response !== "") {
  25117. stanza.t(Base64.encode(response));
  25118. }
  25119. this.send(stanza.tree());
  25120. return true;
  25121. },
  25122. /** PrivateFunction: _auth1_cb
  25123. * _Private_ handler for legacy authentication.
  25124. *
  25125. * This handler is called in response to the initial <iq type='get'/>
  25126. * for legacy authentication. It builds an authentication <iq/> and
  25127. * sends it, creating a handler (calling back to _auth2_cb()) to
  25128. * handle the result
  25129. *
  25130. * Parameters:
  25131. * (XMLElement) elem - The stanza that triggered the callback.
  25132. *
  25133. * Returns:
  25134. * false to remove the handler.
  25135. */
  25136. /* jshint unused:false */
  25137. _auth1_cb: function (elem)
  25138. {
  25139. // build plaintext auth iq
  25140. var iq = $iq({type: "set", id: "_auth_2"})
  25141. .c('query', {xmlns: Strophe.NS.AUTH})
  25142. .c('username', {}).t(Strophe.getNodeFromJid(this.jid))
  25143. .up()
  25144. .c('password').t(this.pass);
  25145. if (!Strophe.getResourceFromJid(this.jid)) {
  25146. // since the user has not supplied a resource, we pick
  25147. // a default one here. unlike other auth methods, the server
  25148. // cannot do this for us.
  25149. this.jid = Strophe.getBareJidFromJid(this.jid) + '/strophe';
  25150. }
  25151. iq.up().c('resource', {}).t(Strophe.getResourceFromJid(this.jid));
  25152. this._addSysHandler(this._auth2_cb.bind(this), null,
  25153. null, null, "_auth_2");
  25154. this.send(iq.tree());
  25155. return false;
  25156. },
  25157. /* jshint unused:true */
  25158. /** PrivateFunction: _sasl_success_cb
  25159. * _Private_ handler for succesful SASL authentication.
  25160. *
  25161. * Parameters:
  25162. * (XMLElement) elem - The matching stanza.
  25163. *
  25164. * Returns:
  25165. * false to remove the handler.
  25166. */
  25167. _sasl_success_cb: function (elem)
  25168. {
  25169. if (this._sasl_data["server-signature"]) {
  25170. var serverSignature;
  25171. var success = Base64.decode(Strophe.getText(elem));
  25172. var attribMatch = /([a-z]+)=([^,]+)(,|$)/;
  25173. var matches = success.match(attribMatch);
  25174. if (matches[1] == "v") {
  25175. serverSignature = matches[2];
  25176. }
  25177. if (serverSignature != this._sasl_data["server-signature"]) {
  25178. // remove old handlers
  25179. this.deleteHandler(this._sasl_failure_handler);
  25180. this._sasl_failure_handler = null;
  25181. if (this._sasl_challenge_handler) {
  25182. this.deleteHandler(this._sasl_challenge_handler);
  25183. this._sasl_challenge_handler = null;
  25184. }
  25185. this._sasl_data = {};
  25186. return this._sasl_failure_cb(null);
  25187. }
  25188. }
  25189. Strophe.info("SASL authentication succeeded.");
  25190. if(this._sasl_mechanism)
  25191. this._sasl_mechanism.onSuccess();
  25192. // remove old handlers
  25193. this.deleteHandler(this._sasl_failure_handler);
  25194. this._sasl_failure_handler = null;
  25195. if (this._sasl_challenge_handler) {
  25196. this.deleteHandler(this._sasl_challenge_handler);
  25197. this._sasl_challenge_handler = null;
  25198. }
  25199. this._addSysHandler(this._sasl_auth1_cb.bind(this), null,
  25200. "stream:features", null, null);
  25201. // we must send an xmpp:restart now
  25202. this._sendRestart();
  25203. return false;
  25204. },
  25205. /** PrivateFunction: _sasl_auth1_cb
  25206. * _Private_ handler to start stream binding.
  25207. *
  25208. * Parameters:
  25209. * (XMLElement) elem - The matching stanza.
  25210. *
  25211. * Returns:
  25212. * false to remove the handler.
  25213. */
  25214. _sasl_auth1_cb: function (elem)
  25215. {
  25216. // save stream:features for future usage
  25217. this.features = elem;
  25218. var i, child;
  25219. for (i = 0; i < elem.childNodes.length; i++) {
  25220. child = elem.childNodes[i];
  25221. if (child.nodeName == 'bind') {
  25222. this.do_bind = true;
  25223. }
  25224. if (child.nodeName == 'session') {
  25225. this.do_session = true;
  25226. }
  25227. }
  25228. if (!this.do_bind) {
  25229. this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
  25230. return false;
  25231. } else {
  25232. this._addSysHandler(this._sasl_bind_cb.bind(this), null, null,
  25233. null, "_bind_auth_2");
  25234. var resource = Strophe.getResourceFromJid(this.jid);
  25235. if (resource) {
  25236. this.send($iq({type: "set", id: "_bind_auth_2"})
  25237. .c('bind', {xmlns: Strophe.NS.BIND})
  25238. .c('resource', {}).t(resource).tree());
  25239. } else {
  25240. this.send($iq({type: "set", id: "_bind_auth_2"})
  25241. .c('bind', {xmlns: Strophe.NS.BIND})
  25242. .tree());
  25243. }
  25244. }
  25245. return false;
  25246. },
  25247. /** PrivateFunction: _sasl_bind_cb
  25248. * _Private_ handler for binding result and session start.
  25249. *
  25250. * Parameters:
  25251. * (XMLElement) elem - The matching stanza.
  25252. *
  25253. * Returns:
  25254. * false to remove the handler.
  25255. */
  25256. _sasl_bind_cb: function (elem)
  25257. {
  25258. if (elem.getAttribute("type") == "error") {
  25259. Strophe.info("SASL binding failed.");
  25260. var conflict = elem.getElementsByTagName("conflict"), condition;
  25261. if (conflict.length > 0) {
  25262. condition = 'conflict';
  25263. }
  25264. this._changeConnectStatus(Strophe.Status.AUTHFAIL, condition);
  25265. return false;
  25266. }
  25267. // TODO - need to grab errors
  25268. var bind = elem.getElementsByTagName("bind");
  25269. var jidNode;
  25270. if (bind.length > 0) {
  25271. // Grab jid
  25272. jidNode = bind[0].getElementsByTagName("jid");
  25273. if (jidNode.length > 0) {
  25274. this.jid = Strophe.getText(jidNode[0]);
  25275. if (this.do_session) {
  25276. this._addSysHandler(this._sasl_session_cb.bind(this),
  25277. null, null, null, "_session_auth_2");
  25278. this.send($iq({type: "set", id: "_session_auth_2"})
  25279. .c('session', {xmlns: Strophe.NS.SESSION})
  25280. .tree());
  25281. } else {
  25282. this.authenticated = true;
  25283. this._changeConnectStatus(Strophe.Status.CONNECTED, null);
  25284. }
  25285. }
  25286. } else {
  25287. Strophe.info("SASL binding failed.");
  25288. this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
  25289. return false;
  25290. }
  25291. },
  25292. /** PrivateFunction: _sasl_session_cb
  25293. * _Private_ handler to finish successful SASL connection.
  25294. *
  25295. * This sets Connection.authenticated to true on success, which
  25296. * starts the processing of user handlers.
  25297. *
  25298. * Parameters:
  25299. * (XMLElement) elem - The matching stanza.
  25300. *
  25301. * Returns:
  25302. * false to remove the handler.
  25303. */
  25304. _sasl_session_cb: function (elem)
  25305. {
  25306. if (elem.getAttribute("type") == "result") {
  25307. this.authenticated = true;
  25308. this._changeConnectStatus(Strophe.Status.CONNECTED, null);
  25309. } else if (elem.getAttribute("type") == "error") {
  25310. Strophe.info("Session creation failed.");
  25311. this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
  25312. return false;
  25313. }
  25314. return false;
  25315. },
  25316. /** PrivateFunction: _sasl_failure_cb
  25317. * _Private_ handler for SASL authentication failure.
  25318. *
  25319. * Parameters:
  25320. * (XMLElement) elem - The matching stanza.
  25321. *
  25322. * Returns:
  25323. * false to remove the handler.
  25324. */
  25325. /* jshint unused:false */
  25326. _sasl_failure_cb: function (elem)
  25327. {
  25328. // delete unneeded handlers
  25329. if (this._sasl_success_handler) {
  25330. this.deleteHandler(this._sasl_success_handler);
  25331. this._sasl_success_handler = null;
  25332. }
  25333. if (this._sasl_challenge_handler) {
  25334. this.deleteHandler(this._sasl_challenge_handler);
  25335. this._sasl_challenge_handler = null;
  25336. }
  25337. if(this._sasl_mechanism)
  25338. this._sasl_mechanism.onFailure();
  25339. this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
  25340. return false;
  25341. },
  25342. /* jshint unused:true */
  25343. /** PrivateFunction: _auth2_cb
  25344. * _Private_ handler to finish legacy authentication.
  25345. *
  25346. * This handler is called when the result from the jabber:iq:auth
  25347. * <iq/> stanza is returned.
  25348. *
  25349. * Parameters:
  25350. * (XMLElement) elem - The stanza that triggered the callback.
  25351. *
  25352. * Returns:
  25353. * false to remove the handler.
  25354. */
  25355. _auth2_cb: function (elem)
  25356. {
  25357. if (elem.getAttribute("type") == "result") {
  25358. this.authenticated = true;
  25359. this._changeConnectStatus(Strophe.Status.CONNECTED, null);
  25360. } else if (elem.getAttribute("type") == "error") {
  25361. this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
  25362. this.disconnect('authentication failed');
  25363. }
  25364. return false;
  25365. },
  25366. /** PrivateFunction: _addSysTimedHandler
  25367. * _Private_ function to add a system level timed handler.
  25368. *
  25369. * This function is used to add a Strophe.TimedHandler for the
  25370. * library code. System timed handlers are allowed to run before
  25371. * authentication is complete.
  25372. *
  25373. * Parameters:
  25374. * (Integer) period - The period of the handler.
  25375. * (Function) handler - The callback function.
  25376. */
  25377. _addSysTimedHandler: function (period, handler)
  25378. {
  25379. var thand = new Strophe.TimedHandler(period, handler);
  25380. thand.user = false;
  25381. this.addTimeds.push(thand);
  25382. return thand;
  25383. },
  25384. /** PrivateFunction: _addSysHandler
  25385. * _Private_ function to add a system level stanza handler.
  25386. *
  25387. * This function is used to add a Strophe.Handler for the
  25388. * library code. System stanza handlers are allowed to run before
  25389. * authentication is complete.
  25390. *
  25391. * Parameters:
  25392. * (Function) handler - The callback function.
  25393. * (String) ns - The namespace to match.
  25394. * (String) name - The stanza name to match.
  25395. * (String) type - The stanza type attribute to match.
  25396. * (String) id - The stanza id attribute to match.
  25397. */
  25398. _addSysHandler: function (handler, ns, name, type, id)
  25399. {
  25400. var hand = new Strophe.Handler(handler, ns, name, type, id);
  25401. hand.user = false;
  25402. this.addHandlers.push(hand);
  25403. return hand;
  25404. },
  25405. /** PrivateFunction: _onDisconnectTimeout
  25406. * _Private_ timeout handler for handling non-graceful disconnection.
  25407. *
  25408. * If the graceful disconnect process does not complete within the
  25409. * time allotted, this handler finishes the disconnect anyway.
  25410. *
  25411. * Returns:
  25412. * false to remove the handler.
  25413. */
  25414. _onDisconnectTimeout: function ()
  25415. {
  25416. Strophe.info("_onDisconnectTimeout was called");
  25417. this._proto._onDisconnectTimeout();
  25418. // actually disconnect
  25419. this._doDisconnect();
  25420. return false;
  25421. },
  25422. /** PrivateFunction: _onIdle
  25423. * _Private_ handler to process events during idle cycle.
  25424. *
  25425. * This handler is called every 100ms to fire timed handlers that
  25426. * are ready and keep poll requests going.
  25427. */
  25428. _onIdle: function ()
  25429. {
  25430. var i, thand, since, newList;
  25431. // add timed handlers scheduled for addition
  25432. // NOTE: we add before remove in the case a timed handler is
  25433. // added and then deleted before the next _onIdle() call.
  25434. while (this.addTimeds.length > 0) {
  25435. this.timedHandlers.push(this.addTimeds.pop());
  25436. }
  25437. // remove timed handlers that have been scheduled for deletion
  25438. while (this.removeTimeds.length > 0) {
  25439. thand = this.removeTimeds.pop();
  25440. i = this.timedHandlers.indexOf(thand);
  25441. if (i >= 0) {
  25442. this.timedHandlers.splice(i, 1);
  25443. }
  25444. }
  25445. // call ready timed handlers
  25446. var now = new Date().getTime();
  25447. newList = [];
  25448. for (i = 0; i < this.timedHandlers.length; i++) {
  25449. thand = this.timedHandlers[i];
  25450. if (this.authenticated || !thand.user) {
  25451. since = thand.lastCalled + thand.period;
  25452. if (since - now <= 0) {
  25453. if (thand.run()) {
  25454. newList.push(thand);
  25455. }
  25456. } else {
  25457. newList.push(thand);
  25458. }
  25459. }
  25460. }
  25461. this.timedHandlers = newList;
  25462. clearTimeout(this._idleTimeout);
  25463. this._proto._onIdle();
  25464. // reactivate the timer only if connected
  25465. if (this.connected) {
  25466. this._idleTimeout = setTimeout(this._onIdle.bind(this), 100);
  25467. }
  25468. }
  25469. };
  25470. if (callback) {
  25471. callback(Strophe, $build, $msg, $iq, $pres);
  25472. }
  25473. /** Class: Strophe.SASLMechanism
  25474. *
  25475. * encapsulates SASL authentication mechanisms.
  25476. *
  25477. * User code may override the priority for each mechanism or disable it completely.
  25478. * See <priority> for information about changing priority and <test> for informatian on
  25479. * how to disable a mechanism.
  25480. *
  25481. * By default, all mechanisms are enabled and the priorities are
  25482. *
  25483. * SCRAM-SHA1 - 40
  25484. * DIGEST-MD5 - 30
  25485. * Plain - 20
  25486. */
  25487. /**
  25488. * PrivateConstructor: Strophe.SASLMechanism
  25489. * SASL auth mechanism abstraction.
  25490. *
  25491. * Parameters:
  25492. * (String) name - SASL Mechanism name.
  25493. * (Boolean) isClientFirst - If client should send response first without challenge.
  25494. * (Number) priority - Priority.
  25495. *
  25496. * Returns:
  25497. * A new Strophe.SASLMechanism object.
  25498. */
  25499. Strophe.SASLMechanism = function(name, isClientFirst, priority) {
  25500. /** PrivateVariable: name
  25501. * Mechanism name.
  25502. */
  25503. this.name = name;
  25504. /** PrivateVariable: isClientFirst
  25505. * If client sends response without initial server challenge.
  25506. */
  25507. this.isClientFirst = isClientFirst;
  25508. /** Variable: priority
  25509. * Determines which <SASLMechanism> is chosen for authentication (Higher is better).
  25510. * Users may override this to prioritize mechanisms differently.
  25511. *
  25512. * In the default configuration the priorities are
  25513. *
  25514. * SCRAM-SHA1 - 40
  25515. * DIGEST-MD5 - 30
  25516. * Plain - 20
  25517. *
  25518. * Example: (This will cause Strophe to choose the mechanism that the server sent first)
  25519. *
  25520. * > Strophe.SASLMD5.priority = Strophe.SASLSHA1.priority;
  25521. *
  25522. * See <SASL mechanisms> for a list of available mechanisms.
  25523. *
  25524. */
  25525. this.priority = priority;
  25526. };
  25527. Strophe.SASLMechanism.prototype = {
  25528. /**
  25529. * Function: test
  25530. * Checks if mechanism able to run.
  25531. * To disable a mechanism, make this return false;
  25532. *
  25533. * To disable plain authentication run
  25534. * > Strophe.SASLPlain.test = function() {
  25535. * > return false;
  25536. * > }
  25537. *
  25538. * See <SASL mechanisms> for a list of available mechanisms.
  25539. *
  25540. * Parameters:
  25541. * (Strophe.Connection) connection - Target Connection.
  25542. *
  25543. * Returns:
  25544. * (Boolean) If mechanism was able to run.
  25545. */
  25546. /* jshint unused:false */
  25547. test: function(connection) {
  25548. return true;
  25549. },
  25550. /* jshint unused:true */
  25551. /** PrivateFunction: onStart
  25552. * Called before starting mechanism on some connection.
  25553. *
  25554. * Parameters:
  25555. * (Strophe.Connection) connection - Target Connection.
  25556. */
  25557. onStart: function(connection)
  25558. {
  25559. this._connection = connection;
  25560. },
  25561. /** PrivateFunction: onChallenge
  25562. * Called by protocol implementation on incoming challenge. If client is
  25563. * first (isClientFirst == true) challenge will be null on the first call.
  25564. *
  25565. * Parameters:
  25566. * (Strophe.Connection) connection - Target Connection.
  25567. * (String) challenge - current challenge to handle.
  25568. *
  25569. * Returns:
  25570. * (String) Mechanism response.
  25571. */
  25572. /* jshint unused:false */
  25573. onChallenge: function(connection, challenge) {
  25574. throw new Error("You should implement challenge handling!");
  25575. },
  25576. /* jshint unused:true */
  25577. /** PrivateFunction: onFailure
  25578. * Protocol informs mechanism implementation about SASL failure.
  25579. */
  25580. onFailure: function() {
  25581. this._connection = null;
  25582. },
  25583. /** PrivateFunction: onSuccess
  25584. * Protocol informs mechanism implementation about SASL success.
  25585. */
  25586. onSuccess: function() {
  25587. this._connection = null;
  25588. }
  25589. };
  25590. /** Constants: SASL mechanisms
  25591. * Available authentication mechanisms
  25592. *
  25593. * Strophe.SASLAnonymous - SASL Anonymous authentication.
  25594. * Strophe.SASLPlain - SASL Plain authentication.
  25595. * Strophe.SASLMD5 - SASL Digest-MD5 authentication
  25596. * Strophe.SASLSHA1 - SASL SCRAM-SHA1 authentication
  25597. */
  25598. // Building SASL callbacks
  25599. /** PrivateConstructor: SASLAnonymous
  25600. * SASL Anonymous authentication.
  25601. */
  25602. Strophe.SASLAnonymous = function() {};
  25603. Strophe.SASLAnonymous.prototype = new Strophe.SASLMechanism("ANONYMOUS", false, 10);
  25604. Strophe.SASLAnonymous.test = function(connection) {
  25605. return connection.authcid === null;
  25606. };
  25607. Strophe.Connection.prototype.mechanisms[Strophe.SASLAnonymous.prototype.name] = Strophe.SASLAnonymous;
  25608. /** PrivateConstructor: SASLPlain
  25609. * SASL Plain authentication.
  25610. */
  25611. Strophe.SASLPlain = function() {};
  25612. Strophe.SASLPlain.prototype = new Strophe.SASLMechanism("PLAIN", true, 20);
  25613. Strophe.SASLPlain.test = function(connection) {
  25614. return connection.authcid !== null;
  25615. };
  25616. Strophe.SASLPlain.prototype.onChallenge = function(connection) {
  25617. var auth_str = connection.authzid;
  25618. auth_str = auth_str + "\u0000";
  25619. auth_str = auth_str + connection.authcid;
  25620. auth_str = auth_str + "\u0000";
  25621. auth_str = auth_str + connection.pass;
  25622. return auth_str;
  25623. };
  25624. Strophe.Connection.prototype.mechanisms[Strophe.SASLPlain.prototype.name] = Strophe.SASLPlain;
  25625. /** PrivateConstructor: SASLSHA1
  25626. * SASL SCRAM SHA 1 authentication.
  25627. */
  25628. Strophe.SASLSHA1 = function() {};
  25629. /* TEST:
  25630. * This is a simple example of a SCRAM-SHA-1 authentication exchange
  25631. * when the client doesn't support channel bindings (username 'user' and
  25632. * password 'pencil' are used):
  25633. *
  25634. * C: n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL
  25635. * S: r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,
  25636. * i=4096
  25637. * C: c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,
  25638. * p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=
  25639. * S: v=rmF9pqV8S7suAoZWja4dJRkFsKQ=
  25640. *
  25641. */
  25642. Strophe.SASLSHA1.prototype = new Strophe.SASLMechanism("SCRAM-SHA-1", true, 40);
  25643. Strophe.SASLSHA1.test = function(connection) {
  25644. return connection.authcid !== null;
  25645. };
  25646. Strophe.SASLSHA1.prototype.onChallenge = function(connection, challenge, test_cnonce) {
  25647. var cnonce = test_cnonce || MD5.hexdigest(Math.random() * 1234567890);
  25648. var auth_str = "n=" + connection.authcid;
  25649. auth_str += ",r=";
  25650. auth_str += cnonce;
  25651. connection._sasl_data.cnonce = cnonce;
  25652. connection._sasl_data["client-first-message-bare"] = auth_str;
  25653. auth_str = "n,," + auth_str;
  25654. this.onChallenge = function (connection, challenge)
  25655. {
  25656. var nonce, salt, iter, Hi, U, U_old, i, k;
  25657. var clientKey, serverKey, clientSignature;
  25658. var responseText = "c=biws,";
  25659. var authMessage = connection._sasl_data["client-first-message-bare"] + "," +
  25660. challenge + ",";
  25661. var cnonce = connection._sasl_data.cnonce;
  25662. var attribMatch = /([a-z]+)=([^,]+)(,|$)/;
  25663. while (challenge.match(attribMatch)) {
  25664. var matches = challenge.match(attribMatch);
  25665. challenge = challenge.replace(matches[0], "");
  25666. switch (matches[1]) {
  25667. case "r":
  25668. nonce = matches[2];
  25669. break;
  25670. case "s":
  25671. salt = matches[2];
  25672. break;
  25673. case "i":
  25674. iter = matches[2];
  25675. break;
  25676. }
  25677. }
  25678. if (nonce.substr(0, cnonce.length) !== cnonce) {
  25679. connection._sasl_data = {};
  25680. return connection._sasl_failure_cb();
  25681. }
  25682. responseText += "r=" + nonce;
  25683. authMessage += responseText;
  25684. salt = Base64.decode(salt);
  25685. salt += "\x00\x00\x00\x01";
  25686. Hi = U_old = core_hmac_sha1(connection.pass, salt);
  25687. for (i = 1; i < iter; i++) {
  25688. U = core_hmac_sha1(connection.pass, binb2str(U_old));
  25689. for (k = 0; k < 5; k++) {
  25690. Hi[k] ^= U[k];
  25691. }
  25692. U_old = U;
  25693. }
  25694. Hi = binb2str(Hi);
  25695. clientKey = core_hmac_sha1(Hi, "Client Key");
  25696. serverKey = str_hmac_sha1(Hi, "Server Key");
  25697. clientSignature = core_hmac_sha1(str_sha1(binb2str(clientKey)), authMessage);
  25698. connection._sasl_data["server-signature"] = b64_hmac_sha1(serverKey, authMessage);
  25699. for (k = 0; k < 5; k++) {
  25700. clientKey[k] ^= clientSignature[k];
  25701. }
  25702. responseText += ",p=" + Base64.encode(binb2str(clientKey));
  25703. return responseText;
  25704. }.bind(this);
  25705. return auth_str;
  25706. };
  25707. Strophe.Connection.prototype.mechanisms[Strophe.SASLSHA1.prototype.name] = Strophe.SASLSHA1;
  25708. /** PrivateConstructor: SASLMD5
  25709. * SASL DIGEST MD5 authentication.
  25710. */
  25711. Strophe.SASLMD5 = function() {};
  25712. Strophe.SASLMD5.prototype = new Strophe.SASLMechanism("DIGEST-MD5", false, 30);
  25713. Strophe.SASLMD5.test = function(connection) {
  25714. return connection.authcid !== null;
  25715. };
  25716. /** PrivateFunction: _quote
  25717. * _Private_ utility function to backslash escape and quote strings.
  25718. *
  25719. * Parameters:
  25720. * (String) str - The string to be quoted.
  25721. *
  25722. * Returns:
  25723. * quoted string
  25724. */
  25725. Strophe.SASLMD5.prototype._quote = function (str)
  25726. {
  25727. return '"' + str.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';
  25728. //" end string workaround for emacs
  25729. };
  25730. Strophe.SASLMD5.prototype.onChallenge = function(connection, challenge, test_cnonce) {
  25731. var attribMatch = /([a-z]+)=("[^"]+"|[^,"]+)(?:,|$)/;
  25732. var cnonce = test_cnonce || MD5.hexdigest("" + (Math.random() * 1234567890));
  25733. var realm = "";
  25734. var host = null;
  25735. var nonce = "";
  25736. var qop = "";
  25737. var matches;
  25738. while (challenge.match(attribMatch)) {
  25739. matches = challenge.match(attribMatch);
  25740. challenge = challenge.replace(matches[0], "");
  25741. matches[2] = matches[2].replace(/^"(.+)"$/, "$1");
  25742. switch (matches[1]) {
  25743. case "realm":
  25744. realm = matches[2];
  25745. break;
  25746. case "nonce":
  25747. nonce = matches[2];
  25748. break;
  25749. case "qop":
  25750. qop = matches[2];
  25751. break;
  25752. case "host":
  25753. host = matches[2];
  25754. break;
  25755. }
  25756. }
  25757. var digest_uri = connection.servtype + "/" + connection.domain;
  25758. if (host !== null) {
  25759. digest_uri = digest_uri + "/" + host;
  25760. }
  25761. var A1 = MD5.hash(connection.authcid +
  25762. ":" + realm + ":" + this._connection.pass) +
  25763. ":" + nonce + ":" + cnonce;
  25764. var A2 = 'AUTHENTICATE:' + digest_uri;
  25765. var responseText = "";
  25766. responseText += 'charset=utf-8,';
  25767. responseText += 'username=' +
  25768. this._quote(connection.authcid) + ',';
  25769. responseText += 'realm=' + this._quote(realm) + ',';
  25770. responseText += 'nonce=' + this._quote(nonce) + ',';
  25771. responseText += 'nc=00000001,';
  25772. responseText += 'cnonce=' + this._quote(cnonce) + ',';
  25773. responseText += 'digest-uri=' + this._quote(digest_uri) + ',';
  25774. responseText += 'response=' + MD5.hexdigest(MD5.hexdigest(A1) + ":" +
  25775. nonce + ":00000001:" +
  25776. cnonce + ":auth:" +
  25777. MD5.hexdigest(A2)) + ",";
  25778. responseText += 'qop=auth';
  25779. this.onChallenge = function ()
  25780. {
  25781. return "";
  25782. }.bind(this);
  25783. return responseText;
  25784. };
  25785. Strophe.Connection.prototype.mechanisms[Strophe.SASLMD5.prototype.name] = Strophe.SASLMD5;
  25786. })(function () {
  25787. window.Strophe = arguments[0];
  25788. window.$build = arguments[1];
  25789. window.$msg = arguments[2];
  25790. window.$iq = arguments[3];
  25791. window.$pres = arguments[4];
  25792. });
  25793. /*
  25794. This program is distributed under the terms of the MIT license.
  25795. Please see the LICENSE file for details.
  25796. Copyright 2006-2008, OGG, LLC
  25797. */
  25798. /* jshint undef: true, unused: true:, noarg: true, latedef: true */
  25799. /*global window, setTimeout, clearTimeout,
  25800. XMLHttpRequest, ActiveXObject,
  25801. Strophe, $build */
  25802. /** PrivateClass: Strophe.Request
  25803. * _Private_ helper class that provides a cross implementation abstraction
  25804. * for a BOSH related XMLHttpRequest.
  25805. *
  25806. * The Strophe.Request class is used internally to encapsulate BOSH request
  25807. * information. It is not meant to be used from user's code.
  25808. */
  25809. /** PrivateConstructor: Strophe.Request
  25810. * Create and initialize a new Strophe.Request object.
  25811. *
  25812. * Parameters:
  25813. * (XMLElement) elem - The XML data to be sent in the request.
  25814. * (Function) func - The function that will be called when the
  25815. * XMLHttpRequest readyState changes.
  25816. * (Integer) rid - The BOSH rid attribute associated with this request.
  25817. * (Integer) sends - The number of times this same request has been
  25818. * sent.
  25819. */
  25820. Strophe.Request = function (elem, func, rid, sends)
  25821. {
  25822. this.id = ++Strophe._requestId;
  25823. this.xmlData = elem;
  25824. this.data = Strophe.serialize(elem);
  25825. // save original function in case we need to make a new request
  25826. // from this one.
  25827. this.origFunc = func;
  25828. this.func = func;
  25829. this.rid = rid;
  25830. this.date = NaN;
  25831. this.sends = sends || 0;
  25832. this.abort = false;
  25833. this.dead = null;
  25834. this.age = function () {
  25835. if (!this.date) { return 0; }
  25836. var now = new Date();
  25837. return (now - this.date) / 1000;
  25838. };
  25839. this.timeDead = function () {
  25840. if (!this.dead) { return 0; }
  25841. var now = new Date();
  25842. return (now - this.dead) / 1000;
  25843. };
  25844. this.xhr = this._newXHR();
  25845. };
  25846. Strophe.Request.prototype = {
  25847. /** PrivateFunction: getResponse
  25848. * Get a response from the underlying XMLHttpRequest.
  25849. *
  25850. * This function attempts to get a response from the request and checks
  25851. * for errors.
  25852. *
  25853. * Throws:
  25854. * "parsererror" - A parser error occured.
  25855. *
  25856. * Returns:
  25857. * The DOM element tree of the response.
  25858. */
  25859. getResponse: function ()
  25860. {
  25861. var node = null;
  25862. if (this.xhr.responseXML && this.xhr.responseXML.documentElement) {
  25863. node = this.xhr.responseXML.documentElement;
  25864. if (node.tagName == "parsererror") {
  25865. Strophe.error("invalid response received");
  25866. Strophe.error("responseText: " + this.xhr.responseText);
  25867. Strophe.error("responseXML: " +
  25868. Strophe.serialize(this.xhr.responseXML));
  25869. throw "parsererror";
  25870. }
  25871. } else if (this.xhr.responseText) {
  25872. Strophe.error("invalid response received");
  25873. Strophe.error("responseText: " + this.xhr.responseText);
  25874. Strophe.error("responseXML: " +
  25875. Strophe.serialize(this.xhr.responseXML));
  25876. }
  25877. return node;
  25878. },
  25879. /** PrivateFunction: _newXHR
  25880. * _Private_ helper function to create XMLHttpRequests.
  25881. *
  25882. * This function creates XMLHttpRequests across all implementations.
  25883. *
  25884. * Returns:
  25885. * A new XMLHttpRequest.
  25886. */
  25887. _newXHR: function ()
  25888. {
  25889. var xhr = null;
  25890. if (window.XMLHttpRequest) {
  25891. xhr = new XMLHttpRequest();
  25892. if (xhr.overrideMimeType) {
  25893. xhr.overrideMimeType("text/xml");
  25894. }
  25895. } else if (window.ActiveXObject) {
  25896. xhr = new ActiveXObject("Microsoft.XMLHTTP");
  25897. }
  25898. // use Function.bind() to prepend ourselves as an argument
  25899. xhr.onreadystatechange = this.func.bind(null, this);
  25900. return xhr;
  25901. }
  25902. };
  25903. /** Class: Strophe.Bosh
  25904. * _Private_ helper class that handles BOSH Connections
  25905. *
  25906. * The Strophe.Bosh class is used internally by Strophe.Connection
  25907. * to encapsulate BOSH sessions. It is not meant to be used from user's code.
  25908. */
  25909. /** File: bosh.js
  25910. * A JavaScript library to enable BOSH in Strophejs.
  25911. *
  25912. * this library uses Bidirectional-streams Over Synchronous HTTP (BOSH)
  25913. * to emulate a persistent, stateful, two-way connection to an XMPP server.
  25914. * More information on BOSH can be found in XEP 124.
  25915. */
  25916. /** PrivateConstructor: Strophe.Bosh
  25917. * Create and initialize a Strophe.Bosh object.
  25918. *
  25919. * Parameters:
  25920. * (Strophe.Connection) connection - The Strophe.Connection that will use BOSH.
  25921. *
  25922. * Returns:
  25923. * A new Strophe.Bosh object.
  25924. */
  25925. Strophe.Bosh = function(connection) {
  25926. this._conn = connection;
  25927. /* request id for body tags */
  25928. this.rid = Math.floor(Math.random() * 4294967295);
  25929. /* The current session ID. */
  25930. this.sid = null;
  25931. // default BOSH values
  25932. this.hold = 1;
  25933. this.wait = 60;
  25934. this.window = 5;
  25935. this._requests = [];
  25936. };
  25937. Strophe.Bosh.prototype = {
  25938. /** Variable: strip
  25939. *
  25940. * BOSH-Connections will have all stanzas wrapped in a <body> tag when
  25941. * passed to <Strophe.Connection.xmlInput> or <Strophe.Connection.xmlOutput>.
  25942. * To strip this tag, User code can set <Strophe.Bosh.strip> to "body":
  25943. *
  25944. * > Strophe.Bosh.prototype.strip = "body";
  25945. *
  25946. * This will enable stripping of the body tag in both
  25947. * <Strophe.Connection.xmlInput> and <Strophe.Connection.xmlOutput>.
  25948. */
  25949. strip: null,
  25950. /** PrivateFunction: _buildBody
  25951. * _Private_ helper function to generate the <body/> wrapper for BOSH.
  25952. *
  25953. * Returns:
  25954. * A Strophe.Builder with a <body/> element.
  25955. */
  25956. _buildBody: function ()
  25957. {
  25958. var bodyWrap = $build('body', {
  25959. rid: this.rid++,
  25960. xmlns: Strophe.NS.HTTPBIND
  25961. });
  25962. if (this.sid !== null) {
  25963. bodyWrap.attrs({sid: this.sid});
  25964. }
  25965. return bodyWrap;
  25966. },
  25967. /** PrivateFunction: _reset
  25968. * Reset the connection.
  25969. *
  25970. * This function is called by the reset function of the Strophe Connection
  25971. */
  25972. _reset: function ()
  25973. {
  25974. this.rid = Math.floor(Math.random() * 4294967295);
  25975. this.sid = null;
  25976. },
  25977. /** PrivateFunction: _connect
  25978. * _Private_ function that initializes the BOSH connection.
  25979. *
  25980. * Creates and sends the Request that initializes the BOSH connection.
  25981. */
  25982. _connect: function (wait, hold, route)
  25983. {
  25984. this.wait = wait || this.wait;
  25985. this.hold = hold || this.hold;
  25986. // build the body tag
  25987. var body = this._buildBody().attrs({
  25988. to: this._conn.domain,
  25989. "xml:lang": "en",
  25990. wait: this.wait,
  25991. hold: this.hold,
  25992. content: "text/xml; charset=utf-8",
  25993. ver: "1.6",
  25994. "xmpp:version": "1.0",
  25995. "xmlns:xmpp": Strophe.NS.BOSH
  25996. });
  25997. if(route){
  25998. body.attrs({
  25999. route: route
  26000. });
  26001. }
  26002. var _connect_cb = this._conn._connect_cb;
  26003. this._requests.push(
  26004. new Strophe.Request(body.tree(),
  26005. this._onRequestStateChange.bind(
  26006. this, _connect_cb.bind(this._conn)),
  26007. body.tree().getAttribute("rid")));
  26008. this._throttledRequestHandler();
  26009. },
  26010. /** PrivateFunction: _attach
  26011. * Attach to an already created and authenticated BOSH session.
  26012. *
  26013. * This function is provided to allow Strophe to attach to BOSH
  26014. * sessions which have been created externally, perhaps by a Web
  26015. * application. This is often used to support auto-login type features
  26016. * without putting user credentials into the page.
  26017. *
  26018. * Parameters:
  26019. * (String) jid - The full JID that is bound by the session.
  26020. * (String) sid - The SID of the BOSH session.
  26021. * (String) rid - The current RID of the BOSH session. This RID
  26022. * will be used by the next request.
  26023. * (Function) callback The connect callback function.
  26024. * (Integer) wait - The optional HTTPBIND wait value. This is the
  26025. * time the server will wait before returning an empty result for
  26026. * a request. The default setting of 60 seconds is recommended.
  26027. * Other settings will require tweaks to the Strophe.TIMEOUT value.
  26028. * (Integer) hold - The optional HTTPBIND hold value. This is the
  26029. * number of connections the server will hold at one time. This
  26030. * should almost always be set to 1 (the default).
  26031. * (Integer) wind - The optional HTTBIND window value. This is the
  26032. * allowed range of request ids that are valid. The default is 5.
  26033. */
  26034. _attach: function (jid, sid, rid, callback, wait, hold, wind)
  26035. {
  26036. this._conn.jid = jid;
  26037. this.sid = sid;
  26038. this.rid = rid;
  26039. this._conn.connect_callback = callback;
  26040. this._conn.domain = Strophe.getDomainFromJid(this._conn.jid);
  26041. this._conn.authenticated = true;
  26042. this._conn.connected = true;
  26043. this.wait = wait || this.wait;
  26044. this.hold = hold || this.hold;
  26045. this.window = wind || this.window;
  26046. this._conn._changeConnectStatus(Strophe.Status.ATTACHED, null);
  26047. },
  26048. /** PrivateFunction: _connect_cb
  26049. * _Private_ handler for initial connection request.
  26050. *
  26051. * This handler is used to process the Bosh-part of the initial request.
  26052. * Parameters:
  26053. * (Strophe.Request) bodyWrap - The received stanza.
  26054. */
  26055. _connect_cb: function (bodyWrap)
  26056. {
  26057. var typ = bodyWrap.getAttribute("type");
  26058. var cond, conflict;
  26059. if (typ !== null && typ == "terminate") {
  26060. // an error occurred
  26061. Strophe.error("BOSH-Connection failed: " + cond);
  26062. cond = bodyWrap.getAttribute("condition");
  26063. conflict = bodyWrap.getElementsByTagName("conflict");
  26064. if (cond !== null) {
  26065. if (cond == "remote-stream-error" && conflict.length > 0) {
  26066. cond = "conflict";
  26067. }
  26068. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, cond);
  26069. } else {
  26070. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "unknown");
  26071. }
  26072. this._conn._doDisconnect();
  26073. return Strophe.Status.CONNFAIL;
  26074. }
  26075. // check to make sure we don't overwrite these if _connect_cb is
  26076. // called multiple times in the case of missing stream:features
  26077. if (!this.sid) {
  26078. this.sid = bodyWrap.getAttribute("sid");
  26079. }
  26080. var wind = bodyWrap.getAttribute('requests');
  26081. if (wind) { this.window = parseInt(wind, 10); }
  26082. var hold = bodyWrap.getAttribute('hold');
  26083. if (hold) { this.hold = parseInt(hold, 10); }
  26084. var wait = bodyWrap.getAttribute('wait');
  26085. if (wait) { this.wait = parseInt(wait, 10); }
  26086. },
  26087. /** PrivateFunction: _disconnect
  26088. * _Private_ part of Connection.disconnect for Bosh
  26089. *
  26090. * Parameters:
  26091. * (Request) pres - This stanza will be sent before disconnecting.
  26092. */
  26093. _disconnect: function (pres)
  26094. {
  26095. this._sendTerminate(pres);
  26096. },
  26097. /** PrivateFunction: _doDisconnect
  26098. * _Private_ function to disconnect.
  26099. *
  26100. * Resets the SID and RID.
  26101. */
  26102. _doDisconnect: function ()
  26103. {
  26104. this.sid = null;
  26105. this.rid = Math.floor(Math.random() * 4294967295);
  26106. },
  26107. /** PrivateFunction: _emptyQueue
  26108. * _Private_ function to check if the Request queue is empty.
  26109. *
  26110. * Returns:
  26111. * True, if there are no Requests queued, False otherwise.
  26112. */
  26113. _emptyQueue: function ()
  26114. {
  26115. return this._requests.length === 0;
  26116. },
  26117. /** PrivateFunction: _hitError
  26118. * _Private_ function to handle the error count.
  26119. *
  26120. * Requests are resent automatically until their error count reaches
  26121. * 5. Each time an error is encountered, this function is called to
  26122. * increment the count and disconnect if the count is too high.
  26123. *
  26124. * Parameters:
  26125. * (Integer) reqStatus - The request status.
  26126. */
  26127. _hitError: function (reqStatus)
  26128. {
  26129. this.errors++;
  26130. Strophe.warn("request errored, status: " + reqStatus +
  26131. ", number of errors: " + this.errors);
  26132. if (this.errors > 4) {
  26133. this._onDisconnectTimeout();
  26134. }
  26135. },
  26136. /** PrivateFunction: _no_auth_received
  26137. *
  26138. * Called on stream start/restart when no stream:features
  26139. * has been received and sends a blank poll request.
  26140. */
  26141. _no_auth_received: function (_callback)
  26142. {
  26143. if (_callback) {
  26144. _callback = _callback.bind(this._conn);
  26145. } else {
  26146. _callback = this._conn._connect_cb.bind(this._conn);
  26147. }
  26148. var body = this._buildBody();
  26149. this._requests.push(
  26150. new Strophe.Request(body.tree(),
  26151. this._onRequestStateChange.bind(
  26152. this, _callback.bind(this._conn)),
  26153. body.tree().getAttribute("rid")));
  26154. this._throttledRequestHandler();
  26155. },
  26156. /** PrivateFunction: _onDisconnectTimeout
  26157. * _Private_ timeout handler for handling non-graceful disconnection.
  26158. *
  26159. * Cancels all remaining Requests and clears the queue.
  26160. */
  26161. _onDisconnectTimeout: function ()
  26162. {
  26163. var req;
  26164. while (this._requests.length > 0) {
  26165. req = this._requests.pop();
  26166. req.abort = true;
  26167. req.xhr.abort();
  26168. // jslint complains, but this is fine. setting to empty func
  26169. // is necessary for IE6
  26170. req.xhr.onreadystatechange = function () {}; // jshint ignore:line
  26171. }
  26172. },
  26173. /** PrivateFunction: _onIdle
  26174. * _Private_ handler called by Strophe.Connection._onIdle
  26175. *
  26176. * Sends all queued Requests or polls with empty Request if there are none.
  26177. */
  26178. _onIdle: function () {
  26179. var data = this._conn._data;
  26180. // if no requests are in progress, poll
  26181. if (this._conn.authenticated && this._requests.length === 0 &&
  26182. data.length === 0 && !this._conn.disconnecting) {
  26183. Strophe.info("no requests during idle cycle, sending " +
  26184. "blank request");
  26185. data.push(null);
  26186. }
  26187. if (this._requests.length < 2 && data.length > 0 &&
  26188. !this._conn.paused) {
  26189. var body = this._buildBody();
  26190. for (var i = 0; i < data.length; i++) {
  26191. if (data[i] !== null) {
  26192. if (data[i] === "restart") {
  26193. body.attrs({
  26194. to: this._conn.domain,
  26195. "xml:lang": "en",
  26196. "xmpp:restart": "true",
  26197. "xmlns:xmpp": Strophe.NS.BOSH
  26198. });
  26199. } else {
  26200. body.cnode(data[i]).up();
  26201. }
  26202. }
  26203. }
  26204. delete this._conn._data;
  26205. this._conn._data = [];
  26206. this._requests.push(
  26207. new Strophe.Request(body.tree(),
  26208. this._onRequestStateChange.bind(
  26209. this, this._conn._dataRecv.bind(this._conn)),
  26210. body.tree().getAttribute("rid")));
  26211. this._processRequest(this._requests.length - 1);
  26212. }
  26213. if (this._requests.length > 0) {
  26214. var time_elapsed = this._requests[0].age();
  26215. if (this._requests[0].dead !== null) {
  26216. if (this._requests[0].timeDead() >
  26217. Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait)) {
  26218. this._throttledRequestHandler();
  26219. }
  26220. }
  26221. if (time_elapsed > Math.floor(Strophe.TIMEOUT * this.wait)) {
  26222. Strophe.warn("Request " +
  26223. this._requests[0].id +
  26224. " timed out, over " + Math.floor(Strophe.TIMEOUT * this.wait) +
  26225. " seconds since last activity");
  26226. this._throttledRequestHandler();
  26227. }
  26228. }
  26229. },
  26230. /** PrivateFunction: _onRequestStateChange
  26231. * _Private_ handler for Strophe.Request state changes.
  26232. *
  26233. * This function is called when the XMLHttpRequest readyState changes.
  26234. * It contains a lot of error handling logic for the many ways that
  26235. * requests can fail, and calls the request callback when requests
  26236. * succeed.
  26237. *
  26238. * Parameters:
  26239. * (Function) func - The handler for the request.
  26240. * (Strophe.Request) req - The request that is changing readyState.
  26241. */
  26242. _onRequestStateChange: function (func, req)
  26243. {
  26244. Strophe.debug("request id " + req.id +
  26245. "." + req.sends + " state changed to " +
  26246. req.xhr.readyState);
  26247. if (req.abort) {
  26248. req.abort = false;
  26249. return;
  26250. }
  26251. // request complete
  26252. var reqStatus;
  26253. if (req.xhr.readyState == 4) {
  26254. reqStatus = 0;
  26255. try {
  26256. reqStatus = req.xhr.status;
  26257. } catch (e) {
  26258. // ignore errors from undefined status attribute. works
  26259. // around a browser bug
  26260. }
  26261. if (typeof(reqStatus) == "undefined") {
  26262. reqStatus = 0;
  26263. }
  26264. if (this.disconnecting) {
  26265. if (reqStatus >= 400) {
  26266. this._hitError(reqStatus);
  26267. return;
  26268. }
  26269. }
  26270. var reqIs0 = (this._requests[0] == req);
  26271. var reqIs1 = (this._requests[1] == req);
  26272. if ((reqStatus > 0 && reqStatus < 500) || req.sends > 5) {
  26273. // remove from internal queue
  26274. this._removeRequest(req);
  26275. Strophe.debug("request id " +
  26276. req.id +
  26277. " should now be removed");
  26278. }
  26279. // request succeeded
  26280. if (reqStatus == 200) {
  26281. // if request 1 finished, or request 0 finished and request
  26282. // 1 is over Strophe.SECONDARY_TIMEOUT seconds old, we need to
  26283. // restart the other - both will be in the first spot, as the
  26284. // completed request has been removed from the queue already
  26285. if (reqIs1 ||
  26286. (reqIs0 && this._requests.length > 0 &&
  26287. this._requests[0].age() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait))) {
  26288. this._restartRequest(0);
  26289. }
  26290. // call handler
  26291. Strophe.debug("request id " +
  26292. req.id + "." +
  26293. req.sends + " got 200");
  26294. func(req);
  26295. this.errors = 0;
  26296. } else {
  26297. Strophe.error("request id " +
  26298. req.id + "." +
  26299. req.sends + " error " + reqStatus +
  26300. " happened");
  26301. if (reqStatus === 0 ||
  26302. (reqStatus >= 400 && reqStatus < 600) ||
  26303. reqStatus >= 12000) {
  26304. this._hitError(reqStatus);
  26305. if (reqStatus >= 400 && reqStatus < 500) {
  26306. this._conn._changeConnectStatus(Strophe.Status.DISCONNECTING,
  26307. null);
  26308. this._conn._doDisconnect();
  26309. }
  26310. }
  26311. }
  26312. if (!((reqStatus > 0 && reqStatus < 500) ||
  26313. req.sends > 5)) {
  26314. this._throttledRequestHandler();
  26315. }
  26316. }
  26317. },
  26318. /** PrivateFunction: _processRequest
  26319. * _Private_ function to process a request in the queue.
  26320. *
  26321. * This function takes requests off the queue and sends them and
  26322. * restarts dead requests.
  26323. *
  26324. * Parameters:
  26325. * (Integer) i - The index of the request in the queue.
  26326. */
  26327. _processRequest: function (i)
  26328. {
  26329. var self = this;
  26330. var req = this._requests[i];
  26331. var reqStatus = -1;
  26332. try {
  26333. if (req.xhr.readyState == 4) {
  26334. reqStatus = req.xhr.status;
  26335. }
  26336. } catch (e) {
  26337. Strophe.error("caught an error in _requests[" + i +
  26338. "], reqStatus: " + reqStatus);
  26339. }
  26340. if (typeof(reqStatus) == "undefined") {
  26341. reqStatus = -1;
  26342. }
  26343. // make sure we limit the number of retries
  26344. if (req.sends > this.maxRetries) {
  26345. this._onDisconnectTimeout();
  26346. return;
  26347. }
  26348. var time_elapsed = req.age();
  26349. var primaryTimeout = (!isNaN(time_elapsed) &&
  26350. time_elapsed > Math.floor(Strophe.TIMEOUT * this.wait));
  26351. var secondaryTimeout = (req.dead !== null &&
  26352. req.timeDead() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait));
  26353. var requestCompletedWithServerError = (req.xhr.readyState == 4 &&
  26354. (reqStatus < 1 ||
  26355. reqStatus >= 500));
  26356. if (primaryTimeout || secondaryTimeout ||
  26357. requestCompletedWithServerError) {
  26358. if (secondaryTimeout) {
  26359. Strophe.error("Request " +
  26360. this._requests[i].id +
  26361. " timed out (secondary), restarting");
  26362. }
  26363. req.abort = true;
  26364. req.xhr.abort();
  26365. // setting to null fails on IE6, so set to empty function
  26366. req.xhr.onreadystatechange = function () {};
  26367. this._requests[i] = new Strophe.Request(req.xmlData,
  26368. req.origFunc,
  26369. req.rid,
  26370. req.sends);
  26371. req = this._requests[i];
  26372. }
  26373. if (req.xhr.readyState === 0) {
  26374. Strophe.debug("request id " + req.id +
  26375. "." + req.sends + " posting");
  26376. try {
  26377. req.xhr.open("POST", this._conn.service, this._conn.options.sync ? false : true);
  26378. } catch (e2) {
  26379. Strophe.error("XHR open failed.");
  26380. if (!this._conn.connected) {
  26381. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL,
  26382. "bad-service");
  26383. }
  26384. this._conn.disconnect();
  26385. return;
  26386. }
  26387. // Fires the XHR request -- may be invoked immediately
  26388. // or on a gradually expanding retry window for reconnects
  26389. var sendFunc = function () {
  26390. req.date = new Date();
  26391. if (self._conn.options.customHeaders){
  26392. var headers = self._conn.options.customHeaders;
  26393. for (var header in headers) {
  26394. if (headers.hasOwnProperty(header)) {
  26395. req.xhr.setRequestHeader(header, headers[header]);
  26396. }
  26397. }
  26398. }
  26399. req.xhr.send(req.data);
  26400. };
  26401. // Implement progressive backoff for reconnects --
  26402. // First retry (send == 1) should also be instantaneous
  26403. if (req.sends > 1) {
  26404. // Using a cube of the retry number creates a nicely
  26405. // expanding retry window
  26406. var backoff = Math.min(Math.floor(Strophe.TIMEOUT * this.wait),
  26407. Math.pow(req.sends, 3)) * 1000;
  26408. setTimeout(sendFunc, backoff);
  26409. } else {
  26410. sendFunc();
  26411. }
  26412. req.sends++;
  26413. if (this._conn.xmlOutput !== Strophe.Connection.prototype.xmlOutput) {
  26414. if (req.xmlData.nodeName === this.strip && req.xmlData.childNodes.length) {
  26415. this._conn.xmlOutput(req.xmlData.childNodes[0]);
  26416. } else {
  26417. this._conn.xmlOutput(req.xmlData);
  26418. }
  26419. }
  26420. if (this._conn.rawOutput !== Strophe.Connection.prototype.rawOutput) {
  26421. this._conn.rawOutput(req.data);
  26422. }
  26423. } else {
  26424. Strophe.debug("_processRequest: " +
  26425. (i === 0 ? "first" : "second") +
  26426. " request has readyState of " +
  26427. req.xhr.readyState);
  26428. }
  26429. },
  26430. /** PrivateFunction: _removeRequest
  26431. * _Private_ function to remove a request from the queue.
  26432. *
  26433. * Parameters:
  26434. * (Strophe.Request) req - The request to remove.
  26435. */
  26436. _removeRequest: function (req)
  26437. {
  26438. Strophe.debug("removing request");
  26439. var i;
  26440. for (i = this._requests.length - 1; i >= 0; i--) {
  26441. if (req == this._requests[i]) {
  26442. this._requests.splice(i, 1);
  26443. }
  26444. }
  26445. // IE6 fails on setting to null, so set to empty function
  26446. req.xhr.onreadystatechange = function () {};
  26447. this._throttledRequestHandler();
  26448. },
  26449. /** PrivateFunction: _restartRequest
  26450. * _Private_ function to restart a request that is presumed dead.
  26451. *
  26452. * Parameters:
  26453. * (Integer) i - The index of the request in the queue.
  26454. */
  26455. _restartRequest: function (i)
  26456. {
  26457. var req = this._requests[i];
  26458. if (req.dead === null) {
  26459. req.dead = new Date();
  26460. }
  26461. this._processRequest(i);
  26462. },
  26463. /** PrivateFunction: _reqToData
  26464. * _Private_ function to get a stanza out of a request.
  26465. *
  26466. * Tries to extract a stanza out of a Request Object.
  26467. * When this fails the current connection will be disconnected.
  26468. *
  26469. * Parameters:
  26470. * (Object) req - The Request.
  26471. *
  26472. * Returns:
  26473. * The stanza that was passed.
  26474. */
  26475. _reqToData: function (req)
  26476. {
  26477. try {
  26478. return req.getResponse();
  26479. } catch (e) {
  26480. if (e != "parsererror") { throw e; }
  26481. this._conn.disconnect("strophe-parsererror");
  26482. }
  26483. },
  26484. /** PrivateFunction: _sendTerminate
  26485. * _Private_ function to send initial disconnect sequence.
  26486. *
  26487. * This is the first step in a graceful disconnect. It sends
  26488. * the BOSH server a terminate body and includes an unavailable
  26489. * presence if authentication has completed.
  26490. */
  26491. _sendTerminate: function (pres)
  26492. {
  26493. Strophe.info("_sendTerminate was called");
  26494. var body = this._buildBody().attrs({type: "terminate"});
  26495. if (pres) {
  26496. body.cnode(pres.tree());
  26497. }
  26498. var req = new Strophe.Request(body.tree(),
  26499. this._onRequestStateChange.bind(
  26500. this, this._conn._dataRecv.bind(this._conn)),
  26501. body.tree().getAttribute("rid"));
  26502. this._requests.push(req);
  26503. this._throttledRequestHandler();
  26504. },
  26505. /** PrivateFunction: _send
  26506. * _Private_ part of the Connection.send function for BOSH
  26507. *
  26508. * Just triggers the RequestHandler to send the messages that are in the queue
  26509. */
  26510. _send: function () {
  26511. clearTimeout(this._conn._idleTimeout);
  26512. this._throttledRequestHandler();
  26513. this._conn._idleTimeout = setTimeout(this._conn._onIdle.bind(this._conn), 100);
  26514. },
  26515. /** PrivateFunction: _sendRestart
  26516. *
  26517. * Send an xmpp:restart stanza.
  26518. */
  26519. _sendRestart: function ()
  26520. {
  26521. this._throttledRequestHandler();
  26522. clearTimeout(this._conn._idleTimeout);
  26523. },
  26524. /** PrivateFunction: _throttledRequestHandler
  26525. * _Private_ function to throttle requests to the connection window.
  26526. *
  26527. * This function makes sure we don't send requests so fast that the
  26528. * request ids overflow the connection window in the case that one
  26529. * request died.
  26530. */
  26531. _throttledRequestHandler: function ()
  26532. {
  26533. if (!this._requests) {
  26534. Strophe.debug("_throttledRequestHandler called with " +
  26535. "undefined requests");
  26536. } else {
  26537. Strophe.debug("_throttledRequestHandler called with " +
  26538. this._requests.length + " requests");
  26539. }
  26540. if (!this._requests || this._requests.length === 0) {
  26541. return;
  26542. }
  26543. if (this._requests.length > 0) {
  26544. this._processRequest(0);
  26545. }
  26546. if (this._requests.length > 1 &&
  26547. Math.abs(this._requests[0].rid -
  26548. this._requests[1].rid) < this.window) {
  26549. this._processRequest(1);
  26550. }
  26551. }
  26552. };
  26553. /*
  26554. This program is distributed under the terms of the MIT license.
  26555. Please see the LICENSE file for details.
  26556. Copyright 2006-2008, OGG, LLC
  26557. */
  26558. /* jshint undef: true, unused: true:, noarg: true, latedef: true */
  26559. /*global document, window, clearTimeout, WebSocket,
  26560. DOMParser, Strophe, $build */
  26561. /** Class: Strophe.WebSocket
  26562. * _Private_ helper class that handles WebSocket Connections
  26563. *
  26564. * The Strophe.WebSocket class is used internally by Strophe.Connection
  26565. * to encapsulate WebSocket sessions. It is not meant to be used from user's code.
  26566. */
  26567. /** File: websocket.js
  26568. * A JavaScript library to enable XMPP over Websocket in Strophejs.
  26569. *
  26570. * This file implements XMPP over WebSockets for Strophejs.
  26571. * If a Connection is established with a Websocket url (ws://...)
  26572. * Strophe will use WebSockets.
  26573. * For more information on XMPP-over WebSocket see this RFC draft:
  26574. * http://tools.ietf.org/html/draft-ietf-xmpp-websocket-00
  26575. *
  26576. * WebSocket support implemented by Andreas Guth (andreas.guth@rwth-aachen.de)
  26577. */
  26578. /** PrivateConstructor: Strophe.Websocket
  26579. * Create and initialize a Strophe.WebSocket object.
  26580. * Currently only sets the connection Object.
  26581. *
  26582. * Parameters:
  26583. * (Strophe.Connection) connection - The Strophe.Connection that will use WebSockets.
  26584. *
  26585. * Returns:
  26586. * A new Strophe.WebSocket object.
  26587. */
  26588. Strophe.Websocket = function(connection) {
  26589. this._conn = connection;
  26590. this.strip = "stream:stream";
  26591. var service = connection.service;
  26592. if (service.indexOf("ws:") !== 0 && service.indexOf("wss:") !== 0) {
  26593. // If the service is not an absolute URL, assume it is a path and put the absolute
  26594. // URL together from options, current URL and the path.
  26595. var new_service = "";
  26596. if (connection.options.protocol === "ws" && window.location.protocol !== "https:") {
  26597. new_service += "ws";
  26598. } else {
  26599. new_service += "wss";
  26600. }
  26601. new_service += "://" + window.location.host;
  26602. if (service.indexOf("/") !== 0) {
  26603. new_service += window.location.pathname + service;
  26604. } else {
  26605. new_service += service;
  26606. }
  26607. connection.service = new_service;
  26608. }
  26609. };
  26610. Strophe.Websocket.prototype = {
  26611. /** PrivateFunction: _buildStream
  26612. * _Private_ helper function to generate the <stream> start tag for WebSockets
  26613. *
  26614. * Returns:
  26615. * A Strophe.Builder with a <stream> element.
  26616. */
  26617. _buildStream: function ()
  26618. {
  26619. return $build("stream:stream", {
  26620. "to": this._conn.domain,
  26621. "xmlns": Strophe.NS.CLIENT,
  26622. "xmlns:stream": Strophe.NS.STREAM,
  26623. "version": '1.0'
  26624. });
  26625. },
  26626. /** PrivateFunction: _check_streamerror
  26627. * _Private_ checks a message for stream:error
  26628. *
  26629. * Parameters:
  26630. * (Strophe.Request) bodyWrap - The received stanza.
  26631. * connectstatus - The ConnectStatus that will be set on error.
  26632. * Returns:
  26633. * true if there was a streamerror, false otherwise.
  26634. */
  26635. _check_streamerror: function (bodyWrap, connectstatus) {
  26636. var errors = bodyWrap.getElementsByTagName("stream:error");
  26637. if (errors.length === 0) {
  26638. return false;
  26639. }
  26640. var error = errors[0];
  26641. var condition = "";
  26642. var text = "";
  26643. var ns = "urn:ietf:params:xml:ns:xmpp-streams";
  26644. for (var i = 0; i < error.childNodes.length; i++) {
  26645. var e = error.childNodes[i];
  26646. if (e.getAttribute("xmlns") !== ns) {
  26647. break;
  26648. } if (e.nodeName === "text") {
  26649. text = e.textContent;
  26650. } else {
  26651. condition = e.nodeName;
  26652. }
  26653. }
  26654. var errorString = "WebSocket stream error: ";
  26655. if (condition) {
  26656. errorString += condition;
  26657. } else {
  26658. errorString += "unknown";
  26659. }
  26660. if (text) {
  26661. errorString += " - " + condition;
  26662. }
  26663. Strophe.error(errorString);
  26664. // close the connection on stream_error
  26665. this._conn._changeConnectStatus(connectstatus, condition);
  26666. this._conn._doDisconnect();
  26667. return true;
  26668. },
  26669. /** PrivateFunction: _reset
  26670. * Reset the connection.
  26671. *
  26672. * This function is called by the reset function of the Strophe Connection.
  26673. * Is not needed by WebSockets.
  26674. */
  26675. _reset: function ()
  26676. {
  26677. return;
  26678. },
  26679. /** PrivateFunction: _connect
  26680. * _Private_ function called by Strophe.Connection.connect
  26681. *
  26682. * Creates a WebSocket for a connection and assigns Callbacks to it.
  26683. * Does nothing if there already is a WebSocket.
  26684. */
  26685. _connect: function () {
  26686. // Ensure that there is no open WebSocket from a previous Connection.
  26687. this._closeSocket();
  26688. // Create the new WobSocket
  26689. this.socket = new WebSocket(this._conn.service, "xmpp");
  26690. this.socket.onopen = this._onOpen.bind(this);
  26691. this.socket.onerror = this._onError.bind(this);
  26692. this.socket.onclose = this._onClose.bind(this);
  26693. this.socket.onmessage = this._connect_cb_wrapper.bind(this);
  26694. },
  26695. /** PrivateFunction: _connect_cb
  26696. * _Private_ function called by Strophe.Connection._connect_cb
  26697. *
  26698. * checks for stream:error
  26699. *
  26700. * Parameters:
  26701. * (Strophe.Request) bodyWrap - The received stanza.
  26702. */
  26703. _connect_cb: function(bodyWrap) {
  26704. var error = this._check_streamerror(bodyWrap, Strophe.Status.CONNFAIL);
  26705. if (error) {
  26706. return Strophe.Status.CONNFAIL;
  26707. }
  26708. },
  26709. /** PrivateFunction: _handleStreamStart
  26710. * _Private_ function that checks the opening stream:stream tag for errors.
  26711. *
  26712. * Disconnects if there is an error and returns false, true otherwise.
  26713. *
  26714. * Parameters:
  26715. * (Node) message - Stanza containing the stream:stream.
  26716. */
  26717. _handleStreamStart: function(message) {
  26718. var error = false;
  26719. // Check for errors in the stream:stream tag
  26720. var ns = message.getAttribute("xmlns");
  26721. if (typeof ns !== "string") {
  26722. error = "Missing xmlns in stream:stream";
  26723. } else if (ns !== Strophe.NS.CLIENT) {
  26724. error = "Wrong xmlns in stream:stream: " + ns;
  26725. }
  26726. var ns_stream = message.namespaceURI;
  26727. if (typeof ns_stream !== "string") {
  26728. error = "Missing xmlns:stream in stream:stream";
  26729. } else if (ns_stream !== Strophe.NS.STREAM) {
  26730. error = "Wrong xmlns:stream in stream:stream: " + ns_stream;
  26731. }
  26732. var ver = message.getAttribute("version");
  26733. if (typeof ver !== "string") {
  26734. error = "Missing version in stream:stream";
  26735. } else if (ver !== "1.0") {
  26736. error = "Wrong version in stream:stream: " + ver;
  26737. }
  26738. if (error) {
  26739. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, error);
  26740. this._conn._doDisconnect();
  26741. return false;
  26742. }
  26743. return true;
  26744. },
  26745. /** PrivateFunction: _connect_cb_wrapper
  26746. * _Private_ function that handles the first connection messages.
  26747. *
  26748. * On receiving an opening stream tag this callback replaces itself with the real
  26749. * message handler. On receiving a stream error the connection is terminated.
  26750. */
  26751. _connect_cb_wrapper: function(message) {
  26752. if (message.data.indexOf("<stream:stream ") === 0 || message.data.indexOf("<?xml") === 0) {
  26753. // Strip the XML Declaration, if there is one
  26754. var data = message.data.replace(/^(<\?.*?\?>\s*)*/, "");
  26755. if (data === '') return;
  26756. //Make the initial stream:stream selfclosing to parse it without a SAX parser.
  26757. data = message.data.replace(/<stream:stream (.*[^\/])>/, "<stream:stream $1/>");
  26758. var streamStart = new DOMParser().parseFromString(data, "text/xml").documentElement;
  26759. this._conn.xmlInput(streamStart);
  26760. this._conn.rawInput(message.data);
  26761. //_handleStreamSteart will check for XML errors and disconnect on error
  26762. if (this._handleStreamStart(streamStart)) {
  26763. //_connect_cb will check for stream:error and disconnect on error
  26764. this._connect_cb(streamStart);
  26765. // ensure received stream:stream is NOT selfclosing and save it for following messages
  26766. this.streamStart = message.data.replace(/^<stream:(.*)\/>$/, "<stream:$1>");
  26767. }
  26768. } else if (message.data === "</stream:stream>") {
  26769. this._conn.rawInput(message.data);
  26770. this._conn.xmlInput(document.createElement("stream:stream"));
  26771. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "Received closing stream");
  26772. this._conn._doDisconnect();
  26773. return;
  26774. } else {
  26775. this.streamStart = "<stream:stream>";
  26776. var string = this._streamWrap(message.data);
  26777. var elem = new DOMParser().parseFromString(string, "text/xml").documentElement;
  26778. this.socket.onmessage = this._onMessage.bind(this);
  26779. this._conn._connect_cb(elem, null, message.data);
  26780. }
  26781. },
  26782. /** PrivateFunction: _disconnect
  26783. * _Private_ function called by Strophe.Connection.disconnect
  26784. *
  26785. * Disconnects and sends a last stanza if one is given
  26786. *
  26787. * Parameters:
  26788. * (Request) pres - This stanza will be sent before disconnecting.
  26789. */
  26790. _disconnect: function (pres)
  26791. {
  26792. if (this.socket.readyState !== WebSocket.CLOSED) {
  26793. if (pres) {
  26794. this._conn.send(pres);
  26795. }
  26796. var close = '</stream:stream>';
  26797. this._conn.xmlOutput(document.createElement("stream:stream"));
  26798. this._conn.rawOutput(close);
  26799. try {
  26800. this.socket.send(close);
  26801. } catch (e) {
  26802. Strophe.info("Couldn't send closing stream tag.");
  26803. }
  26804. }
  26805. this._conn._doDisconnect();
  26806. },
  26807. /** PrivateFunction: _doDisconnect
  26808. * _Private_ function to disconnect.
  26809. *
  26810. * Just closes the Socket for WebSockets
  26811. */
  26812. _doDisconnect: function ()
  26813. {
  26814. Strophe.info("WebSockets _doDisconnect was called");
  26815. this._closeSocket();
  26816. },
  26817. /** PrivateFunction _streamWrap
  26818. * _Private_ helper function to wrap a stanza in a <stream> tag.
  26819. * This is used so Strophe can process stanzas from WebSockets like BOSH
  26820. */
  26821. _streamWrap: function (stanza)
  26822. {
  26823. return this.streamStart + stanza + '</stream:stream>';
  26824. },
  26825. /** PrivateFunction: _closeSocket
  26826. * _Private_ function to close the WebSocket.
  26827. *
  26828. * Closes the socket if it is still open and deletes it
  26829. */
  26830. _closeSocket: function ()
  26831. {
  26832. if (this.socket) { try {
  26833. this.socket.close();
  26834. } catch (e) {} }
  26835. this.socket = null;
  26836. },
  26837. /** PrivateFunction: _emptyQueue
  26838. * _Private_ function to check if the message queue is empty.
  26839. *
  26840. * Returns:
  26841. * True, because WebSocket messages are send immediately after queueing.
  26842. */
  26843. _emptyQueue: function ()
  26844. {
  26845. return true;
  26846. },
  26847. /** PrivateFunction: _onClose
  26848. * _Private_ function to handle websockets closing.
  26849. *
  26850. * Nothing to do here for WebSockets
  26851. */
  26852. _onClose: function() {
  26853. if(this._conn.connected && !this._conn.disconnecting) {
  26854. Strophe.error("Websocket closed unexcectedly");
  26855. this._conn._doDisconnect();
  26856. } else {
  26857. Strophe.info("Websocket closed");
  26858. }
  26859. },
  26860. /** PrivateFunction: _no_auth_received
  26861. *
  26862. * Called on stream start/restart when no stream:features
  26863. * has been received.
  26864. */
  26865. _no_auth_received: function (_callback)
  26866. {
  26867. Strophe.error("Server did not send any auth methods");
  26868. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "Server did not send any auth methods");
  26869. if (_callback) {
  26870. _callback = _callback.bind(this._conn);
  26871. _callback();
  26872. }
  26873. this._conn._doDisconnect();
  26874. },
  26875. /** PrivateFunction: _onDisconnectTimeout
  26876. * _Private_ timeout handler for handling non-graceful disconnection.
  26877. *
  26878. * This does nothing for WebSockets
  26879. */
  26880. _onDisconnectTimeout: function () {},
  26881. /** PrivateFunction: _onError
  26882. * _Private_ function to handle websockets errors.
  26883. *
  26884. * Parameters:
  26885. * (Object) error - The websocket error.
  26886. */
  26887. _onError: function(error) {
  26888. Strophe.error("Websocket error " + error);
  26889. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "The WebSocket connection could not be established was disconnected.");
  26890. this._disconnect();
  26891. },
  26892. /** PrivateFunction: _onIdle
  26893. * _Private_ function called by Strophe.Connection._onIdle
  26894. *
  26895. * sends all queued stanzas
  26896. */
  26897. _onIdle: function () {
  26898. var data = this._conn._data;
  26899. if (data.length > 0 && !this._conn.paused) {
  26900. for (var i = 0; i < data.length; i++) {
  26901. if (data[i] !== null) {
  26902. var stanza, rawStanza;
  26903. if (data[i] === "restart") {
  26904. stanza = this._buildStream();
  26905. rawStanza = this._removeClosingTag(stanza);
  26906. stanza = stanza.tree();
  26907. } else {
  26908. stanza = data[i];
  26909. rawStanza = Strophe.serialize(stanza);
  26910. }
  26911. this._conn.xmlOutput(stanza);
  26912. this._conn.rawOutput(rawStanza);
  26913. this.socket.send(rawStanza);
  26914. }
  26915. }
  26916. this._conn._data = [];
  26917. }
  26918. },
  26919. /** PrivateFunction: _onMessage
  26920. * _Private_ function to handle websockets messages.
  26921. *
  26922. * This function parses each of the messages as if they are full documents. [TODO : We may actually want to use a SAX Push parser].
  26923. *
  26924. * Since all XMPP traffic starts with "<stream:stream version='1.0' xml:lang='en' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='3697395463' from='SERVER'>"
  26925. * The first stanza will always fail to be parsed...
  26926. * Addtionnaly, the seconds stanza will always be a <stream:features> with the stream NS defined in the previous stanza... so we need to 'force' the inclusion of the NS in this stanza!
  26927. *
  26928. * Parameters:
  26929. * (string) message - The websocket message.
  26930. */
  26931. _onMessage: function(message) {
  26932. var elem, data;
  26933. // check for closing stream
  26934. if (message.data === "</stream:stream>") {
  26935. var close = "</stream:stream>";
  26936. this._conn.rawInput(close);
  26937. this._conn.xmlInput(document.createElement("stream:stream"));
  26938. if (!this._conn.disconnecting) {
  26939. this._conn._doDisconnect();
  26940. }
  26941. return;
  26942. } else if (message.data.search("<stream:stream ") === 0) {
  26943. //Make the initial stream:stream selfclosing to parse it without a SAX parser.
  26944. data = message.data.replace(/<stream:stream (.*[^\/])>/, "<stream:stream $1/>");
  26945. elem = new DOMParser().parseFromString(data, "text/xml").documentElement;
  26946. if (!this._handleStreamStart(elem)) {
  26947. return;
  26948. }
  26949. } else {
  26950. data = this._streamWrap(message.data);
  26951. elem = new DOMParser().parseFromString(data, "text/xml").documentElement;
  26952. }
  26953. if (this._check_streamerror(elem, Strophe.Status.ERROR)) {
  26954. return;
  26955. }
  26956. //handle unavailable presence stanza before disconnecting
  26957. if (this._conn.disconnecting &&
  26958. elem.firstChild.nodeName === "presence" &&
  26959. elem.firstChild.getAttribute("type") === "unavailable") {
  26960. this._conn.xmlInput(elem);
  26961. this._conn.rawInput(Strophe.serialize(elem));
  26962. // if we are already disconnecting we will ignore the unavailable stanza and
  26963. // wait for the </stream:stream> tag before we close the connection
  26964. return;
  26965. }
  26966. this._conn._dataRecv(elem, message.data);
  26967. },
  26968. /** PrivateFunction: _onOpen
  26969. * _Private_ function to handle websockets connection setup.
  26970. *
  26971. * The opening stream tag is sent here.
  26972. */
  26973. _onOpen: function() {
  26974. Strophe.info("Websocket open");
  26975. var start = this._buildStream();
  26976. this._conn.xmlOutput(start.tree());
  26977. var startString = this._removeClosingTag(start);
  26978. this._conn.rawOutput(startString);
  26979. this.socket.send(startString);
  26980. },
  26981. /** PrivateFunction: _removeClosingTag
  26982. * _Private_ function to Make the first <stream:stream> non-selfclosing
  26983. *
  26984. * Parameters:
  26985. * (Object) elem - The <stream:stream> tag.
  26986. *
  26987. * Returns:
  26988. * The stream:stream tag as String
  26989. */
  26990. _removeClosingTag: function(elem) {
  26991. var string = Strophe.serialize(elem);
  26992. string = string.replace(/<(stream:stream .*[^\/])\/>$/, "<$1>");
  26993. return string;
  26994. },
  26995. /** PrivateFunction: _reqToData
  26996. * _Private_ function to get a stanza out of a request.
  26997. *
  26998. * WebSockets don't use requests, so the passed argument is just returned.
  26999. *
  27000. * Parameters:
  27001. * (Object) stanza - The stanza.
  27002. *
  27003. * Returns:
  27004. * The stanza that was passed.
  27005. */
  27006. _reqToData: function (stanza)
  27007. {
  27008. return stanza;
  27009. },
  27010. /** PrivateFunction: _send
  27011. * _Private_ part of the Connection.send function for WebSocket
  27012. *
  27013. * Just flushes the messages that are in the queue
  27014. */
  27015. _send: function () {
  27016. this._conn.flush();
  27017. },
  27018. /** PrivateFunction: _sendRestart
  27019. *
  27020. * Send an xmpp:restart stanza.
  27021. */
  27022. _sendRestart: function ()
  27023. {
  27024. clearTimeout(this._conn._idleTimeout);
  27025. this._conn._onIdle.bind(this._conn)();
  27026. }
  27027. };
  27028. define("strophe", (function (global) {
  27029. return function () {
  27030. var ret, fn;
  27031. return ret || global.Strophe;
  27032. };
  27033. }(this)));
  27034. // Generated by CoffeeScript 1.8.0
  27035. /*
  27036. *Plugin to implement the MUC extension.
  27037. http://xmpp.org/extensions/xep-0045.html
  27038. *Previous Author:
  27039. Nathan Zorn <nathan.zorn@gmail.com>
  27040. *Complete CoffeeScript rewrite:
  27041. Andreas Guth <guth@dbis.rwth-aachen.de>
  27042. */
  27043. (function() {
  27044. var Occupant, RoomConfig, XmppRoom,
  27045. __hasProp = {}.hasOwnProperty,
  27046. __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
  27047. Strophe.addConnectionPlugin('muc', {
  27048. _connection: null,
  27049. rooms: {},
  27050. roomNames: [],
  27051. /*Function
  27052. Initialize the MUC plugin. Sets the correct connection object and
  27053. extends the namesace.
  27054. */
  27055. init: function(conn) {
  27056. this._connection = conn;
  27057. this._muc_handler = null;
  27058. Strophe.addNamespace('MUC_OWNER', Strophe.NS.MUC + "#owner");
  27059. Strophe.addNamespace('MUC_ADMIN', Strophe.NS.MUC + "#admin");
  27060. Strophe.addNamespace('MUC_USER', Strophe.NS.MUC + "#user");
  27061. Strophe.addNamespace('MUC_ROOMCONF', Strophe.NS.MUC + "#roomconfig");
  27062. return Strophe.addNamespace('MUC_REGISTER', "jabber:iq:register");
  27063. },
  27064. /*Function
  27065. Join a multi-user chat room
  27066. Parameters:
  27067. (String) room - The multi-user chat room to join.
  27068. (String) nick - The nickname to use in the chat room. Optional
  27069. (Function) msg_handler_cb - The function call to handle messages from the
  27070. specified chat room.
  27071. (Function) pres_handler_cb - The function call back to handle presence
  27072. in the chat room.
  27073. (Function) roster_cb - The function call to handle roster info in the chat room
  27074. (String) password - The optional password to use. (password protected
  27075. rooms only)
  27076. (Object) history_attrs - Optional attributes for retrieving history
  27077. (XML DOM Element) extended_presence - Optional XML for extending presence
  27078. */
  27079. join: function(room, nick, msg_handler_cb, pres_handler_cb, roster_cb, password, history_attrs) {
  27080. var msg, room_nick;
  27081. room_nick = this.test_append_nick(room, nick);
  27082. msg = $pres({
  27083. from: this._connection.jid,
  27084. to: room_nick
  27085. }).c("x", {
  27086. xmlns: Strophe.NS.MUC
  27087. });
  27088. if (history_attrs != null) {
  27089. msg = msg.c("history", history_attrs).up();
  27090. }
  27091. if (password != null) {
  27092. msg.cnode(Strophe.xmlElement("password", [], password));
  27093. }
  27094. if (typeof extended_presence !== "undefined" && extended_presence !== null) {
  27095. msg.up.cnode(extended_presence);
  27096. }
  27097. if (this._muc_handler == null) {
  27098. this._muc_handler = this._connection.addHandler((function(_this) {
  27099. return function(stanza) {
  27100. var from, handler, handlers, id, roomname, x, xmlns, xquery, _i, _len;
  27101. from = stanza.getAttribute('from');
  27102. if (!from) {
  27103. return true;
  27104. }
  27105. roomname = from.split("/")[0];
  27106. if (!_this.rooms[roomname]) {
  27107. return true;
  27108. }
  27109. room = _this.rooms[roomname];
  27110. handlers = {};
  27111. if (stanza.nodeName === "message") {
  27112. handlers = room._message_handlers;
  27113. } else if (stanza.nodeName === "presence") {
  27114. xquery = stanza.getElementsByTagName("x");
  27115. if (xquery.length > 0) {
  27116. for (_i = 0, _len = xquery.length; _i < _len; _i++) {
  27117. x = xquery[_i];
  27118. xmlns = x.getAttribute("xmlns");
  27119. if (xmlns && xmlns.match(Strophe.NS.MUC)) {
  27120. handlers = room._presence_handlers;
  27121. break;
  27122. }
  27123. }
  27124. }
  27125. }
  27126. for (id in handlers) {
  27127. handler = handlers[id];
  27128. if (!handler(stanza, room)) {
  27129. delete handlers[id];
  27130. }
  27131. }
  27132. return true;
  27133. };
  27134. })(this));
  27135. }
  27136. if (!this.rooms.hasOwnProperty(room)) {
  27137. this.rooms[room] = new XmppRoom(this, room, nick, password);
  27138. this.roomNames.push(room);
  27139. }
  27140. if (pres_handler_cb) {
  27141. this.rooms[room].addHandler('presence', pres_handler_cb);
  27142. }
  27143. if (msg_handler_cb) {
  27144. this.rooms[room].addHandler('message', msg_handler_cb);
  27145. }
  27146. if (roster_cb) {
  27147. this.rooms[room].addHandler('roster', roster_cb);
  27148. }
  27149. return this._connection.send(msg);
  27150. },
  27151. /*Function
  27152. Leave a multi-user chat room
  27153. Parameters:
  27154. (String) room - The multi-user chat room to leave.
  27155. (String) nick - The nick name used in the room.
  27156. (Function) handler_cb - Optional function to handle the successful leave.
  27157. (String) exit_msg - optional exit message.
  27158. Returns:
  27159. iqid - The unique id for the room leave.
  27160. */
  27161. leave: function(room, nick, handler_cb, exit_msg) {
  27162. var id, presence, presenceid, room_nick;
  27163. id = this.roomNames.indexOf(room);
  27164. delete this.rooms[room];
  27165. if (id >= 0) {
  27166. this.roomNames.splice(id, 1);
  27167. if (this.roomNames.length === 0) {
  27168. this._connection.deleteHandler(this._muc_handler);
  27169. this._muc_handler = null;
  27170. }
  27171. }
  27172. room_nick = this.test_append_nick(room, nick);
  27173. presenceid = this._connection.getUniqueId();
  27174. presence = $pres({
  27175. type: "unavailable",
  27176. id: presenceid,
  27177. from: this._connection.jid,
  27178. to: room_nick
  27179. });
  27180. if (exit_msg != null) {
  27181. presence.c("status", exit_msg);
  27182. }
  27183. if (handler_cb != null) {
  27184. this._connection.addHandler(handler_cb, null, "presence", null, presenceid);
  27185. }
  27186. this._connection.send(presence);
  27187. return presenceid;
  27188. },
  27189. /*Function
  27190. Parameters:
  27191. (String) room - The multi-user chat room name.
  27192. (String) nick - The nick name used in the chat room.
  27193. (String) message - The plaintext message to send to the room.
  27194. (String) html_message - The message to send to the room with html markup.
  27195. (String) type - "groupchat" for group chat messages o
  27196. "chat" for private chat messages
  27197. Returns:
  27198. msgiq - the unique id used to send the message
  27199. */
  27200. message: function(room, nick, message, html_message, type, msgid) {
  27201. var msg, parent, room_nick;
  27202. room_nick = this.test_append_nick(room, nick);
  27203. type = type || (nick != null ? "chat" : "groupchat");
  27204. msgid = msgid || this._connection.getUniqueId();
  27205. msg = $msg({
  27206. to: room_nick,
  27207. from: this._connection.jid,
  27208. type: type,
  27209. id: msgid
  27210. }).c("body", {
  27211. xmlns: Strophe.NS.CLIENT
  27212. }).t(message);
  27213. msg.up();
  27214. if (html_message != null) {
  27215. msg.c("html", {
  27216. xmlns: Strophe.NS.XHTML_IM
  27217. }).c("body", {
  27218. xmlns: Strophe.NS.XHTML
  27219. }).h(html_message);
  27220. if (msg.node.childNodes.length === 0) {
  27221. parent = msg.node.parentNode;
  27222. msg.up().up();
  27223. msg.node.removeChild(parent);
  27224. } else {
  27225. msg.up().up();
  27226. }
  27227. }
  27228. msg.c("x", {
  27229. xmlns: "jabber:x:event"
  27230. }).c("composing");
  27231. this._connection.send(msg);
  27232. return msgid;
  27233. },
  27234. /*Function
  27235. Convenience Function to send a Message to all Occupants
  27236. Parameters:
  27237. (String) room - The multi-user chat room name.
  27238. (String) message - The plaintext message to send to the room.
  27239. (String) html_message - The message to send to the room with html markup.
  27240. (String) msgid - Optional unique ID which will be set as the 'id' attribute of the stanza
  27241. Returns:
  27242. msgiq - the unique id used to send the message
  27243. */
  27244. groupchat: function(room, message, html_message, msgid) {
  27245. return this.message(room, null, message, html_message, void 0, msgid);
  27246. },
  27247. /*Function
  27248. Send a mediated invitation.
  27249. Parameters:
  27250. (String) room - The multi-user chat room name.
  27251. (String) receiver - The invitation's receiver.
  27252. (String) reason - Optional reason for joining the room.
  27253. Returns:
  27254. msgiq - the unique id used to send the invitation
  27255. */
  27256. invite: function(room, receiver, reason) {
  27257. var invitation, msgid;
  27258. msgid = this._connection.getUniqueId();
  27259. invitation = $msg({
  27260. from: this._connection.jid,
  27261. to: room,
  27262. id: msgid
  27263. }).c('x', {
  27264. xmlns: Strophe.NS.MUC_USER
  27265. }).c('invite', {
  27266. to: receiver
  27267. });
  27268. if (reason != null) {
  27269. invitation.c('reason', reason);
  27270. }
  27271. this._connection.send(invitation);
  27272. return msgid;
  27273. },
  27274. /*Function
  27275. Send a mediated multiple invitation.
  27276. Parameters:
  27277. (String) room - The multi-user chat room name.
  27278. (Array) receivers - The invitation's receivers.
  27279. (String) reason - Optional reason for joining the room.
  27280. Returns:
  27281. msgiq - the unique id used to send the invitation
  27282. */
  27283. multipleInvites: function(room, receivers, reason) {
  27284. var invitation, msgid, receiver, _i, _len;
  27285. msgid = this._connection.getUniqueId();
  27286. invitation = $msg({
  27287. from: this._connection.jid,
  27288. to: room,
  27289. id: msgid
  27290. }).c('x', {
  27291. xmlns: Strophe.NS.MUC_USER
  27292. });
  27293. for (_i = 0, _len = receivers.length; _i < _len; _i++) {
  27294. receiver = receivers[_i];
  27295. invitation.c('invite', {
  27296. to: receiver
  27297. });
  27298. if (reason != null) {
  27299. invitation.c('reason', reason);
  27300. invitation.up();
  27301. }
  27302. invitation.up();
  27303. }
  27304. this._connection.send(invitation);
  27305. return msgid;
  27306. },
  27307. /*Function
  27308. Send a direct invitation.
  27309. Parameters:
  27310. (String) room - The multi-user chat room name.
  27311. (String) receiver - The invitation's receiver.
  27312. (String) reason - Optional reason for joining the room.
  27313. (String) password - Optional password for the room.
  27314. Returns:
  27315. msgiq - the unique id used to send the invitation
  27316. */
  27317. directInvite: function(room, receiver, reason, password) {
  27318. var attrs, invitation, msgid;
  27319. msgid = this._connection.getUniqueId();
  27320. attrs = {
  27321. xmlns: 'jabber:x:conference',
  27322. jid: room
  27323. };
  27324. if (reason != null) {
  27325. attrs.reason = reason;
  27326. }
  27327. if (password != null) {
  27328. attrs.password = password;
  27329. }
  27330. invitation = $msg({
  27331. from: this._connection.jid,
  27332. to: receiver,
  27333. id: msgid
  27334. }).c('x', attrs);
  27335. this._connection.send(invitation);
  27336. return msgid;
  27337. },
  27338. /*Function
  27339. Queries a room for a list of occupants
  27340. (String) room - The multi-user chat room name.
  27341. (Function) success_cb - Optional function to handle the info.
  27342. (Function) error_cb - Optional function to handle an error.
  27343. Returns:
  27344. id - the unique id used to send the info request
  27345. */
  27346. queryOccupants: function(room, success_cb, error_cb) {
  27347. var attrs, info;
  27348. attrs = {
  27349. xmlns: Strophe.NS.DISCO_ITEMS
  27350. };
  27351. info = $iq({
  27352. from: this._connection.jid,
  27353. to: room,
  27354. type: 'get'
  27355. }).c('query', attrs);
  27356. return this._connection.sendIQ(info, success_cb, error_cb);
  27357. },
  27358. /*Function
  27359. Start a room configuration.
  27360. Parameters:
  27361. (String) room - The multi-user chat room name.
  27362. (Function) handler_cb - Optional function to handle the config form.
  27363. Returns:
  27364. id - the unique id used to send the configuration request
  27365. */
  27366. configure: function(room, handler_cb, error_cb) {
  27367. var config, stanza;
  27368. config = $iq({
  27369. to: room,
  27370. type: "get"
  27371. }).c("query", {
  27372. xmlns: Strophe.NS.MUC_OWNER
  27373. });
  27374. stanza = config.tree();
  27375. return this._connection.sendIQ(stanza, handler_cb, error_cb);
  27376. },
  27377. /*Function
  27378. Cancel the room configuration
  27379. Parameters:
  27380. (String) room - The multi-user chat room name.
  27381. Returns:
  27382. id - the unique id used to cancel the configuration.
  27383. */
  27384. cancelConfigure: function(room) {
  27385. var config, stanza;
  27386. config = $iq({
  27387. to: room,
  27388. type: "set"
  27389. }).c("query", {
  27390. xmlns: Strophe.NS.MUC_OWNER
  27391. }).c("x", {
  27392. xmlns: "jabber:x:data",
  27393. type: "cancel"
  27394. });
  27395. stanza = config.tree();
  27396. return this._connection.sendIQ(stanza);
  27397. },
  27398. /*Function
  27399. Save a room configuration.
  27400. Parameters:
  27401. (String) room - The multi-user chat room name.
  27402. (Array) config- Form Object or an array of form elements used to configure the room.
  27403. Returns:
  27404. id - the unique id used to save the configuration.
  27405. */
  27406. saveConfiguration: function(room, config, success_cb, error_cb) {
  27407. var conf, iq, stanza, _i, _len;
  27408. iq = $iq({
  27409. to: room,
  27410. type: "set"
  27411. }).c("query", {
  27412. xmlns: Strophe.NS.MUC_OWNER
  27413. });
  27414. if (typeof Form !== "undefined" && config instanceof Form) {
  27415. config.type = "submit";
  27416. iq.cnode(config.toXML());
  27417. } else {
  27418. iq.c("x", {
  27419. xmlns: "jabber:x:data",
  27420. type: "submit"
  27421. });
  27422. for (_i = 0, _len = config.length; _i < _len; _i++) {
  27423. conf = config[_i];
  27424. iq.cnode(conf).up();
  27425. }
  27426. }
  27427. stanza = iq.tree();
  27428. return this._connection.sendIQ(stanza, success_cb, error_cb);
  27429. },
  27430. /*Function
  27431. Parameters:
  27432. (String) room - The multi-user chat room name.
  27433. Returns:
  27434. id - the unique id used to create the chat room.
  27435. */
  27436. createInstantRoom: function(room, success_cb, error_cb) {
  27437. var roomiq;
  27438. roomiq = $iq({
  27439. to: room,
  27440. type: "set"
  27441. }).c("query", {
  27442. xmlns: Strophe.NS.MUC_OWNER
  27443. }).c("x", {
  27444. xmlns: "jabber:x:data",
  27445. type: "submit"
  27446. });
  27447. return this._connection.sendIQ(roomiq.tree(), success_cb, error_cb);
  27448. },
  27449. /*Function
  27450. Parameters:
  27451. (String) room - The multi-user chat room name.
  27452. (Object) config - the configuration. ex: {"muc#roomconfig_publicroom": "0", "muc#roomconfig_persistentroom": "1"}
  27453. Returns:
  27454. id - the unique id used to create the chat room.
  27455. */
  27456. createConfiguredRoom: function(room, config, success_cb, error_cb) {
  27457. var k, roomiq, v;
  27458. roomiq = $iq({
  27459. to: room,
  27460. type: "set"
  27461. }).c("query", {
  27462. xmlns: Strophe.NS.MUC_OWNER
  27463. }).c("x", {
  27464. xmlns: "jabber:x:data",
  27465. type: "submit"
  27466. });
  27467. roomiq.c('field', {
  27468. 'var': 'FORM_TYPE'
  27469. }).c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up();
  27470. for (k in config) {
  27471. if (!__hasProp.call(config, k)) continue;
  27472. v = config[k];
  27473. roomiq.c('field', {
  27474. 'var': k
  27475. }).c('value').t(v).up().up();
  27476. }
  27477. return this._connection.sendIQ(roomiq.tree(), success_cb, error_cb);
  27478. },
  27479. /*Function
  27480. Set the topic of the chat room.
  27481. Parameters:
  27482. (String) room - The multi-user chat room name.
  27483. (String) topic - Topic message.
  27484. */
  27485. setTopic: function(room, topic) {
  27486. var msg;
  27487. msg = $msg({
  27488. to: room,
  27489. from: this._connection.jid,
  27490. type: "groupchat"
  27491. }).c("subject", {
  27492. xmlns: "jabber:client"
  27493. }).t(topic);
  27494. return this._connection.send(msg.tree());
  27495. },
  27496. /*Function
  27497. Internal Function that Changes the role or affiliation of a member
  27498. of a MUC room. This function is used by modifyRole and modifyAffiliation.
  27499. The modification can only be done by a room moderator. An error will be
  27500. returned if the user doesn't have permission.
  27501. Parameters:
  27502. (String) room - The multi-user chat room name.
  27503. (Object) item - Object with nick and role or jid and affiliation attribute
  27504. (String) reason - Optional reason for the change.
  27505. (Function) handler_cb - Optional callback for success
  27506. (Function) error_cb - Optional callback for error
  27507. Returns:
  27508. iq - the id of the mode change request.
  27509. */
  27510. _modifyPrivilege: function(room, item, reason, handler_cb, error_cb) {
  27511. var iq;
  27512. iq = $iq({
  27513. to: room,
  27514. type: "set"
  27515. }).c("query", {
  27516. xmlns: Strophe.NS.MUC_ADMIN
  27517. }).cnode(item.node);
  27518. if (reason != null) {
  27519. iq.c("reason", reason);
  27520. }
  27521. return this._connection.sendIQ(iq.tree(), handler_cb, error_cb);
  27522. },
  27523. /*Function
  27524. Changes the role of a member of a MUC room.
  27525. The modification can only be done by a room moderator. An error will be
  27526. returned if the user doesn't have permission.
  27527. Parameters:
  27528. (String) room - The multi-user chat room name.
  27529. (String) nick - The nick name of the user to modify.
  27530. (String) role - The new role of the user.
  27531. (String) affiliation - The new affiliation of the user.
  27532. (String) reason - Optional reason for the change.
  27533. (Function) handler_cb - Optional callback for success
  27534. (Function) error_cb - Optional callback for error
  27535. Returns:
  27536. iq - the id of the mode change request.
  27537. */
  27538. modifyRole: function(room, nick, role, reason, handler_cb, error_cb) {
  27539. var item;
  27540. item = $build("item", {
  27541. nick: nick,
  27542. role: role
  27543. });
  27544. return this._modifyPrivilege(room, item, reason, handler_cb, error_cb);
  27545. },
  27546. kick: function(room, nick, reason, handler_cb, error_cb) {
  27547. return this.modifyRole(room, nick, 'none', reason, handler_cb, error_cb);
  27548. },
  27549. voice: function(room, nick, reason, handler_cb, error_cb) {
  27550. return this.modifyRole(room, nick, 'participant', reason, handler_cb, error_cb);
  27551. },
  27552. mute: function(room, nick, reason, handler_cb, error_cb) {
  27553. return this.modifyRole(room, nick, 'visitor', reason, handler_cb, error_cb);
  27554. },
  27555. op: function(room, nick, reason, handler_cb, error_cb) {
  27556. return this.modifyRole(room, nick, 'moderator', reason, handler_cb, error_cb);
  27557. },
  27558. deop: function(room, nick, reason, handler_cb, error_cb) {
  27559. return this.modifyRole(room, nick, 'participant', reason, handler_cb, error_cb);
  27560. },
  27561. /*Function
  27562. Changes the affiliation of a member of a MUC room.
  27563. The modification can only be done by a room moderator. An error will be
  27564. returned if the user doesn't have permission.
  27565. Parameters:
  27566. (String) room - The multi-user chat room name.
  27567. (String) jid - The jid of the user to modify.
  27568. (String) affiliation - The new affiliation of the user.
  27569. (String) reason - Optional reason for the change.
  27570. (Function) handler_cb - Optional callback for success
  27571. (Function) error_cb - Optional callback for error
  27572. Returns:
  27573. iq - the id of the mode change request.
  27574. */
  27575. modifyAffiliation: function(room, jid, affiliation, reason, handler_cb, error_cb) {
  27576. var item;
  27577. item = $build("item", {
  27578. jid: jid,
  27579. affiliation: affiliation
  27580. });
  27581. return this._modifyPrivilege(room, item, reason, handler_cb, error_cb);
  27582. },
  27583. ban: function(room, jid, reason, handler_cb, error_cb) {
  27584. return this.modifyAffiliation(room, jid, 'outcast', reason, handler_cb, error_cb);
  27585. },
  27586. member: function(room, jid, reason, handler_cb, error_cb) {
  27587. return this.modifyAffiliation(room, jid, 'member', reason, handler_cb, error_cb);
  27588. },
  27589. revoke: function(room, jid, reason, handler_cb, error_cb) {
  27590. return this.modifyAffiliation(room, jid, 'none', reason, handler_cb, error_cb);
  27591. },
  27592. owner: function(room, jid, reason, handler_cb, error_cb) {
  27593. return this.modifyAffiliation(room, jid, 'owner', reason, handler_cb, error_cb);
  27594. },
  27595. admin: function(room, jid, reason, handler_cb, error_cb) {
  27596. return this.modifyAffiliation(room, jid, 'admin', reason, handler_cb, error_cb);
  27597. },
  27598. /*Function
  27599. Change the current users nick name.
  27600. Parameters:
  27601. (String) room - The multi-user chat room name.
  27602. (String) user - The new nick name.
  27603. */
  27604. changeNick: function(room, user) {
  27605. var presence, room_nick;
  27606. room_nick = this.test_append_nick(room, user);
  27607. presence = $pres({
  27608. from: this._connection.jid,
  27609. to: room_nick,
  27610. id: this._connection.getUniqueId()
  27611. });
  27612. return this._connection.send(presence.tree());
  27613. },
  27614. /*Function
  27615. Change the current users status.
  27616. Parameters:
  27617. (String) room - The multi-user chat room name.
  27618. (String) user - The current nick.
  27619. (String) show - The new show-text.
  27620. (String) status - The new status-text.
  27621. */
  27622. setStatus: function(room, user, show, status) {
  27623. var presence, room_nick;
  27624. room_nick = this.test_append_nick(room, user);
  27625. presence = $pres({
  27626. from: this._connection.jid,
  27627. to: room_nick
  27628. });
  27629. if (show != null) {
  27630. presence.c('show', show).up();
  27631. }
  27632. if (status != null) {
  27633. presence.c('status', status);
  27634. }
  27635. return this._connection.send(presence.tree());
  27636. },
  27637. /*Function
  27638. Registering with a room.
  27639. @see http://xmpp.org/extensions/xep-0045.html#register
  27640. Parameters:
  27641. (String) room - The multi-user chat room name.
  27642. (Function) handle_cb - Function to call for room list return.
  27643. (Function) error_cb - Function to call on error.
  27644. */
  27645. registrationRequest: function(room, handle_cb, error_cb) {
  27646. var iq;
  27647. iq = $iq({
  27648. to: room,
  27649. from: this._connection.jid,
  27650. type: "get"
  27651. }).c("query", {
  27652. xmlns: Strophe.NS.MUC_REGISTER
  27653. });
  27654. return this._connection.sendIQ(iq, function(stanza) {
  27655. var $field, $fields, field, fields, length, _i, _len;
  27656. $fields = stanza.getElementsByTagName('field');
  27657. length = $fields.length;
  27658. fields = {
  27659. required: [],
  27660. optional: []
  27661. };
  27662. for (_i = 0, _len = $fields.length; _i < _len; _i++) {
  27663. $field = $fields[_i];
  27664. field = {
  27665. "var": $field.getAttribute('var'),
  27666. label: $field.getAttribute('label'),
  27667. type: $field.getAttribute('type')
  27668. };
  27669. if ($field.getElementsByTagName('required').length > 0) {
  27670. fields.required.push(field);
  27671. } else {
  27672. fields.optional.push(field);
  27673. }
  27674. }
  27675. return handle_cb(fields);
  27676. }, error_cb);
  27677. },
  27678. /*Function
  27679. Submits registration form.
  27680. Parameters:
  27681. (String) room - The multi-user chat room name.
  27682. (Function) handle_cb - Function to call for room list return.
  27683. (Function) error_cb - Function to call on error.
  27684. */
  27685. submitRegistrationForm: function(room, fields, handle_cb, error_cb) {
  27686. var iq, key, val;
  27687. iq = $iq({
  27688. to: room,
  27689. type: "set"
  27690. }).c("query", {
  27691. xmlns: Strophe.NS.MUC_REGISTER
  27692. });
  27693. iq.c("x", {
  27694. xmlns: "jabber:x:data",
  27695. type: "submit"
  27696. });
  27697. iq.c('field', {
  27698. 'var': 'FORM_TYPE'
  27699. }).c('value').t('http://jabber.org/protocol/muc#register').up().up();
  27700. for (key in fields) {
  27701. val = fields[key];
  27702. iq.c('field', {
  27703. 'var': key
  27704. }).c('value').t(val).up().up();
  27705. }
  27706. return this._connection.sendIQ(iq, handle_cb, error_cb);
  27707. },
  27708. /*Function
  27709. List all chat room available on a server.
  27710. Parameters:
  27711. (String) server - name of chat server.
  27712. (String) handle_cb - Function to call for room list return.
  27713. (String) error_cb - Function to call on error.
  27714. */
  27715. listRooms: function(server, handle_cb, error_cb) {
  27716. var iq;
  27717. iq = $iq({
  27718. to: server,
  27719. from: this._connection.jid,
  27720. type: "get"
  27721. }).c("query", {
  27722. xmlns: Strophe.NS.DISCO_ITEMS
  27723. });
  27724. return this._connection.sendIQ(iq, handle_cb, error_cb);
  27725. },
  27726. test_append_nick: function(room, nick) {
  27727. var domain, node;
  27728. node = Strophe.escapeNode(Strophe.getNodeFromJid(room));
  27729. domain = Strophe.getDomainFromJid(room);
  27730. return node + "@" + domain + (nick != null ? "/" + nick : "");
  27731. }
  27732. });
  27733. XmppRoom = (function() {
  27734. function XmppRoom(client, name, nick, password) {
  27735. this.client = client;
  27736. this.name = name;
  27737. this.nick = nick;
  27738. this.password = password;
  27739. this._roomRosterHandler = __bind(this._roomRosterHandler, this);
  27740. this._addOccupant = __bind(this._addOccupant, this);
  27741. this.roster = {};
  27742. this._message_handlers = {};
  27743. this._presence_handlers = {};
  27744. this._roster_handlers = {};
  27745. this._handler_ids = 0;
  27746. if (client.muc) {
  27747. this.client = client.muc;
  27748. }
  27749. this.name = Strophe.getBareJidFromJid(name);
  27750. this.addHandler('presence', this._roomRosterHandler);
  27751. }
  27752. XmppRoom.prototype.join = function(msg_handler_cb, pres_handler_cb, roster_cb) {
  27753. return this.client.join(this.name, this.nick, msg_handler_cb, pres_handler_cb, roster_cb, this.password);
  27754. };
  27755. XmppRoom.prototype.leave = function(handler_cb, message) {
  27756. this.client.leave(this.name, this.nick, handler_cb, message);
  27757. return delete this.client.rooms[this.name];
  27758. };
  27759. XmppRoom.prototype.message = function(nick, message, html_message, type) {
  27760. return this.client.message(this.name, nick, message, html_message, type);
  27761. };
  27762. XmppRoom.prototype.groupchat = function(message, html_message) {
  27763. return this.client.groupchat(this.name, message, html_message);
  27764. };
  27765. XmppRoom.prototype.invite = function(receiver, reason) {
  27766. return this.client.invite(this.name, receiver, reason);
  27767. };
  27768. XmppRoom.prototype.multipleInvites = function(receivers, reason) {
  27769. return this.client.invite(this.name, receivers, reason);
  27770. };
  27771. XmppRoom.prototype.directInvite = function(receiver, reason) {
  27772. return this.client.directInvite(this.name, receiver, reason, this.password);
  27773. };
  27774. XmppRoom.prototype.configure = function(handler_cb) {
  27775. return this.client.configure(this.name, handler_cb);
  27776. };
  27777. XmppRoom.prototype.cancelConfigure = function() {
  27778. return this.client.cancelConfigure(this.name);
  27779. };
  27780. XmppRoom.prototype.saveConfiguration = function(config) {
  27781. return this.client.saveConfiguration(this.name, config);
  27782. };
  27783. XmppRoom.prototype.queryOccupants = function(success_cb, error_cb) {
  27784. return this.client.queryOccupants(this.name, success_cb, error_cb);
  27785. };
  27786. XmppRoom.prototype.setTopic = function(topic) {
  27787. return this.client.setTopic(this.name, topic);
  27788. };
  27789. XmppRoom.prototype.modifyRole = function(nick, role, reason, success_cb, error_cb) {
  27790. return this.client.modifyRole(this.name, nick, role, reason, success_cb, error_cb);
  27791. };
  27792. XmppRoom.prototype.kick = function(nick, reason, handler_cb, error_cb) {
  27793. return this.client.kick(this.name, nick, reason, handler_cb, error_cb);
  27794. };
  27795. XmppRoom.prototype.voice = function(nick, reason, handler_cb, error_cb) {
  27796. return this.client.voice(this.name, nick, reason, handler_cb, error_cb);
  27797. };
  27798. XmppRoom.prototype.mute = function(nick, reason, handler_cb, error_cb) {
  27799. return this.client.mute(this.name, nick, reason, handler_cb, error_cb);
  27800. };
  27801. XmppRoom.prototype.op = function(nick, reason, handler_cb, error_cb) {
  27802. return this.client.op(this.name, nick, reason, handler_cb, error_cb);
  27803. };
  27804. XmppRoom.prototype.deop = function(nick, reason, handler_cb, error_cb) {
  27805. return this.client.deop(this.name, nick, reason, handler_cb, error_cb);
  27806. };
  27807. XmppRoom.prototype.modifyAffiliation = function(jid, affiliation, reason, success_cb, error_cb) {
  27808. return this.client.modifyAffiliation(this.name, jid, affiliation, reason, success_cb, error_cb);
  27809. };
  27810. XmppRoom.prototype.ban = function(jid, reason, handler_cb, error_cb) {
  27811. return this.client.ban(this.name, jid, reason, handler_cb, error_cb);
  27812. };
  27813. XmppRoom.prototype.member = function(jid, reason, handler_cb, error_cb) {
  27814. return this.client.member(this.name, jid, reason, handler_cb, error_cb);
  27815. };
  27816. XmppRoom.prototype.revoke = function(jid, reason, handler_cb, error_cb) {
  27817. return this.client.revoke(this.name, jid, reason, handler_cb, error_cb);
  27818. };
  27819. XmppRoom.prototype.owner = function(jid, reason, handler_cb, error_cb) {
  27820. return this.client.owner(this.name, jid, reason, handler_cb, error_cb);
  27821. };
  27822. XmppRoom.prototype.admin = function(jid, reason, handler_cb, error_cb) {
  27823. return this.client.admin(this.name, jid, reason, handler_cb, error_cb);
  27824. };
  27825. XmppRoom.prototype.changeNick = function(nick) {
  27826. this.nick = nick;
  27827. return this.client.changeNick(this.name, nick);
  27828. };
  27829. XmppRoom.prototype.setStatus = function(show, status) {
  27830. return this.client.setStatus(this.name, this.nick, show, status);
  27831. };
  27832. /*Function
  27833. Adds a handler to the MUC room.
  27834. Parameters:
  27835. (String) handler_type - 'message', 'presence' or 'roster'.
  27836. (Function) handler - The handler function.
  27837. Returns:
  27838. id - the id of handler.
  27839. */
  27840. XmppRoom.prototype.addHandler = function(handler_type, handler) {
  27841. var id;
  27842. id = this._handler_ids++;
  27843. switch (handler_type) {
  27844. case 'presence':
  27845. this._presence_handlers[id] = handler;
  27846. break;
  27847. case 'message':
  27848. this._message_handlers[id] = handler;
  27849. break;
  27850. case 'roster':
  27851. this._roster_handlers[id] = handler;
  27852. break;
  27853. default:
  27854. this._handler_ids--;
  27855. return null;
  27856. }
  27857. return id;
  27858. };
  27859. /*Function
  27860. Removes a handler from the MUC room.
  27861. This function takes ONLY ids returned by the addHandler function
  27862. of this room. passing handler ids returned by connection.addHandler
  27863. may brake things!
  27864. Parameters:
  27865. (number) id - the id of the handler
  27866. */
  27867. XmppRoom.prototype.removeHandler = function(id) {
  27868. delete this._presence_handlers[id];
  27869. delete this._message_handlers[id];
  27870. return delete this._roster_handlers[id];
  27871. };
  27872. /*Function
  27873. Creates and adds an Occupant to the Room Roster.
  27874. Parameters:
  27875. (Object) data - the data the Occupant is filled with
  27876. Returns:
  27877. occ - the created Occupant.
  27878. */
  27879. XmppRoom.prototype._addOccupant = function(data) {
  27880. var occ;
  27881. occ = new Occupant(data, this);
  27882. this.roster[occ.nick] = occ;
  27883. return occ;
  27884. };
  27885. /*Function
  27886. The standard handler that managed the Room Roster.
  27887. Parameters:
  27888. (Object) pres - the presence stanza containing user information
  27889. */
  27890. XmppRoom.prototype._roomRosterHandler = function(pres) {
  27891. var data, handler, id, newnick, nick, _ref;
  27892. data = XmppRoom._parsePresence(pres);
  27893. nick = data.nick;
  27894. newnick = data.newnick || null;
  27895. switch (data.type) {
  27896. case 'error':
  27897. return true;
  27898. case 'unavailable':
  27899. if (newnick) {
  27900. data.nick = newnick;
  27901. if (this.roster[nick] && this.roster[newnick]) {
  27902. this.roster[nick].update(this.roster[newnick]);
  27903. this.roster[newnick] = this.roster[nick];
  27904. }
  27905. if (this.roster[nick] && !this.roster[newnick]) {
  27906. this.roster[newnick] = this.roster[nick].update(data);
  27907. }
  27908. }
  27909. delete this.roster[nick];
  27910. break;
  27911. default:
  27912. if (this.roster[nick]) {
  27913. this.roster[nick].update(data);
  27914. } else {
  27915. this._addOccupant(data);
  27916. }
  27917. }
  27918. _ref = this._roster_handlers;
  27919. for (id in _ref) {
  27920. handler = _ref[id];
  27921. if (!handler(this.roster, this)) {
  27922. delete this._roster_handlers[id];
  27923. }
  27924. }
  27925. return true;
  27926. };
  27927. /*Function
  27928. Parses a presence stanza
  27929. Parameters:
  27930. (Object) data - the data extracted from the presence stanza
  27931. */
  27932. XmppRoom._parsePresence = function(pres) {
  27933. var c, c2, data, _i, _j, _len, _len1, _ref, _ref1;
  27934. data = {};
  27935. data.nick = Strophe.getResourceFromJid(pres.getAttribute("from"));
  27936. data.type = pres.getAttribute("type");
  27937. data.states = [];
  27938. _ref = pres.childNodes;
  27939. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  27940. c = _ref[_i];
  27941. switch (c.nodeName) {
  27942. case "status":
  27943. data.status = c.textContent || null;
  27944. break;
  27945. case "show":
  27946. data.show = c.textContent || null;
  27947. break;
  27948. case "x":
  27949. if (c.getAttribute("xmlns") === Strophe.NS.MUC_USER) {
  27950. _ref1 = c.childNodes;
  27951. for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
  27952. c2 = _ref1[_j];
  27953. switch (c2.nodeName) {
  27954. case "item":
  27955. data.affiliation = c2.getAttribute("affiliation");
  27956. data.role = c2.getAttribute("role");
  27957. data.jid = c2.getAttribute("jid");
  27958. data.newnick = c2.getAttribute("nick");
  27959. break;
  27960. case "status":
  27961. if (c2.getAttribute("code")) {
  27962. data.states.push(c2.getAttribute("code"));
  27963. }
  27964. }
  27965. }
  27966. }
  27967. }
  27968. }
  27969. return data;
  27970. };
  27971. return XmppRoom;
  27972. })();
  27973. RoomConfig = (function() {
  27974. function RoomConfig(info) {
  27975. this.parse = __bind(this.parse, this);
  27976. if (info != null) {
  27977. this.parse(info);
  27978. }
  27979. }
  27980. RoomConfig.prototype.parse = function(result) {
  27981. var attr, attrs, child, field, identity, query, _i, _j, _k, _len, _len1, _len2, _ref;
  27982. query = result.getElementsByTagName("query")[0].childNodes;
  27983. this.identities = [];
  27984. this.features = [];
  27985. this.x = [];
  27986. for (_i = 0, _len = query.length; _i < _len; _i++) {
  27987. child = query[_i];
  27988. attrs = child.attributes;
  27989. switch (child.nodeName) {
  27990. case "identity":
  27991. identity = {};
  27992. for (_j = 0, _len1 = attrs.length; _j < _len1; _j++) {
  27993. attr = attrs[_j];
  27994. identity[attr.name] = attr.textContent;
  27995. }
  27996. this.identities.push(identity);
  27997. break;
  27998. case "feature":
  27999. this.features.push(child.getAttribute("var"));
  28000. break;
  28001. case "x":
  28002. if ((!child.childNodes[0].getAttribute("var") === 'FORM_TYPE') || (!child.childNodes[0].getAttribute("type") === 'hidden')) {
  28003. break;
  28004. }
  28005. _ref = child.childNodes;
  28006. for (_k = 0, _len2 = _ref.length; _k < _len2; _k++) {
  28007. field = _ref[_k];
  28008. if (!field.attributes.type) {
  28009. this.x.push({
  28010. "var": field.getAttribute("var"),
  28011. label: field.getAttribute("label") || "",
  28012. value: field.firstChild.textContent || ""
  28013. });
  28014. }
  28015. }
  28016. }
  28017. }
  28018. return {
  28019. "identities": this.identities,
  28020. "features": this.features,
  28021. "x": this.x
  28022. };
  28023. };
  28024. return RoomConfig;
  28025. })();
  28026. Occupant = (function() {
  28027. function Occupant(data, room) {
  28028. this.room = room;
  28029. this.update = __bind(this.update, this);
  28030. this.admin = __bind(this.admin, this);
  28031. this.owner = __bind(this.owner, this);
  28032. this.revoke = __bind(this.revoke, this);
  28033. this.member = __bind(this.member, this);
  28034. this.ban = __bind(this.ban, this);
  28035. this.modifyAffiliation = __bind(this.modifyAffiliation, this);
  28036. this.deop = __bind(this.deop, this);
  28037. this.op = __bind(this.op, this);
  28038. this.mute = __bind(this.mute, this);
  28039. this.voice = __bind(this.voice, this);
  28040. this.kick = __bind(this.kick, this);
  28041. this.modifyRole = __bind(this.modifyRole, this);
  28042. this.update(data);
  28043. }
  28044. Occupant.prototype.modifyRole = function(role, reason, success_cb, error_cb) {
  28045. return this.room.modifyRole(this.nick, role, reason, success_cb, error_cb);
  28046. };
  28047. Occupant.prototype.kick = function(reason, handler_cb, error_cb) {
  28048. return this.room.kick(this.nick, reason, handler_cb, error_cb);
  28049. };
  28050. Occupant.prototype.voice = function(reason, handler_cb, error_cb) {
  28051. return this.room.voice(this.nick, reason, handler_cb, error_cb);
  28052. };
  28053. Occupant.prototype.mute = function(reason, handler_cb, error_cb) {
  28054. return this.room.mute(this.nick, reason, handler_cb, error_cb);
  28055. };
  28056. Occupant.prototype.op = function(reason, handler_cb, error_cb) {
  28057. return this.room.op(this.nick, reason, handler_cb, error_cb);
  28058. };
  28059. Occupant.prototype.deop = function(reason, handler_cb, error_cb) {
  28060. return this.room.deop(this.nick, reason, handler_cb, error_cb);
  28061. };
  28062. Occupant.prototype.modifyAffiliation = function(affiliation, reason, success_cb, error_cb) {
  28063. return this.room.modifyAffiliation(this.jid, affiliation, reason, success_cb, error_cb);
  28064. };
  28065. Occupant.prototype.ban = function(reason, handler_cb, error_cb) {
  28066. return this.room.ban(this.jid, reason, handler_cb, error_cb);
  28067. };
  28068. Occupant.prototype.member = function(reason, handler_cb, error_cb) {
  28069. return this.room.member(this.jid, reason, handler_cb, error_cb);
  28070. };
  28071. Occupant.prototype.revoke = function(reason, handler_cb, error_cb) {
  28072. return this.room.revoke(this.jid, reason, handler_cb, error_cb);
  28073. };
  28074. Occupant.prototype.owner = function(reason, handler_cb, error_cb) {
  28075. return this.room.owner(this.jid, reason, handler_cb, error_cb);
  28076. };
  28077. Occupant.prototype.admin = function(reason, handler_cb, error_cb) {
  28078. return this.room.admin(this.jid, reason, handler_cb, error_cb);
  28079. };
  28080. Occupant.prototype.update = function(data) {
  28081. this.nick = data.nick || null;
  28082. this.affiliation = data.affiliation || null;
  28083. this.role = data.role || null;
  28084. this.jid = data.jid || null;
  28085. this.status = data.status || null;
  28086. this.show = data.show || null;
  28087. return this;
  28088. };
  28089. return Occupant;
  28090. })();
  28091. }).call(this);
  28092. define("strophe.muc", ["strophe"], function(){});
  28093. /*
  28094. Copyright 2010, François de Metz <francois@2metz.fr>
  28095. */
  28096. /**
  28097. * Roster Plugin
  28098. * Allow easily roster management
  28099. *
  28100. * Features
  28101. * * Get roster from server
  28102. * * handle presence
  28103. * * handle roster iq
  28104. * * subscribe/unsubscribe
  28105. * * authorize/unauthorize
  28106. * * roster versioning (xep 237)
  28107. */
  28108. Strophe.addConnectionPlugin('roster',
  28109. {
  28110. /** Function: init
  28111. * Plugin init
  28112. *
  28113. * Parameters:
  28114. * (Strophe.Connection) conn - Strophe connection
  28115. */
  28116. init: function(conn)
  28117. {
  28118. this._connection = conn;
  28119. this._callbacks = [];
  28120. /** Property: items
  28121. * Roster items
  28122. * [
  28123. * {
  28124. * name : "",
  28125. * jid : "",
  28126. * subscription : "",
  28127. * ask : "",
  28128. * groups : ["", ""],
  28129. * resources : {
  28130. * myresource : {
  28131. * show : "",
  28132. * status : "",
  28133. * priority : ""
  28134. * }
  28135. * }
  28136. * }
  28137. * ]
  28138. */
  28139. this.items = [];
  28140. /** Property: ver
  28141. * current roster revision
  28142. * always null if server doesn't support xep 237
  28143. */
  28144. this.ver = null;
  28145. // Override the connect and attach methods to always add presence and roster handlers.
  28146. // They are removed when the connection disconnects, so must be added on connection.
  28147. var oldCallback, roster = this, _connect = conn.connect, _attach = conn.attach;
  28148. var newCallback = function(status)
  28149. {
  28150. if (status == Strophe.Status.ATTACHED || status == Strophe.Status.CONNECTED)
  28151. {
  28152. try
  28153. {
  28154. // Presence subscription
  28155. conn.addHandler(roster._onReceivePresence.bind(roster), null, 'presence', null, null, null);
  28156. conn.addHandler(roster._onReceiveIQ.bind(roster), Strophe.NS.ROSTER, 'iq', "set", null, null);
  28157. }
  28158. catch (e)
  28159. {
  28160. Strophe.error(e);
  28161. }
  28162. }
  28163. if (typeof oldCallback === "function") {
  28164. oldCallback.apply(this, arguments);
  28165. }
  28166. };
  28167. conn.connect = function(jid, pass, callback, wait, hold, route)
  28168. {
  28169. oldCallback = callback;
  28170. if (typeof jid == "undefined")
  28171. jid = null;
  28172. if (typeof pass == "undefined")
  28173. pass = null;
  28174. callback = newCallback;
  28175. _connect.apply(conn, [jid, pass, callback, wait, hold, route]);
  28176. };
  28177. conn.attach = function(jid, sid, rid, callback, wait, hold, wind)
  28178. {
  28179. oldCallback = callback;
  28180. if (typeof jid == "undefined")
  28181. jid = null;
  28182. if (typeof sid == "undefined")
  28183. sid = null;
  28184. if (typeof rid == "undefined")
  28185. rid = null;
  28186. callback = newCallback;
  28187. _attach.apply(conn, [jid, sid, rid, callback, wait, hold, wind]);
  28188. };
  28189. Strophe.addNamespace('ROSTER_VER', 'urn:xmpp:features:rosterver');
  28190. Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick');
  28191. },
  28192. /** Function: supportVersioning
  28193. * return true if roster versioning is enabled on server
  28194. */
  28195. supportVersioning: function()
  28196. {
  28197. return (this._connection.features && this._connection.features.getElementsByTagName('ver').length > 0);
  28198. },
  28199. /** Function: get
  28200. * Get Roster on server
  28201. *
  28202. * Parameters:
  28203. * (Function) userCallback - callback on roster result
  28204. * (String) ver - current rev of roster
  28205. * (only used if roster versioning is enabled)
  28206. * (Array) items - initial items of ver
  28207. * (only used if roster versioning is enabled)
  28208. * In browser context you can use sessionStorage
  28209. * to store your roster in json (JSON.stringify())
  28210. */
  28211. get: function(userCallback, ver, items)
  28212. {
  28213. var attrs = {xmlns: Strophe.NS.ROSTER};
  28214. if (this.supportVersioning())
  28215. {
  28216. // empty rev because i want an rev attribute in the result
  28217. attrs.ver = ver || '';
  28218. this.items = items || [];
  28219. }
  28220. var iq = $iq({type: 'get', 'id' : this._connection.getUniqueId('roster')}).c('query', attrs);
  28221. return this._connection.sendIQ(iq,
  28222. this._onReceiveRosterSuccess.bind(this, userCallback),
  28223. this._onReceiveRosterError.bind(this, userCallback));
  28224. },
  28225. /** Function: registerCallback
  28226. * register callback on roster (presence and iq)
  28227. *
  28228. * Parameters:
  28229. * (Function) call_back
  28230. */
  28231. registerCallback: function(call_back)
  28232. {
  28233. this._callbacks.push(call_back);
  28234. },
  28235. /** Function: findItem
  28236. * Find item by JID
  28237. *
  28238. * Parameters:
  28239. * (String) jid
  28240. */
  28241. findItem : function(jid)
  28242. {
  28243. try {
  28244. for (var i = 0; i < this.items.length; i++)
  28245. {
  28246. if (this.items[i] && this.items[i].jid == jid)
  28247. {
  28248. return this.items[i];
  28249. }
  28250. }
  28251. } catch (e)
  28252. {
  28253. Strophe.error(e);
  28254. }
  28255. return false;
  28256. },
  28257. /** Function: removeItem
  28258. * Remove item by JID
  28259. *
  28260. * Parameters:
  28261. * (String) jid
  28262. */
  28263. removeItem : function(jid)
  28264. {
  28265. for (var i = 0; i < this.items.length; i++)
  28266. {
  28267. if (this.items[i] && this.items[i].jid == jid)
  28268. {
  28269. this.items.splice(i, 1);
  28270. return true;
  28271. }
  28272. }
  28273. return false;
  28274. },
  28275. /** Function: subscribe
  28276. * Subscribe presence
  28277. *
  28278. * Parameters:
  28279. * (String) jid
  28280. * (String) message (optional)
  28281. * (String) nick (optional)
  28282. */
  28283. subscribe: function(jid, message, nick) {
  28284. var pres = $pres({to: jid, type: "subscribe"});
  28285. if (message && message !== "") {
  28286. pres.c("status").t(message).up();
  28287. }
  28288. if (nick && nick !== "") {
  28289. pres.c('nick', {'xmlns': Strophe.NS.NICK}).t(nick).up();
  28290. }
  28291. this._connection.send(pres);
  28292. },
  28293. /** Function: unsubscribe
  28294. * Unsubscribe presence
  28295. *
  28296. * Parameters:
  28297. * (String) jid
  28298. * (String) message
  28299. */
  28300. unsubscribe: function(jid, message)
  28301. {
  28302. var pres = $pres({to: jid, type: "unsubscribe"});
  28303. if (message && message !== "")
  28304. pres.c("status").t(message);
  28305. this._connection.send(pres);
  28306. },
  28307. /** Function: authorize
  28308. * Authorize presence subscription
  28309. *
  28310. * Parameters:
  28311. * (String) jid
  28312. * (String) message
  28313. */
  28314. authorize: function(jid, message)
  28315. {
  28316. var pres = $pres({to: jid, type: "subscribed"});
  28317. if (message && message !== "")
  28318. pres.c("status").t(message);
  28319. this._connection.send(pres);
  28320. },
  28321. /** Function: unauthorize
  28322. * Unauthorize presence subscription
  28323. *
  28324. * Parameters:
  28325. * (String) jid
  28326. * (String) message
  28327. */
  28328. unauthorize: function(jid, message)
  28329. {
  28330. var pres = $pres({to: jid, type: "unsubscribed"});
  28331. if (message && message !== "")
  28332. pres.c("status").t(message);
  28333. this._connection.send(pres);
  28334. },
  28335. /** Function: add
  28336. * Add roster item
  28337. *
  28338. * Parameters:
  28339. * (String) jid - item jid
  28340. * (String) name - name
  28341. * (Array) groups
  28342. * (Function) call_back
  28343. */
  28344. add: function(jid, name, groups, call_back)
  28345. {
  28346. var iq = $iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item', {jid: jid,
  28347. name: name});
  28348. for (var i = 0; i < groups.length; i++)
  28349. {
  28350. iq.c('group').t(groups[i]).up();
  28351. }
  28352. this._connection.sendIQ(iq, call_back, call_back);
  28353. },
  28354. /** Function: update
  28355. * Update roster item
  28356. *
  28357. * Parameters:
  28358. * (String) jid - item jid
  28359. * (String) name - name
  28360. * (Array) groups
  28361. * (Function) call_back
  28362. */
  28363. update: function(jid, name, groups, call_back)
  28364. {
  28365. var item = this.findItem(jid);
  28366. if (!item)
  28367. {
  28368. throw "item not found";
  28369. }
  28370. var newName = name || item.name;
  28371. var newGroups = groups || item.groups;
  28372. var iq = $iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item', {jid: item.jid,
  28373. name: newName});
  28374. for (var i = 0; i < newGroups.length; i++)
  28375. {
  28376. iq.c('group').t(newGroups[i]).up();
  28377. }
  28378. return this._connection.sendIQ(iq, call_back, call_back);
  28379. },
  28380. /** Function: remove
  28381. * Remove roster item
  28382. *
  28383. * Parameters:
  28384. * (String) jid - item jid
  28385. * (Function) call_back
  28386. */
  28387. remove: function(jid, call_back)
  28388. {
  28389. var item = this.findItem(jid);
  28390. if (!item)
  28391. {
  28392. throw "item not found";
  28393. }
  28394. var iq = $iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item', {jid: item.jid,
  28395. subscription: "remove"});
  28396. this._connection.sendIQ(iq, call_back, call_back);
  28397. },
  28398. /** PrivateFunction: _onReceiveRosterSuccess
  28399. *
  28400. */
  28401. _onReceiveRosterSuccess: function(userCallback, stanza)
  28402. {
  28403. this._updateItems(stanza);
  28404. if (typeof userCallback === "function") {
  28405. userCallback(this.items);
  28406. }
  28407. },
  28408. /** PrivateFunction: _onReceiveRosterError
  28409. *
  28410. */
  28411. _onReceiveRosterError: function(userCallback, stanza)
  28412. {
  28413. userCallback(this.items);
  28414. },
  28415. /** PrivateFunction: _onReceivePresence
  28416. * Handle presence
  28417. */
  28418. _onReceivePresence : function(presence)
  28419. {
  28420. // TODO: from is optional
  28421. var jid = presence.getAttribute('from');
  28422. var from = Strophe.getBareJidFromJid(jid);
  28423. var item = this.findItem(from);
  28424. // not in roster
  28425. if (!item)
  28426. {
  28427. return true;
  28428. }
  28429. var type = presence.getAttribute('type');
  28430. if (type == 'unavailable')
  28431. {
  28432. delete item.resources[Strophe.getResourceFromJid(jid)];
  28433. }
  28434. else if (!type)
  28435. {
  28436. // TODO: add timestamp
  28437. item.resources[Strophe.getResourceFromJid(jid)] = {
  28438. show : (presence.getElementsByTagName('show').length !== 0) ? Strophe.getText(presence.getElementsByTagName('show')[0]) : "",
  28439. status : (presence.getElementsByTagName('status').length !== 0) ? Strophe.getText(presence.getElementsByTagName('status')[0]) : "",
  28440. priority : (presence.getElementsByTagName('priority').length !== 0) ? Strophe.getText(presence.getElementsByTagName('priority')[0]) : ""
  28441. };
  28442. }
  28443. else
  28444. {
  28445. // Stanza is not a presence notification. (It's probably a subscription type stanza.)
  28446. return true;
  28447. }
  28448. this._call_backs(this.items, item);
  28449. return true;
  28450. },
  28451. /** PrivateFunction: _call_backs
  28452. *
  28453. */
  28454. _call_backs : function(items, item)
  28455. {
  28456. for (var i = 0; i < this._callbacks.length; i++) // [].forEach my love ...
  28457. {
  28458. this._callbacks[i](items, item);
  28459. }
  28460. },
  28461. /** PrivateFunction: _onReceiveIQ
  28462. * Handle roster push.
  28463. */
  28464. _onReceiveIQ : function(iq)
  28465. {
  28466. var id = iq.getAttribute('id');
  28467. var from = iq.getAttribute('from');
  28468. // Receiving client MUST ignore stanza unless it has no from or from = user's JID.
  28469. if (from && from !== "" && from != this._connection.jid && from != Strophe.getBareJidFromJid(this._connection.jid))
  28470. return true;
  28471. var iqresult = $iq({type: 'result', id: id, from: this._connection.jid});
  28472. this._connection.send(iqresult);
  28473. this._updateItems(iq);
  28474. return true;
  28475. },
  28476. /** PrivateFunction: _updateItems
  28477. * Update items from iq
  28478. */
  28479. _updateItems : function(iq)
  28480. {
  28481. var query = iq.getElementsByTagName('query');
  28482. if (query.length !== 0)
  28483. {
  28484. this.ver = query.item(0).getAttribute('ver');
  28485. var self = this;
  28486. Strophe.forEachChild(query.item(0), 'item',
  28487. function (item)
  28488. {
  28489. self._updateItem(item);
  28490. }
  28491. );
  28492. }
  28493. this._call_backs(this.items);
  28494. },
  28495. /** PrivateFunction: _updateItem
  28496. * Update internal representation of roster item
  28497. */
  28498. _updateItem : function(item)
  28499. {
  28500. var jid = item.getAttribute("jid");
  28501. var name = item.getAttribute("name");
  28502. var subscription = item.getAttribute("subscription");
  28503. var ask = item.getAttribute("ask");
  28504. var groups = [];
  28505. Strophe.forEachChild(item, 'group',
  28506. function(group)
  28507. {
  28508. groups.push(Strophe.getText(group));
  28509. }
  28510. );
  28511. if (subscription == "remove")
  28512. {
  28513. this.removeItem(jid);
  28514. return;
  28515. }
  28516. item = this.findItem(jid);
  28517. if (!item)
  28518. {
  28519. this.items.push({
  28520. name : name,
  28521. jid : jid,
  28522. subscription : subscription,
  28523. ask : ask,
  28524. groups : groups,
  28525. resources : {}
  28526. });
  28527. }
  28528. else
  28529. {
  28530. item.name = name;
  28531. item.subscription = subscription;
  28532. item.ask = ask;
  28533. item.groups = groups;
  28534. }
  28535. }
  28536. });
  28537. define("strophe.roster", ["strophe"], function(){});
  28538. // Generated by CoffeeScript 1.3.3
  28539. /*
  28540. Plugin to implement the vCard extension.
  28541. http://xmpp.org/extensions/xep-0054.html
  28542. Author: Nathan Zorn (nathan.zorn@gmail.com)
  28543. CoffeeScript port: Andreas Guth (guth@dbis.rwth-aachen.de)
  28544. */
  28545. /* jslint configuration:
  28546. */
  28547. /* global document, window, setTimeout, clearTimeout, console,
  28548. XMLHttpRequest, ActiveXObject,
  28549. Base64, MD5,
  28550. Strophe, $build, $msg, $iq, $pres
  28551. */
  28552. var buildIq;
  28553. buildIq = function(type, jid, vCardEl) {
  28554. var iq;
  28555. iq = $iq(jid ? {
  28556. type: type,
  28557. to: jid
  28558. } : {
  28559. type: type
  28560. });
  28561. iq.c("vCard", {
  28562. xmlns: Strophe.NS.VCARD
  28563. });
  28564. if (vCardEl) {
  28565. iq.cnode(vCardEl);
  28566. }
  28567. return iq;
  28568. };
  28569. Strophe.addConnectionPlugin('vcard', {
  28570. _connection: null,
  28571. init: function(conn) {
  28572. this._connection = conn;
  28573. return Strophe.addNamespace('VCARD', 'vcard-temp');
  28574. },
  28575. /*Function
  28576. Retrieve a vCard for a JID/Entity
  28577. Parameters:
  28578. (Function) handler_cb - The callback function used to handle the request.
  28579. (String) jid - optional - The name of the entity to request the vCard
  28580. If no jid is given, this function retrieves the current user's vcard.
  28581. */
  28582. get: function(handler_cb, jid, error_cb) {
  28583. var iq;
  28584. iq = buildIq("get", jid);
  28585. return this._connection.sendIQ(iq, handler_cb, error_cb);
  28586. },
  28587. /* Function
  28588. Set an entity's vCard.
  28589. */
  28590. set: function(handler_cb, vCardEl, jid, error_cb) {
  28591. var iq;
  28592. iq = buildIq("set", jid, vCardEl);
  28593. return this._connection.sendIQ(iq, handler_cb, error_rb);
  28594. }
  28595. });
  28596. define("strophe.vcard", ["strophe"], function(){});
  28597. /*
  28598. Copyright 2010, François de Metz <francois@2metz.fr>
  28599. */
  28600. /**
  28601. * Disco Strophe Plugin
  28602. * Implement http://xmpp.org/extensions/xep-0030.html
  28603. * TODO: manage node hierarchies, and node on info request
  28604. */
  28605. Strophe.addConnectionPlugin('disco',
  28606. {
  28607. _connection: null,
  28608. _identities : [],
  28609. _features : [],
  28610. _items : [],
  28611. /** Function: init
  28612. * Plugin init
  28613. *
  28614. * Parameters:
  28615. * (Strophe.Connection) conn - Strophe connection
  28616. */
  28617. init: function(conn)
  28618. {
  28619. this._connection = conn;
  28620. this._identities = [];
  28621. this._features = [];
  28622. this._items = [];
  28623. // disco info
  28624. conn.addHandler(this._onDiscoInfo.bind(this), Strophe.NS.DISCO_INFO, 'iq', 'get', null, null);
  28625. // disco items
  28626. conn.addHandler(this._onDiscoItems.bind(this), Strophe.NS.DISCO_ITEMS, 'iq', 'get', null, null);
  28627. },
  28628. /** Function: addIdentity
  28629. * See http://xmpp.org/registrar/disco-categories.html
  28630. * Parameters:
  28631. * (String) category - category of identity (like client, automation, etc ...)
  28632. * (String) type - type of identity (like pc, web, bot , etc ...)
  28633. * (String) name - name of identity in natural language
  28634. * (String) lang - lang of name parameter
  28635. *
  28636. * Returns:
  28637. * Boolean
  28638. */
  28639. addIdentity: function(category, type, name, lang)
  28640. {
  28641. for (var i=0; i<this._identities.length; i++)
  28642. {
  28643. if (this._identities[i].category == category &&
  28644. this._identities[i].type == type &&
  28645. this._identities[i].name == name &&
  28646. this._identities[i].lang == lang)
  28647. {
  28648. return false;
  28649. }
  28650. }
  28651. this._identities.push({category: category, type: type, name: name, lang: lang});
  28652. return true;
  28653. },
  28654. /** Function: addFeature
  28655. *
  28656. * Parameters:
  28657. * (String) var_name - feature name (like jabber:iq:version)
  28658. *
  28659. * Returns:
  28660. * boolean
  28661. */
  28662. addFeature: function(var_name)
  28663. {
  28664. for (var i=0; i<this._features.length; i++)
  28665. {
  28666. if (this._features[i] == var_name)
  28667. return false;
  28668. }
  28669. this._features.push(var_name);
  28670. return true;
  28671. },
  28672. /** Function: removeFeature
  28673. *
  28674. * Parameters:
  28675. * (String) var_name - feature name (like jabber:iq:version)
  28676. *
  28677. * Returns:
  28678. * boolean
  28679. */
  28680. removeFeature: function(var_name)
  28681. {
  28682. for (var i=0; i<this._features.length; i++)
  28683. {
  28684. if (this._features[i] === var_name){
  28685. this._features.splice(i,1)
  28686. return true;
  28687. }
  28688. }
  28689. return false;
  28690. },
  28691. /** Function: addItem
  28692. *
  28693. * Parameters:
  28694. * (String) jid
  28695. * (String) name
  28696. * (String) node
  28697. * (Function) call_back
  28698. *
  28699. * Returns:
  28700. * boolean
  28701. */
  28702. addItem: function(jid, name, node, call_back)
  28703. {
  28704. if (node && !call_back)
  28705. return false;
  28706. this._items.push({jid: jid, name: name, node: node, call_back: call_back});
  28707. return true;
  28708. },
  28709. /** Function: info
  28710. * Info query
  28711. *
  28712. * Parameters:
  28713. * (Function) call_back
  28714. * (String) jid
  28715. * (String) node
  28716. */
  28717. info: function(jid, node, success, error, timeout)
  28718. {
  28719. var attrs = {xmlns: Strophe.NS.DISCO_INFO};
  28720. if (node)
  28721. attrs.node = node;
  28722. var info = $iq({from:this._connection.jid,
  28723. to:jid, type:'get'}).c('query', attrs);
  28724. this._connection.sendIQ(info, success, error, timeout);
  28725. },
  28726. /** Function: items
  28727. * Items query
  28728. *
  28729. * Parameters:
  28730. * (Function) call_back
  28731. * (String) jid
  28732. * (String) node
  28733. */
  28734. items: function(jid, node, success, error, timeout)
  28735. {
  28736. var attrs = {xmlns: Strophe.NS.DISCO_ITEMS};
  28737. if (node)
  28738. attrs.node = node;
  28739. var items = $iq({from:this._connection.jid,
  28740. to:jid, type:'get'}).c('query', attrs);
  28741. this._connection.sendIQ(items, success, error, timeout);
  28742. },
  28743. /** PrivateFunction: _buildIQResult
  28744. */
  28745. _buildIQResult: function(stanza, query_attrs)
  28746. {
  28747. var id = stanza.getAttribute('id');
  28748. var from = stanza.getAttribute('from');
  28749. var iqresult = $iq({type: 'result', id: id});
  28750. if (from !== null) {
  28751. iqresult.attrs({to: from});
  28752. }
  28753. return iqresult.c('query', query_attrs);
  28754. },
  28755. /** PrivateFunction: _onDiscoInfo
  28756. * Called when receive info request
  28757. */
  28758. _onDiscoInfo: function(stanza)
  28759. {
  28760. var node = stanza.getElementsByTagName('query')[0].getAttribute('node');
  28761. var attrs = {xmlns: Strophe.NS.DISCO_INFO};
  28762. if (node)
  28763. {
  28764. attrs.node = node;
  28765. }
  28766. var iqresult = this._buildIQResult(stanza, attrs);
  28767. for (var i=0; i<this._identities.length; i++)
  28768. {
  28769. var attrs = {category: this._identities[i].category,
  28770. type : this._identities[i].type};
  28771. if (this._identities[i].name)
  28772. attrs.name = this._identities[i].name;
  28773. if (this._identities[i].lang)
  28774. attrs['xml:lang'] = this._identities[i].lang;
  28775. iqresult.c('identity', attrs).up();
  28776. }
  28777. for (var i=0; i<this._features.length; i++)
  28778. {
  28779. iqresult.c('feature', {'var':this._features[i]}).up();
  28780. }
  28781. this._connection.send(iqresult.tree());
  28782. return true;
  28783. },
  28784. /** PrivateFunction: _onDiscoItems
  28785. * Called when receive items request
  28786. */
  28787. _onDiscoItems: function(stanza)
  28788. {
  28789. var query_attrs = {xmlns: Strophe.NS.DISCO_ITEMS};
  28790. var node = stanza.getElementsByTagName('query')[0].getAttribute('node');
  28791. if (node)
  28792. {
  28793. query_attrs.node = node;
  28794. var items = [];
  28795. for (var i = 0; i < this._items.length; i++)
  28796. {
  28797. if (this._items[i].node == node)
  28798. {
  28799. items = this._items[i].call_back(stanza);
  28800. break;
  28801. }
  28802. }
  28803. }
  28804. else
  28805. {
  28806. var items = this._items;
  28807. }
  28808. var iqresult = this._buildIQResult(stanza, query_attrs);
  28809. for (var i = 0; i < items.length; i++)
  28810. {
  28811. var attrs = {jid: items[i].jid};
  28812. if (items[i].name)
  28813. attrs.name = items[i].name;
  28814. if (items[i].node)
  28815. attrs.node = items[i].node;
  28816. iqresult.c('item', attrs).up();
  28817. }
  28818. this._connection.send(iqresult.tree());
  28819. return true;
  28820. }
  28821. });
  28822. define("strophe.disco", ["strophe"], function(){});
  28823. define("converse-dependencies", [
  28824. "jquery",
  28825. "utils",
  28826. "otr",
  28827. "moment",
  28828. "locales",
  28829. "backbone.browserStorage",
  28830. "backbone.overview",
  28831. "jquery.browser",
  28832. "typeahead",
  28833. "strophe",
  28834. "strophe.muc",
  28835. "strophe.roster",
  28836. "strophe.vcard",
  28837. "strophe.disco"
  28838. ], function($, utils, otr, moment) {
  28839. return {
  28840. 'jQuery': $,
  28841. 'moment': moment,
  28842. 'otr': otr,
  28843. 'utils': utils
  28844. };
  28845. });
  28846. /*!
  28847. * Converse.js (Web-based XMPP instant messaging client)
  28848. * http://conversejs.org
  28849. *
  28850. * Copyright (c) 2012, Jan-Carel Brand <jc@opkode.com>
  28851. * Licensed under the Mozilla Public License (MPL)
  28852. */
  28853. // AMD/global registrations
  28854. (function (root, factory) {
  28855. if (typeof define === 'function' && define.amd) {
  28856. define("converse",
  28857. ["converse-dependencies", "converse-templates"],
  28858. function (dependencies, templates) {
  28859. var otr = dependencies.otr;
  28860. if (typeof otr !== "undefined") {
  28861. return factory(
  28862. dependencies.jQuery,
  28863. _,
  28864. otr.OTR,
  28865. otr.DSA,
  28866. templates,
  28867. dependencies.moment,
  28868. dependencies.utils
  28869. );
  28870. } else {
  28871. return factory(
  28872. dependencies.jQuery,
  28873. _,
  28874. undefined,
  28875. undefined,
  28876. templates,
  28877. dependencies.moment,
  28878. dependencies.utils
  28879. );
  28880. }
  28881. }
  28882. );
  28883. } else {
  28884. root.converse = factory(jQuery, _, OTR, DSA, JST, moment, utils);
  28885. }
  28886. }(this, function ($, _, OTR, DSA, templates, moment, utils) {
  28887. //
  28888. // Cannot use this due to Safari bug.
  28889. // See https://github.com/jcbrand/converse.js/issues/196
  28890. if (typeof console === "undefined" || typeof console.log === "undefined") {
  28891. console = { log: function () {}, error: function () {} };
  28892. }
  28893. // Configuration of underscore templates (this config is distict to the
  28894. // config of requirejs-tpl in main.js). This one is for normal inline
  28895. // templates.
  28896. // Use Mustache style syntax for variable interpolation
  28897. _.templateSettings = {
  28898. evaluate : /\{\[([\s\S]+?)\]\}/g,
  28899. interpolate : /\{\{([\s\S]+?)\}\}/g
  28900. };
  28901. var contains = function (attr, query) {
  28902. return function (item) {
  28903. if (typeof attr === 'object') {
  28904. var value = false;
  28905. _.each(attr, function (a) {
  28906. value = value || item.get(a).toLowerCase().indexOf(query.toLowerCase()) !== -1;
  28907. });
  28908. return value;
  28909. } else if (typeof attr === 'string') {
  28910. return item.get(attr).toLowerCase().indexOf(query.toLowerCase()) !== -1;
  28911. } else {
  28912. throw new Error('Wrong attribute type. Must be string or array.');
  28913. }
  28914. };
  28915. };
  28916. contains.not = function (attr, query) {
  28917. return function (item) {
  28918. return !(contains(attr, query)(item));
  28919. };
  28920. };
  28921. // XXX: these can perhaps be moved to src/polyfills.js
  28922. String.prototype.splitOnce = function (delimiter) {
  28923. var components = this.split(delimiter);
  28924. return [components.shift(), components.join(delimiter)];
  28925. };
  28926. $.fn.addEmoticons = function () {
  28927. if (converse.visible_toolbar_buttons.emoticons) {
  28928. if (this.length > 0) {
  28929. this.each(function (i, obj) {
  28930. var text = $(obj).html();
  28931. text = text.replace(/&gt;:\)/g, '<span class="emoticon icon-evil"></span>');
  28932. text = text.replace(/:\)/g, '<span class="emoticon icon-smiley"></span>');
  28933. text = text.replace(/:\-\)/g, '<span class="emoticon icon-smiley"></span>');
  28934. text = text.replace(/;\)/g, '<span class="emoticon icon-wink"></span>');
  28935. text = text.replace(/;\-\)/g, '<span class="emoticon icon-wink"></span>');
  28936. text = text.replace(/:D/g, '<span class="emoticon icon-grin"></span>');
  28937. text = text.replace(/:\-D/g, '<span class="emoticon icon-grin"></span>');
  28938. text = text.replace(/:P/g, '<span class="emoticon icon-tongue"></span>');
  28939. text = text.replace(/:\-P/g, '<span class="emoticon icon-tongue"></span>');
  28940. text = text.replace(/:p/g, '<span class="emoticon icon-tongue"></span>');
  28941. text = text.replace(/:\-p/g, '<span class="emoticon icon-tongue"></span>');
  28942. text = text.replace(/8\)/g, '<span class="emoticon icon-cool"></span>');
  28943. text = text.replace(/:S/g, '<span class="emoticon icon-confused"></span>');
  28944. text = text.replace(/:\\/g, '<span class="emoticon icon-wondering"></span>');
  28945. text = text.replace(/:\/ /g, '<span class="emoticon icon-wondering"></span>');
  28946. text = text.replace(/&gt;:\(/g, '<span class="emoticon icon-angry"></span>');
  28947. text = text.replace(/:\(/g, '<span class="emoticon icon-sad"></span>');
  28948. text = text.replace(/:\-\(/g, '<span class="emoticon icon-sad"></span>');
  28949. text = text.replace(/:O/g, '<span class="emoticon icon-shocked"></span>');
  28950. text = text.replace(/:\-O/g, '<span class="emoticon icon-shocked"></span>');
  28951. text = text.replace(/\=\-O/g, '<span class="emoticon icon-shocked"></span>');
  28952. text = text.replace(/\(\^.\^\)b/g, '<span class="emoticon icon-thumbs-up"></span>');
  28953. text = text.replace(/&lt;3/g, '<span class="emoticon icon-heart"></span>');
  28954. $(obj).html(text);
  28955. });
  28956. }
  28957. }
  28958. return this;
  28959. };
  28960. var playNotification = function () {
  28961. var audio;
  28962. if (converse.play_sounds && typeof Audio !== "undefined"){
  28963. audio = new Audio("sounds/msg_received.ogg");
  28964. if (audio.canPlayType('/audio/ogg')) {
  28965. audio.play();
  28966. } else {
  28967. audio = new Audio("/sounds/msg_received.mp3");
  28968. audio.play();
  28969. }
  28970. }
  28971. };
  28972. var converse = {
  28973. plugins: {},
  28974. templates: templates,
  28975. emit: function (evt, data) {
  28976. $(this).trigger(evt, data);
  28977. },
  28978. once: function (evt, handler) {
  28979. $(this).one(evt, handler);
  28980. },
  28981. on: function (evt, handler) {
  28982. $(this).bind(evt, handler);
  28983. },
  28984. off: function (evt, handler) {
  28985. $(this).unbind(evt, handler);
  28986. },
  28987. refreshWebkit: function () {
  28988. /* This works around a webkit bug. Refresh the browser's viewport,
  28989. * otherwise chatboxes are not moved along when one is closed.
  28990. */
  28991. if ($.browser.webkit) {
  28992. var conversejs = document.getElementById('conversejs');
  28993. conversejs.style.display = 'none';
  28994. conversejs.offsetHeight = conversejs.offsetHeight;
  28995. conversejs.style.display = 'block';
  28996. }
  28997. }
  28998. };
  28999. converse.initialize = function (settings, callback) {
  29000. var converse = this;
  29001. // Logging
  29002. Strophe.log = function (level, msg) { console.log(level+' '+msg); };
  29003. Strophe.error = function (msg) {
  29004. console.log('ERROR: '+msg);
  29005. };
  29006. // Add Strophe Namespaces
  29007. Strophe.addNamespace('REGISTER', 'jabber:iq:register');
  29008. Strophe.addNamespace('XFORM', 'jabber:x:data');
  29009. // Add Strophe Statuses
  29010. var i = 0;
  29011. Object.keys(Strophe.Status).forEach(function (key) {
  29012. i = Math.max(i, Strophe.Status[key]);
  29013. });
  29014. Strophe.Status.REGIFAIL = i + 1;
  29015. Strophe.Status.REGISTERED = i + 2;
  29016. Strophe.Status.CONFLICT = i + 3;
  29017. Strophe.Status.NOTACCEPTABLE = i + 5;
  29018. // Constants
  29019. // ---------
  29020. var UNENCRYPTED = 0;
  29021. var UNVERIFIED= 1;
  29022. var VERIFIED= 2;
  29023. var FINISHED = 3;
  29024. var KEY = {
  29025. ENTER: 13
  29026. };
  29027. var STATUS_WEIGHTS = {
  29028. 'offline': 6,
  29029. 'unavailable': 5,
  29030. 'xa': 4,
  29031. 'away': 3,
  29032. 'dnd': 2,
  29033. 'online': 1
  29034. };
  29035. var INACTIVE = 'inactive';
  29036. var ACTIVE = 'active';
  29037. var COMPOSING = 'composing';
  29038. var PAUSED = 'paused';
  29039. var GONE = 'gone';
  29040. var HAS_CSPRNG = ((typeof crypto !== 'undefined') &&
  29041. ((typeof crypto.randomBytes === 'function') ||
  29042. (typeof crypto.getRandomValues === 'function')
  29043. ));
  29044. var HAS_CRYPTO = HAS_CSPRNG && (
  29045. (typeof CryptoJS !== "undefined") &&
  29046. (typeof OTR !== "undefined") &&
  29047. (typeof DSA !== "undefined")
  29048. );
  29049. var OPENED = 'opened';
  29050. var CLOSED = 'closed';
  29051. // Default configuration values
  29052. // ----------------------------
  29053. var default_settings = {
  29054. allow_contact_requests: true,
  29055. allow_dragresize: true,
  29056. allow_logout: true,
  29057. allow_muc: true,
  29058. allow_otr: true,
  29059. allow_registration: true,
  29060. animate: true,
  29061. auto_list_rooms: false,
  29062. auto_reconnect: false,
  29063. auto_subscribe: false,
  29064. bosh_service_url: undefined, // The BOSH connection manager URL.
  29065. cache_otr_key: false,
  29066. debug: false,
  29067. domain_placeholder: " e.g. conversejs.org", // Placeholder text shown in the domain input on the registration form
  29068. default_box_height: 400, // The default height, in pixels, for the control box, chat boxes and chatrooms.
  29069. expose_rid_and_sid: false,
  29070. forward_messages: false,
  29071. hide_muc_server: false,
  29072. hide_offline_users: false,
  29073. i18n: locales.en,
  29074. jid: undefined,
  29075. keepalive: false,
  29076. message_carbons: false,
  29077. no_trimming: false, // Set to true for phantomjs tests (where browser apparently has no width)
  29078. play_sounds: false,
  29079. prebind: false,
  29080. providers_link: 'https://xmpp.net/directory.php', // Link to XMPP providers shown on registration page
  29081. rid: undefined,
  29082. roster_groups: false,
  29083. show_controlbox_by_default: false,
  29084. show_only_online_users: false,
  29085. show_toolbar: true,
  29086. sid: undefined,
  29087. storage: 'session',
  29088. use_otr_by_default: false,
  29089. use_vcards: true,
  29090. visible_toolbar_buttons: {
  29091. 'emoticons': true,
  29092. 'call': false,
  29093. 'clear': true,
  29094. 'toggle_participants': true
  29095. },
  29096. xhr_custom_status: false,
  29097. xhr_custom_status_url: '',
  29098. xhr_user_search: false,
  29099. xhr_user_search_url: ''
  29100. };
  29101. _.extend(this, default_settings);
  29102. // Allow only whitelisted configuration attributes to be overwritten
  29103. _.extend(this, _.pick(settings, Object.keys(default_settings)));
  29104. if (settings.visible_toolbar_buttons) {
  29105. _.extend(
  29106. this.visible_toolbar_buttons,
  29107. _.pick(settings.visible_toolbar_buttons, [
  29108. 'emoticons', 'call', 'clear', 'toggle_participants'
  29109. ]
  29110. ));
  29111. }
  29112. $.fx.off = !this.animate;
  29113. // Only allow OTR if we have the capability
  29114. this.allow_otr = this.allow_otr && HAS_CRYPTO;
  29115. // Only use OTR by default if allow OTR is enabled to begin with
  29116. this.use_otr_by_default = this.use_otr_by_default && this.allow_otr;
  29117. // Translation machinery
  29118. // ---------------------
  29119. var __ = $.proxy(utils.__, this);
  29120. var ___ = utils.___;
  29121. // Translation aware constants
  29122. // ---------------------------
  29123. var OTR_CLASS_MAPPING = {};
  29124. OTR_CLASS_MAPPING[UNENCRYPTED] = 'unencrypted';
  29125. OTR_CLASS_MAPPING[UNVERIFIED] = 'unverified';
  29126. OTR_CLASS_MAPPING[VERIFIED] = 'verified';
  29127. OTR_CLASS_MAPPING[FINISHED] = 'finished';
  29128. var OTR_TRANSLATED_MAPPING = {};
  29129. OTR_TRANSLATED_MAPPING[UNENCRYPTED] = __('unencrypted');
  29130. OTR_TRANSLATED_MAPPING[UNVERIFIED] = __('unverified');
  29131. OTR_TRANSLATED_MAPPING[VERIFIED] = __('verified');
  29132. OTR_TRANSLATED_MAPPING[FINISHED] = __('finished');
  29133. var STATUSES = {
  29134. 'dnd': __('This contact is busy'),
  29135. 'online': __('This contact is online'),
  29136. 'offline': __('This contact is offline'),
  29137. 'unavailable': __('This contact is unavailable'),
  29138. 'xa': __('This contact is away for an extended period'),
  29139. 'away': __('This contact is away')
  29140. };
  29141. var DESC_GROUP_TOGGLE = __('Click to hide these contacts');
  29142. var HEADER_CURRENT_CONTACTS = __('My contacts');
  29143. var HEADER_PENDING_CONTACTS = __('Pending contacts');
  29144. var HEADER_REQUESTING_CONTACTS = __('Contact requests');
  29145. var HEADER_UNGROUPED = __('Ungrouped');
  29146. var LABEL_CONTACTS = __('Contacts');
  29147. var LABEL_GROUPS = __('Groups');
  29148. var HEADER_WEIGHTS = {};
  29149. HEADER_WEIGHTS[HEADER_CURRENT_CONTACTS] = 0;
  29150. HEADER_WEIGHTS[HEADER_UNGROUPED] = 1;
  29151. HEADER_WEIGHTS[HEADER_REQUESTING_CONTACTS] = 2;
  29152. HEADER_WEIGHTS[HEADER_PENDING_CONTACTS] = 3;
  29153. // Module-level variables
  29154. // ----------------------
  29155. this.callback = callback || function () {};
  29156. this.initial_presence_sent = 0;
  29157. this.msg_counter = 0;
  29158. // Module-level functions
  29159. // ----------------------
  29160. this.giveFeedback = function (message, klass) {
  29161. $('.conn-feedback').each(function (idx, el) {
  29162. var $el = $(el);
  29163. $el.addClass('conn-feedback').text(message);
  29164. if (klass) {
  29165. $el.addClass(klass);
  29166. } else {
  29167. $el.removeClass('error');
  29168. }
  29169. });
  29170. };
  29171. this.log = function (txt, level) {
  29172. if (this.debug) {
  29173. if (level == 'error') {
  29174. console.log('ERROR: '+txt);
  29175. } else {
  29176. console.log(txt);
  29177. }
  29178. }
  29179. };
  29180. this.getVCard = function (jid, callback, errback) {
  29181. if (!this.use_vcards) {
  29182. if (callback) {
  29183. callback(jid, jid);
  29184. }
  29185. return;
  29186. }
  29187. converse.connection.vcard.get(
  29188. $.proxy(function (iq) {
  29189. // Successful callback
  29190. var $vcard = $(iq).find('vCard');
  29191. var fullname = $vcard.find('FN').text(),
  29192. img = $vcard.find('BINVAL').text(),
  29193. img_type = $vcard.find('TYPE').text(),
  29194. url = $vcard.find('URL').text();
  29195. if (jid) {
  29196. var contact = converse.roster.get(jid);
  29197. if (contact) {
  29198. fullname = _.isEmpty(fullname)? contact.get('fullname') || jid: fullname;
  29199. contact.save({
  29200. 'fullname': fullname,
  29201. 'image_type': img_type,
  29202. 'image': img,
  29203. 'url': url,
  29204. 'vcard_updated': moment().format()
  29205. });
  29206. }
  29207. }
  29208. if (callback) {
  29209. callback(jid, fullname, img, img_type, url);
  29210. }
  29211. }, this),
  29212. jid,
  29213. function (iq) {
  29214. // Error callback
  29215. var contact = converse.roster.get(jid);
  29216. if (contact) {
  29217. contact.save({
  29218. 'vcard_updated': moment().format()
  29219. });
  29220. }
  29221. if (errback) {
  29222. errback(jid, iq);
  29223. }
  29224. }
  29225. );
  29226. };
  29227. this.reconnect = function () {
  29228. converse.giveFeedback(__('Reconnecting'), 'error');
  29229. converse.emit('reconnect');
  29230. if (!converse.prebind) {
  29231. this.connection.connect(
  29232. this.connection.jid,
  29233. this.connection.pass,
  29234. function (status, condition) {
  29235. converse.onConnect(status, condition, true);
  29236. },
  29237. this.connection.wait,
  29238. this.connection.hold,
  29239. this.connection.route
  29240. );
  29241. }
  29242. };
  29243. this.renderLoginPanel = function () {
  29244. converse._tearDown();
  29245. var view = converse.chatboxviews.get('controlbox');
  29246. view.model.set({connected:false});
  29247. view.renderLoginPanel();
  29248. };
  29249. this.onConnect = function (status, condition, reconnect) {
  29250. if ((status === Strophe.Status.CONNECTED) ||
  29251. (status === Strophe.Status.ATTACHED)) {
  29252. if ((typeof reconnect !== 'undefined') && (reconnect)) {
  29253. converse.log(status === Strophe.Status.CONNECTED ? 'Reconnected' : 'Reattached');
  29254. converse.onReconnected();
  29255. } else {
  29256. converse.log(status === Strophe.Status.CONNECTED ? 'Connected' : 'Attached');
  29257. converse.onConnected();
  29258. }
  29259. } else if (status === Strophe.Status.DISCONNECTED) {
  29260. if (converse.auto_reconnect) {
  29261. converse.reconnect();
  29262. } else {
  29263. converse.renderLoginPanel();
  29264. }
  29265. } else if (status === Strophe.Status.Error) {
  29266. converse.giveFeedback(__('Error'), 'error');
  29267. } else if (status === Strophe.Status.CONNECTING) {
  29268. converse.giveFeedback(__('Connecting'));
  29269. } else if (status === Strophe.Status.AUTHENTICATING) {
  29270. converse.giveFeedback(__('Authenticating'));
  29271. } else if (status === Strophe.Status.AUTHFAIL) {
  29272. converse.giveFeedback(__('Authentication Failed'), 'error');
  29273. converse.connection.disconnect(__('Authentication Failed'));
  29274. } else if (status === Strophe.Status.DISCONNECTING) {
  29275. if (!converse.connection.connected) {
  29276. converse.renderLoginPanel();
  29277. }
  29278. if (condition) {
  29279. converse.giveFeedback(condition, 'error');
  29280. }
  29281. }
  29282. };
  29283. this.applyHeightResistance = function (height) {
  29284. /* This method applies some resistance/gravity around the
  29285. * "default_box_height". If "height" is close enough to
  29286. * default_box_height, then that is returned instead.
  29287. */
  29288. if (typeof height === 'undefined') {
  29289. return converse.default_box_height;
  29290. }
  29291. var resistance = 10;
  29292. if ((height !== converse.default_box_height) &&
  29293. (Math.abs(height - converse.default_box_height) < resistance)) {
  29294. return converse.default_box_height;
  29295. }
  29296. return height;
  29297. };
  29298. this.updateMsgCounter = function () {
  29299. if (this.msg_counter > 0) {
  29300. if (document.title.search(/^Messages \(\d+\) /) == -1) {
  29301. document.title = "Messages (" + this.msg_counter + ") " + document.title;
  29302. } else {
  29303. document.title = document.title.replace(/^Messages \(\d+\) /, "Messages (" + this.msg_counter + ") ");
  29304. }
  29305. window.blur();
  29306. window.focus();
  29307. } else if (document.title.search(/^Messages \(\d+\) /) != -1) {
  29308. document.title = document.title.replace(/^Messages \(\d+\) /, "");
  29309. }
  29310. };
  29311. this.incrementMsgCounter = function () {
  29312. this.msg_counter += 1;
  29313. this.updateMsgCounter();
  29314. };
  29315. this.clearMsgCounter = function () {
  29316. this.msg_counter = 0;
  29317. this.updateMsgCounter();
  29318. };
  29319. this.initStatus = function (callback) {
  29320. this.xmppstatus = new this.XMPPStatus();
  29321. var id = b64_sha1('converse.xmppstatus-'+converse.bare_jid);
  29322. this.xmppstatus.id = id; // Appears to be necessary for backbone.browserStorage
  29323. this.xmppstatus.browserStorage = new Backbone.BrowserStorage[converse.storage](id);
  29324. this.xmppstatus.fetch({success: callback, error: callback});
  29325. };
  29326. this.initSession = function () {
  29327. this.session = new this.BOSHSession();
  29328. var id = b64_sha1('converse.bosh-session');
  29329. this.session.id = id; // Appears to be necessary for backbone.browserStorage
  29330. this.session.browserStorage = new Backbone.BrowserStorage[converse.storage](id);
  29331. this.session.fetch();
  29332. $(window).on('beforeunload', $.proxy(function () {
  29333. if (converse.connection.authenticated) {
  29334. this.setSession();
  29335. } else {
  29336. this.clearSession();
  29337. }
  29338. }, this));
  29339. };
  29340. this.clearSession = function () {
  29341. this.roster.browserStorage._clear();
  29342. this.session.browserStorage._clear();
  29343. // XXX: this should perhaps go into the beforeunload handler
  29344. converse.chatboxes.get('controlbox').save({'connected': false});
  29345. };
  29346. this.setSession = function () {
  29347. if (this.keepalive) {
  29348. this.session.save({
  29349. jid: this.connection.jid,
  29350. rid: this.connection._proto.rid,
  29351. sid: this.connection._proto.sid
  29352. });
  29353. }
  29354. };
  29355. this.logOut = function () {
  29356. converse.chatboxviews.closeAllChatBoxes(false);
  29357. converse.clearSession();
  29358. converse.connection.disconnect();
  29359. };
  29360. this.registerGlobalEventHandlers = function () {
  29361. $(document).click(function () {
  29362. if ($('.toggle-otr ul').is(':visible')) {
  29363. $('.toggle-otr ul', this).slideUp();
  29364. }
  29365. if ($('.toggle-smiley ul').is(':visible')) {
  29366. $('.toggle-smiley ul', this).slideUp();
  29367. }
  29368. });
  29369. $(document).on('mousemove', $.proxy(function (ev) {
  29370. if (!this.resized_chatbox || !this.allow_dragresize) { return true; }
  29371. ev.preventDefault();
  29372. this.resized_chatbox.resizeChatBox(ev);
  29373. }, this));
  29374. $(document).on('mouseup', $.proxy(function (ev) {
  29375. if (!this.resized_chatbox || !this.allow_dragresize) { return true; }
  29376. ev.preventDefault();
  29377. var height = this.applyHeightResistance(this.resized_chatbox.height);
  29378. if (this.connection.connected) {
  29379. this.resized_chatbox.model.save({'height': height});
  29380. } else {
  29381. this.resized_chatbox.model.set({'height': height});
  29382. }
  29383. this.resized_chatbox = null;
  29384. }, this));
  29385. $(window).on("blur focus", $.proxy(function (ev) {
  29386. if ((this.windowState != ev.type) && (ev.type == 'focus')) {
  29387. converse.clearMsgCounter();
  29388. }
  29389. this.windowState = ev.type;
  29390. },this));
  29391. $(window).on("resize", _.debounce($.proxy(function (ev) {
  29392. this.chatboxviews.trimChats();
  29393. },this), 200));
  29394. };
  29395. this.onReconnected = function () {
  29396. // We need to re-register all the event handlers on the newly
  29397. // created connection.
  29398. this.initStatus($.proxy(function () {
  29399. this.registerRosterXHandler();
  29400. this.registerPresenceHandler();
  29401. this.chatboxes.registerMessageHandler();
  29402. converse.xmppstatus.sendPresence();
  29403. this.giveFeedback(__('Online Contacts'));
  29404. }, this));
  29405. };
  29406. this.enableCarbons = function () {
  29407. /* Ask the XMPP server to enable Message Carbons
  29408. * See XEP-0280 https://xmpp.org/extensions/xep-0280.html#enabling
  29409. */
  29410. if (!this.message_carbons) {
  29411. return;
  29412. }
  29413. var carbons_iq = new Strophe.Builder('iq', {
  29414. from: this.connection.jid,
  29415. id: 'enablecarbons',
  29416. type: 'set'
  29417. })
  29418. .c('enable', {xmlns: 'urn:xmpp:carbons:2'});
  29419. this.connection.send(carbons_iq);
  29420. this.connection.addHandler(function (iq) {
  29421. if ($(iq).find('error').length > 0) {
  29422. converse.log('ERROR: An error occured while trying to enable message carbons.');
  29423. } else {
  29424. converse.log('Message carbons appear to have been enabled.');
  29425. }
  29426. }, null, "iq", null, "enablecarbons");
  29427. };
  29428. this.onConnected = function () {
  29429. // When reconnecting, there might be some open chat boxes. We don't
  29430. // know whether these boxes are of the same account or not, so we
  29431. // close them now.
  29432. this.chatboxviews.closeAllChatBoxes();
  29433. this.setSession();
  29434. this.jid = this.connection.jid;
  29435. this.bare_jid = Strophe.getBareJidFromJid(this.connection.jid);
  29436. this.domain = Strophe.getDomainFromJid(this.connection.jid);
  29437. this.minimized_chats = new converse.MinimizedChats({model: this.chatboxes});
  29438. this.features = new this.Features();
  29439. this.enableCarbons();
  29440. this.initStatus($.proxy(function () {
  29441. this.chatboxes.onConnected();
  29442. this.giveFeedback(__('Online Contacts'));
  29443. if (this.callback) {
  29444. if (this.connection.service === 'jasmine tests') {
  29445. // XXX: Call back with the internal converse object. This
  29446. // object should never be exposed to production systems.
  29447. // 'jasmine tests' is an invalid http bind service value,
  29448. // so we're sure that this is just for tests.
  29449. //
  29450. // TODO: We might need to consider websockets, which
  29451. // probably won't use the 'service' attr. Current
  29452. // strophe.js version used by converse.js doesn't support
  29453. // websockets.
  29454. this.callback(this);
  29455. } else {
  29456. this.callback();
  29457. }
  29458. }
  29459. }, this));
  29460. converse.emit('ready');
  29461. };
  29462. // Backbone Models and Views
  29463. // -------------------------
  29464. this.OTR = Backbone.Model.extend({
  29465. // A model for managing OTR settings.
  29466. getSessionPassphrase: function () {
  29467. if (converse.prebind) {
  29468. var key = b64_sha1(converse.connection.jid),
  29469. pass = window.sessionStorage[key];
  29470. if (typeof pass === 'undefined') {
  29471. pass = Math.floor(Math.random()*4294967295).toString();
  29472. window.sessionStorage[key] = pass;
  29473. }
  29474. return pass;
  29475. } else {
  29476. return converse.connection.pass;
  29477. }
  29478. },
  29479. generatePrivateKey: function () {
  29480. var key = new DSA();
  29481. var jid = converse.connection.jid;
  29482. if (converse.cache_otr_key) {
  29483. var cipher = CryptoJS.lib.PasswordBasedCipher;
  29484. var pass = this.getSessionPassphrase();
  29485. if (typeof pass !== "undefined") {
  29486. // Encrypt the key and set in sessionStorage. Also store instance tag.
  29487. window.sessionStorage[b64_sha1(jid+'priv_key')] =
  29488. cipher.encrypt(CryptoJS.algo.AES, key.packPrivate(), pass).toString();
  29489. window.sessionStorage[b64_sha1(jid+'instance_tag')] = instance_tag;
  29490. window.sessionStorage[b64_sha1(jid+'pass_check')] =
  29491. cipher.encrypt(CryptoJS.algo.AES, 'match', pass).toString();
  29492. }
  29493. }
  29494. return key;
  29495. }
  29496. });
  29497. this.Message = Backbone.Model;
  29498. this.Messages = Backbone.Collection.extend({
  29499. model: converse.Message
  29500. });
  29501. this.ChatBox = Backbone.Model.extend({
  29502. initialize: function () {
  29503. var height = converse.applyHeightResistance(this.get('height'));
  29504. if (this.get('box_id') !== 'controlbox') {
  29505. this.messages = new converse.Messages();
  29506. this.messages.browserStorage = new Backbone.BrowserStorage[converse.storage](
  29507. b64_sha1('converse.messages'+this.get('jid')+converse.bare_jid));
  29508. this.save({
  29509. 'box_id' : b64_sha1(this.get('jid')),
  29510. 'height': height,
  29511. 'minimized': this.get('minimized') || false,
  29512. 'otr_status': this.get('otr_status') || UNENCRYPTED,
  29513. 'time_minimized': this.get('time_minimized') || moment(),
  29514. 'time_opened': this.get('time_opened') || moment().valueOf(),
  29515. 'user_id' : Strophe.getNodeFromJid(this.get('jid')),
  29516. 'num_unread': this.get('num_unread') || 0,
  29517. 'url': ''
  29518. });
  29519. } else {
  29520. this.set({
  29521. 'height': height,
  29522. 'time_opened': moment(0).valueOf(),
  29523. 'num_unread': this.get('num_unread') || 0
  29524. });
  29525. }
  29526. },
  29527. maximize: function () {
  29528. this.save({
  29529. 'minimized': false,
  29530. 'time_opened': moment().valueOf()
  29531. });
  29532. },
  29533. minimize: function () {
  29534. this.save({
  29535. 'minimized': true,
  29536. 'time_minimized': moment().format()
  29537. });
  29538. },
  29539. getSession: function (callback) {
  29540. var cipher = CryptoJS.lib.PasswordBasedCipher;
  29541. var result, pass, instance_tag, saved_key, pass_check;
  29542. if (converse.cache_otr_key) {
  29543. pass = converse.otr.getSessionPassphrase();
  29544. if (typeof pass !== "undefined") {
  29545. instance_tag = window.sessionStorage[b64_sha1(this.id+'instance_tag')];
  29546. saved_key = window.sessionStorage[b64_sha1(this.id+'priv_key')];
  29547. pass_check = window.sessionStorage[b64_sha1(this.connection.jid+'pass_check')];
  29548. if (saved_key && instance_tag && typeof pass_check !== 'undefined') {
  29549. var decrypted = cipher.decrypt(CryptoJS.algo.AES, saved_key, pass);
  29550. var key = DSA.parsePrivate(decrypted.toString(CryptoJS.enc.Latin1));
  29551. if (cipher.decrypt(CryptoJS.algo.AES, pass_check, pass).toString(CryptoJS.enc.Latin1) === 'match') {
  29552. // Verified that the passphrase is still the same
  29553. this.trigger('showHelpMessages', [__('Re-establishing encrypted session')]);
  29554. callback({
  29555. 'key': key,
  29556. 'instance_tag': instance_tag
  29557. });
  29558. return; // Our work is done here
  29559. }
  29560. }
  29561. }
  29562. }
  29563. // We need to generate a new key and instance tag
  29564. this.trigger('showHelpMessages', [
  29565. __('Generating private key.'),
  29566. __('Your browser might become unresponsive.')],
  29567. null,
  29568. true // show spinner
  29569. );
  29570. setTimeout(function () {
  29571. callback({
  29572. 'key': converse.otr.generatePrivateKey.apply(this),
  29573. 'instance_tag': OTR.makeInstanceTag()
  29574. });
  29575. }, 500);
  29576. },
  29577. updateOTRStatus: function (state) {
  29578. switch (state) {
  29579. case OTR.CONST.STATUS_AKE_SUCCESS:
  29580. if (this.otr.msgstate === OTR.CONST.MSGSTATE_ENCRYPTED) {
  29581. this.save({'otr_status': UNVERIFIED});
  29582. }
  29583. break;
  29584. case OTR.CONST.STATUS_END_OTR:
  29585. if (this.otr.msgstate === OTR.CONST.MSGSTATE_FINISHED) {
  29586. this.save({'otr_status': FINISHED});
  29587. } else if (this.otr.msgstate === OTR.CONST.MSGSTATE_PLAINTEXT) {
  29588. this.save({'otr_status': UNENCRYPTED});
  29589. }
  29590. break;
  29591. }
  29592. },
  29593. onSMP: function (type, data) {
  29594. // Event handler for SMP (Socialist's Millionaire Protocol)
  29595. // used by OTR (off-the-record).
  29596. switch (type) {
  29597. case 'question':
  29598. this.otr.smpSecret(prompt(__(
  29599. 'Authentication request from %1$s\n\nYour chat contact is attempting to verify your identity, by asking you the question below.\n\n%2$s',
  29600. [this.get('fullname'), data])));
  29601. break;
  29602. case 'trust':
  29603. if (data === true) {
  29604. this.save({'otr_status': VERIFIED});
  29605. } else {
  29606. this.trigger(
  29607. 'showHelpMessages',
  29608. [__("Could not verify this user's identify.")],
  29609. 'error');
  29610. this.save({'otr_status': UNVERIFIED});
  29611. }
  29612. break;
  29613. default:
  29614. throw new Error('Unknown type.');
  29615. }
  29616. },
  29617. initiateOTR: function (query_msg) {
  29618. // Sets up an OTR object through which we can send and receive
  29619. // encrypted messages.
  29620. //
  29621. // If 'query_msg' is passed in, it means there is an alread incoming
  29622. // query message from our contact. Otherwise, it is us who will
  29623. // send the query message to them.
  29624. this.save({'otr_status': UNENCRYPTED});
  29625. var session = this.getSession($.proxy(function (session) {
  29626. this.otr = new OTR({
  29627. fragment_size: 140,
  29628. send_interval: 200,
  29629. priv: session.key,
  29630. instance_tag: session.instance_tag,
  29631. debug: this.debug
  29632. });
  29633. this.otr.on('status', $.proxy(this.updateOTRStatus, this));
  29634. this.otr.on('smp', $.proxy(this.onSMP, this));
  29635. this.otr.on('ui', $.proxy(function (msg) {
  29636. this.trigger('showReceivedOTRMessage', msg);
  29637. }, this));
  29638. this.otr.on('io', $.proxy(function (msg) {
  29639. this.trigger('sendMessageStanza', msg);
  29640. }, this));
  29641. this.otr.on('error', $.proxy(function (msg) {
  29642. this.trigger('showOTRError', msg);
  29643. }, this));
  29644. this.trigger('showHelpMessages', [__('Exchanging private key with contact.')]);
  29645. if (query_msg) {
  29646. this.otr.receiveMsg(query_msg);
  29647. } else {
  29648. this.otr.sendQueryMsg();
  29649. }
  29650. }, this));
  29651. },
  29652. endOTR: function () {
  29653. if (this.otr) {
  29654. this.otr.endOtr();
  29655. }
  29656. this.save({'otr_status': UNENCRYPTED});
  29657. },
  29658. createMessage: function ($message) {
  29659. var body = $message.children('body').text(),
  29660. composing = $message.find('composing'),
  29661. paused = $message.find('paused'),
  29662. delayed = $message.find('delay').length > 0,
  29663. fullname = this.get('fullname'),
  29664. is_groupchat = $message.attr('type') === 'groupchat',
  29665. msgid = $message.attr('id'),
  29666. stamp, time, sender, from;
  29667. if (is_groupchat) {
  29668. from = Strophe.unescapeNode(Strophe.getResourceFromJid($message.attr('from')));
  29669. } else {
  29670. from = Strophe.getBareJidFromJid($message.attr('from'));
  29671. }
  29672. fullname = (_.isEmpty(fullname)? from: fullname).split(' ')[0];
  29673. if (!body) {
  29674. if (composing.length || paused.length) {
  29675. // FIXME: use one attribute for chat states (e.g.
  29676. // chatstate) instead of saving 'paused' and
  29677. // 'composing' separately.
  29678. this.messages.add({
  29679. fullname: fullname,
  29680. sender: 'them',
  29681. delayed: delayed,
  29682. time: moment().format(),
  29683. composing: composing.length,
  29684. paused: paused.length
  29685. });
  29686. }
  29687. } else {
  29688. if (delayed) {
  29689. stamp = $message.find('delay').attr('stamp');
  29690. time = stamp;
  29691. } else {
  29692. time = moment().format();
  29693. }
  29694. if ((is_groupchat && from === this.get('nick')) || (!is_groupchat && from == converse.bare_jid)) {
  29695. sender = 'me';
  29696. } else {
  29697. sender = 'them';
  29698. }
  29699. this.messages.create({
  29700. fullname: fullname,
  29701. sender: sender,
  29702. delayed: delayed,
  29703. time: time,
  29704. message: body,
  29705. msgid: msgid
  29706. });
  29707. }
  29708. },
  29709. receiveMessage: function ($message) {
  29710. var $body = $message.children('body');
  29711. var text = ($body.length > 0 ? $body.text() : undefined);
  29712. if ((!text) || (!converse.allow_otr)) {
  29713. return this.createMessage($message);
  29714. }
  29715. if (text.match(/^\?OTRv23?/)) {
  29716. this.initiateOTR(text);
  29717. } else {
  29718. if (_.contains([UNVERIFIED, VERIFIED], this.get('otr_status'))) {
  29719. this.otr.receiveMsg(text);
  29720. } else {
  29721. if (text.match(/^\?OTR/)) {
  29722. if (!this.otr) {
  29723. this.initiateOTR(text);
  29724. } else {
  29725. this.otr.receiveMsg(text);
  29726. }
  29727. } else {
  29728. // Normal unencrypted message.
  29729. this.createMessage($message);
  29730. }
  29731. }
  29732. }
  29733. }
  29734. });
  29735. this.ChatBoxView = Backbone.View.extend({
  29736. length: 200,
  29737. tagName: 'div',
  29738. className: 'chatbox',
  29739. is_chatroom: false, // This is not a multi-user chatroom
  29740. events: {
  29741. 'click .close-chatbox-button': 'close',
  29742. 'click .toggle-chatbox-button': 'minimize',
  29743. 'keypress textarea.chat-textarea': 'keyPressed',
  29744. 'click .toggle-smiley': 'toggleEmoticonMenu',
  29745. 'click .toggle-smiley ul li': 'insertEmoticon',
  29746. 'click .toggle-clear': 'clearMessages',
  29747. 'click .toggle-otr': 'toggleOTRMenu',
  29748. 'click .start-otr': 'startOTRFromToolbar',
  29749. 'click .end-otr': 'endOTR',
  29750. 'click .auth-otr': 'authOTR',
  29751. 'click .toggle-call': 'toggleCall',
  29752. 'mousedown .dragresize-tm': 'onDragResizeStart'
  29753. },
  29754. initialize: function (){
  29755. this.model.messages.on('add', this.onMessageAdded, this);
  29756. this.model.on('show', this.show, this);
  29757. this.model.on('destroy', this.hide, this);
  29758. this.model.on('change', this.onChange, this);
  29759. this.model.on('showOTRError', this.showOTRError, this);
  29760. // XXX: doesn't look like this event is being used?
  29761. this.model.on('buddyStartsOTR', this.buddyStartsOTR, this);
  29762. this.model.on('showHelpMessages', this.showHelpMessages, this);
  29763. this.model.on('sendMessageStanza', this.sendMessageStanza, this);
  29764. this.model.on('showSentOTRMessage', function (text) {
  29765. this.showMessage({'message': text, 'sender': 'me'});
  29766. }, this);
  29767. this.model.on('showReceivedOTRMessage', function (text) {
  29768. this.showMessage({'message': text, 'sender': 'them'});
  29769. }, this);
  29770. this.updateVCard();
  29771. this.$el.insertAfter(converse.chatboxviews.get("controlbox").$el);
  29772. this.render().model.messages.fetch({add: true});
  29773. if (this.model.get('minimized')) {
  29774. this.hide();
  29775. } else {
  29776. this.show();
  29777. }
  29778. if ((_.contains([UNVERIFIED, VERIFIED], this.model.get('otr_status'))) || converse.use_otr_by_default) {
  29779. this.model.initiateOTR();
  29780. }
  29781. },
  29782. render: function () {
  29783. this.$el.attr('id', this.model.get('box_id'))
  29784. .html(converse.templates.chatbox(
  29785. _.extend(this.model.toJSON(), {
  29786. show_toolbar: converse.show_toolbar,
  29787. label_personal_message: __('Personal message')
  29788. }
  29789. )
  29790. )
  29791. );
  29792. this.renderToolbar().renderAvatar();
  29793. converse.emit('chatBoxOpened', this);
  29794. setTimeout(function () {
  29795. converse.refreshWebkit();
  29796. }, 50);
  29797. return this.showStatusMessage();
  29798. },
  29799. initDragResize: function () {
  29800. this.prev_pageY = 0; // To store last known mouse position
  29801. if (converse.connection.connected) {
  29802. this.height = this.model.get('height');
  29803. }
  29804. return this;
  29805. },
  29806. showStatusNotification: function (message, keep_old) {
  29807. var $chat_content = this.$el.find('.chat-content');
  29808. if (!keep_old) {
  29809. $chat_content.find('div.chat-event').remove();
  29810. }
  29811. $chat_content.append($('<div class="chat-event"></div>').text(message));
  29812. this.scrollDown();
  29813. },
  29814. clearChatRoomMessages: function (ev) {
  29815. if (typeof ev !== "undefined") { ev.stopPropagation(); }
  29816. var result = confirm(__("Are you sure you want to clear the messages from this room?"));
  29817. if (result === true) {
  29818. this.$el.find('.chat-content').empty();
  29819. }
  29820. return this;
  29821. },
  29822. showMessage: function (msg_dict) {
  29823. var $content = this.$el.find('.chat-content'),
  29824. msg_time = moment(msg_dict.time) || moment,
  29825. text = msg_dict.message,
  29826. match = text.match(/^\/(.*?)(?: (.*))?$/),
  29827. fullname = this.model.get('fullname') || msg_dict.fullname,
  29828. extra_classes = msg_dict.delayed && 'delayed' || '',
  29829. template, username;
  29830. if ((match) && (match[1] === 'me')) {
  29831. text = text.replace(/^\/me/, '');
  29832. template = converse.templates.action;
  29833. username = fullname;
  29834. } else {
  29835. template = converse.templates.message;
  29836. username = msg_dict.sender === 'me' && __('me') || fullname;
  29837. }
  29838. $content.find('div.chat-event').remove();
  29839. if (this.is_chatroom && msg_dict.sender == 'them' && (new RegExp("\\b"+this.model.get('nick')+"\\b")).test(text)) {
  29840. // Add special class to mark groupchat messages in which we
  29841. // are mentioned.
  29842. extra_classes += ' mentioned';
  29843. }
  29844. var message = template({
  29845. 'sender': msg_dict.sender,
  29846. 'time': msg_time.format('hh:mm'),
  29847. 'username': username,
  29848. 'message': '',
  29849. 'extra_classes': extra_classes
  29850. });
  29851. $content.append($(message).children('.chat-message-content').first().text(text).addHyperlinks().addEmoticons().parent());
  29852. this.scrollDown();
  29853. },
  29854. showHelpMessages: function (msgs, type, spinner) {
  29855. var $chat_content = this.$el.find('.chat-content'), i,
  29856. msgs_length = msgs.length;
  29857. for (i=0; i<msgs_length; i++) {
  29858. $chat_content.append($('<div class="chat-'+(type||'info')+'">'+msgs[i]+'</div>'));
  29859. }
  29860. if (spinner === true) {
  29861. $chat_content.append('<span class="spinner"/>');
  29862. } else if (spinner === false) {
  29863. $chat_content.find('span.spinner').remove();
  29864. }
  29865. return this.scrollDown();
  29866. },
  29867. onMessageAdded: function (message) {
  29868. var time = message.get('time'),
  29869. times = this.model.messages.pluck('time'),
  29870. previous_message, idx, this_date, prev_date, text, match;
  29871. // If this message is on a different day than the one received
  29872. // prior, then indicate it on the chatbox.
  29873. idx = _.indexOf(times, time)-1;
  29874. if (idx >= 0) {
  29875. previous_message = this.model.messages.at(idx);
  29876. prev_date = moment(previous_message.get('time'));
  29877. if (prev_date.isBefore(time, 'day')) {
  29878. this_date = moment(time);
  29879. this.$el.find('.chat-content').append(converse.templates.new_day({
  29880. isodate: this_date.format("YYYY-MM-DD"),
  29881. datestring: this_date.format("dddd MMM Do YYYY")
  29882. }));
  29883. }
  29884. }
  29885. if (message.get(COMPOSING)) {
  29886. this.showStatusNotification(message.get('fullname')+' '+__('is typing'));
  29887. return;
  29888. } else if (message.get(PAUSED)) {
  29889. this.showStatusNotification(message.get('fullname')+' '+__('has stopped typing'));
  29890. return;
  29891. } else {
  29892. this.showMessage(_.clone(message.attributes));
  29893. }
  29894. if ((message.get('sender') != 'me') && (converse.windowState == 'blur')) {
  29895. converse.incrementMsgCounter();
  29896. }
  29897. return this.scrollDown();
  29898. },
  29899. sendMessageStanza: function (text) {
  29900. /*
  29901. * Sends the actual XML stanza to the XMPP server.
  29902. */
  29903. // TODO: Look in ChatPartners to see what resources we have for the recipient.
  29904. // if we have one resource, we sent to only that resources, if we have multiple
  29905. // we send to the bare jid.
  29906. var timestamp = (new Date()).getTime();
  29907. var bare_jid = this.model.get('jid');
  29908. var message = $msg({from: converse.connection.jid, to: bare_jid, type: 'chat', id: timestamp})
  29909. .c('body').t(text).up()
  29910. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'});
  29911. converse.connection.send(message);
  29912. if (converse.forward_messages) {
  29913. // Forward the message, so that other connected resources are also aware of it.
  29914. var forwarded = $msg({to:converse.bare_jid, type:'chat', id:timestamp})
  29915. .c('forwarded', {xmlns:'urn:xmpp:forward:0'})
  29916. .c('delay', {xmns:'urn:xmpp:delay',stamp:timestamp}).up()
  29917. .cnode(message.tree());
  29918. converse.connection.send(forwarded);
  29919. }
  29920. },
  29921. sendMessage: function (text) {
  29922. var match = text.replace(/^\s*/, "").match(/^\/(.*)\s*$/), msgs;
  29923. if (match) {
  29924. if (match[1] === "clear") {
  29925. return this.clearMessages();
  29926. }
  29927. else if (match[1] === "help") {
  29928. msgs = [
  29929. '<strong>/help</strong>:'+__('Show this menu')+'',
  29930. '<strong>/me</strong>:'+__('Write in the third person')+'',
  29931. '<strong>/clear</strong>:'+__('Remove messages')+''
  29932. ];
  29933. this.showHelpMessages(msgs);
  29934. return;
  29935. } else if ((converse.allow_otr) && (match[1] === "endotr")) {
  29936. return this.endOTR();
  29937. } else if ((converse.allow_otr) && (match[1] === "otr")) {
  29938. return this.model.initiateOTR();
  29939. }
  29940. }
  29941. if (_.contains([UNVERIFIED, VERIFIED], this.model.get('otr_status'))) {
  29942. // Off-the-record encryption is active
  29943. this.model.otr.sendMsg(text);
  29944. this.model.trigger('showSentOTRMessage', text);
  29945. } else {
  29946. // We only save unencrypted messages.
  29947. var fullname = converse.xmppstatus.get('fullname');
  29948. fullname = _.isEmpty(fullname)? converse.bare_jid: fullname;
  29949. this.model.messages.create({
  29950. fullname: fullname,
  29951. sender: 'me',
  29952. time: moment().format(),
  29953. message: text
  29954. });
  29955. this.sendMessageStanza(text);
  29956. }
  29957. },
  29958. keyPressed: function (ev) {
  29959. var $textarea = $(ev.target),
  29960. message, notify, composing;
  29961. if(ev.keyCode == KEY.ENTER) {
  29962. ev.preventDefault();
  29963. message = $textarea.val();
  29964. $textarea.val('').focus();
  29965. if (message !== '') {
  29966. if (this.model.get('chatroom')) {
  29967. this.sendChatRoomMessage(message);
  29968. } else {
  29969. this.sendMessage(message);
  29970. }
  29971. converse.emit('messageSend', message);
  29972. }
  29973. this.$el.data('composing', false);
  29974. } else if (!this.model.get('chatroom')) {
  29975. // composing data is only for single user chat
  29976. composing = this.$el.data('composing');
  29977. if (!composing) {
  29978. if (ev.keyCode != 47) {
  29979. // We don't send composing messages if the message
  29980. // starts with forward-slash.
  29981. notify = $msg({'to':this.model.get('jid'), 'type': 'chat'})
  29982. .c('composing', {'xmlns':'http://jabber.org/protocol/chatstates'});
  29983. converse.connection.send(notify);
  29984. }
  29985. this.$el.data('composing', true);
  29986. }
  29987. }
  29988. },
  29989. onDragResizeStart: function (ev) {
  29990. if (!converse.allow_dragresize) { return true; }
  29991. // Record element attributes for mouseMove().
  29992. this.height = this.$el.children('.box-flyout').height();
  29993. converse.resized_chatbox = this;
  29994. this.prev_pageY = ev.pageY;
  29995. },
  29996. setChatBoxHeight: function (height) {
  29997. if (!this.model.get('minimized')) {
  29998. this.$el.children('.box-flyout')[0].style.height = converse.applyHeightResistance(height)+'px';
  29999. }
  30000. },
  30001. resizeChatBox: function (ev) {
  30002. var diff = ev.pageY - this.prev_pageY;
  30003. if (!diff) { return; }
  30004. this.height -= diff;
  30005. this.prev_pageY = ev.pageY;
  30006. this.setChatBoxHeight(this.height);
  30007. },
  30008. clearMessages: function (ev) {
  30009. if (ev && ev.preventDefault) { ev.preventDefault(); }
  30010. var result = confirm(__("Are you sure you want to clear the messages from this chat box?"));
  30011. if (result === true) {
  30012. this.$el.find('.chat-content').empty();
  30013. this.model.messages.reset();
  30014. this.model.messages.browserStorage._clear();
  30015. }
  30016. return this;
  30017. },
  30018. insertEmoticon: function (ev) {
  30019. ev.stopPropagation();
  30020. this.$el.find('.toggle-smiley ul').slideToggle(200);
  30021. var $textbox = this.$el.find('textarea.chat-textarea');
  30022. var value = $textbox.val();
  30023. var $target = $(ev.target);
  30024. $target = $target.is('a') ? $target : $target.children('a');
  30025. if (value && (value[value.length-1] !== ' ')) {
  30026. value = value + ' ';
  30027. }
  30028. $textbox.focus().val(value+$target.data('emoticon')+' ');
  30029. },
  30030. toggleEmoticonMenu: function (ev) {
  30031. ev.stopPropagation();
  30032. this.$el.find('.toggle-smiley ul').slideToggle(200);
  30033. },
  30034. toggleOTRMenu: function (ev) {
  30035. ev.stopPropagation();
  30036. this.$el.find('.toggle-otr ul').slideToggle(200);
  30037. },
  30038. showOTRError: function (msg) {
  30039. if (msg == 'Message cannot be sent at this time.') {
  30040. this.showHelpMessages(
  30041. [__('Your message could not be sent')], 'error');
  30042. } else if (msg == 'Received an unencrypted message.') {
  30043. this.showHelpMessages(
  30044. [__('We received an unencrypted message')], 'error');
  30045. } else if (msg == 'Received an unreadable encrypted message.') {
  30046. this.showHelpMessages(
  30047. [__('We received an unreadable encrypted message')],
  30048. 'error');
  30049. } else {
  30050. this.showHelpMessages(['Encryption error occured: '+msg], 'error');
  30051. }
  30052. console.log("OTR ERROR:"+msg);
  30053. },
  30054. buddyStartsOTR: function (ev) {
  30055. this.showHelpMessages([__('This user has requested an encrypted session.')]);
  30056. this.model.initiateOTR();
  30057. },
  30058. startOTRFromToolbar: function (ev) {
  30059. $(ev.target).parent().parent().slideUp();
  30060. ev.stopPropagation();
  30061. this.model.initiateOTR();
  30062. },
  30063. endOTR: function (ev) {
  30064. if (typeof ev !== "undefined") {
  30065. ev.preventDefault();
  30066. ev.stopPropagation();
  30067. }
  30068. this.model.endOTR();
  30069. },
  30070. authOTR: function (ev) {
  30071. var scheme = $(ev.target).data().scheme;
  30072. var result, question, answer;
  30073. if (scheme === 'fingerprint') {
  30074. result = confirm(__('Here are the fingerprints, please confirm them with %1$s, outside of this chat.\n\nFingerprint for you, %2$s: %3$s\n\nFingerprint for %1$s: %4$s\n\nIf you have confirmed that the fingerprints match, click OK, otherwise click Cancel.', [
  30075. this.model.get('fullname'),
  30076. converse.xmppstatus.get('fullname')||converse.bare_jid,
  30077. this.model.otr.priv.fingerprint(),
  30078. this.model.otr.their_priv_pk.fingerprint()
  30079. ]
  30080. ));
  30081. if (result === true) {
  30082. this.model.save({'otr_status': VERIFIED});
  30083. } else {
  30084. this.model.save({'otr_status': UNVERIFIED});
  30085. }
  30086. } else if (scheme === 'smp') {
  30087. alert(__('You will be prompted to provide a security question and then an answer to that question.\n\nYour contact will then be prompted the same question and if they type the exact same answer (case sensitive), their identity will be verified.'));
  30088. question = prompt(__('What is your security question?'));
  30089. if (question) {
  30090. answer = prompt(__('What is the answer to the security question?'));
  30091. this.model.otr.smpSecret(answer, question);
  30092. }
  30093. } else {
  30094. this.showHelpMessages([__('Invalid authentication scheme provided')], 'error');
  30095. }
  30096. },
  30097. toggleCall: function (ev) {
  30098. ev.stopPropagation();
  30099. converse.emit('callButtonClicked', {
  30100. connection: converse.connection,
  30101. model: this.model
  30102. });
  30103. },
  30104. onChange: function (item, changed) {
  30105. if (_.has(item.changed, 'chat_status')) {
  30106. var chat_status = item.get('chat_status'),
  30107. fullname = item.get('fullname');
  30108. fullname = _.isEmpty(fullname)? item.get('jid'): fullname;
  30109. if (this.$el.is(':visible')) {
  30110. if (chat_status === 'offline') {
  30111. this.showStatusNotification(fullname+' '+'has gone offline');
  30112. } else if (chat_status === 'away') {
  30113. this.showStatusNotification(fullname+' '+'has gone away');
  30114. } else if ((chat_status === 'dnd')) {
  30115. this.showStatusNotification(fullname+' '+'is busy');
  30116. } else if (chat_status === 'online') {
  30117. this.$el.find('div.chat-event').remove();
  30118. }
  30119. }
  30120. converse.emit('contactStatusChanged', item.attributes, item.get('chat_status'));
  30121. // TODO: DEPRECATED AND SHOULD BE REMOVED IN 0.9.0
  30122. converse.emit('buddyStatusChanged', item.attributes, item.get('chat_status'));
  30123. }
  30124. if (_.has(item.changed, 'status')) {
  30125. this.showStatusMessage();
  30126. converse.emit('contactStatusMessageChanged', item.attributes, item.get('status'));
  30127. // TODO: DEPRECATED AND SHOULD BE REMOVED IN 0.9.0
  30128. converse.emit('buddyStatusMessageChanged', item.attributes, item.get('status'));
  30129. }
  30130. if (_.has(item.changed, 'image')) {
  30131. this.renderAvatar();
  30132. }
  30133. if (_.has(item.changed, 'otr_status')) {
  30134. this.renderToolbar().informOTRChange();
  30135. }
  30136. if (_.has(item.changed, 'minimized')) {
  30137. if (item.get('minimized')) {
  30138. this.hide();
  30139. } else {
  30140. this.maximize();
  30141. }
  30142. }
  30143. // TODO check for changed fullname as well
  30144. },
  30145. showStatusMessage: function (msg) {
  30146. msg = msg || this.model.get('status');
  30147. if (typeof msg === "string") {
  30148. this.$el.find('p.user-custom-message').text(msg).attr('title', msg);
  30149. }
  30150. return this;
  30151. },
  30152. close: function (ev) {
  30153. if (ev && ev.preventDefault) { ev.preventDefault(); }
  30154. if (converse.connection.connected) {
  30155. this.model.destroy();
  30156. } else {
  30157. this.model.trigger('hide');
  30158. }
  30159. converse.emit('chatBoxClosed', this);
  30160. return this;
  30161. },
  30162. maximize: function () {
  30163. // Restores a minimized chat box
  30164. this.$el.insertAfter(converse.chatboxviews.get("controlbox").$el).show('fast', $.proxy(function () {
  30165. converse.refreshWebkit();
  30166. this.focus();
  30167. converse.emit('chatBoxMaximized', this);
  30168. }, this));
  30169. },
  30170. minimize: function (ev) {
  30171. if (ev && ev.preventDefault) { ev.preventDefault(); }
  30172. // Minimizes a chat box
  30173. this.model.minimize();
  30174. this.$el.hide('fast', converse.refreshwebkit);
  30175. converse.emit('chatBoxMinimized', this);
  30176. },
  30177. updateVCard: function () {
  30178. var jid = this.model.get('jid'),
  30179. contact = converse.roster.get(jid);
  30180. if ((contact) && (!contact.get('vcard_updated'))) {
  30181. converse.getVCard(
  30182. jid,
  30183. $.proxy(function (jid, fullname, image, image_type, url) {
  30184. this.model.save({
  30185. 'fullname' : fullname || jid,
  30186. 'url': url,
  30187. 'image_type': image_type,
  30188. 'image': image
  30189. });
  30190. }, this),
  30191. $.proxy(function (stanza) {
  30192. converse.log("ChatBoxView.initialize: An error occured while fetching vcard");
  30193. }, this)
  30194. );
  30195. }
  30196. },
  30197. informOTRChange: function () {
  30198. var data = this.model.toJSON();
  30199. var msgs = [];
  30200. if (data.otr_status == UNENCRYPTED) {
  30201. msgs.push(__("Your messages are not encrypted anymore"));
  30202. } else if (data.otr_status == UNVERIFIED){
  30203. msgs.push(__("Your messages are now encrypted but your contact's identity has not been verified."));
  30204. } else if (data.otr_status == VERIFIED){
  30205. msgs.push(__("Your contact's identify has been verified."));
  30206. } else if (data.otr_status == FINISHED){
  30207. msgs.push(__("Your contact has ended encryption on their end, you should do the same."));
  30208. }
  30209. return this.showHelpMessages(msgs, 'info', false);
  30210. },
  30211. renderToolbar: function () {
  30212. if (converse.show_toolbar) {
  30213. var data = this.model.toJSON();
  30214. if (data.otr_status == UNENCRYPTED) {
  30215. data.otr_tooltip = __('Your messages are not encrypted. Click here to enable OTR encryption.');
  30216. } else if (data.otr_status == UNVERIFIED){
  30217. data.otr_tooltip = __('Your messages are encrypted, but your contact has not been verified.');
  30218. } else if (data.otr_status == VERIFIED){
  30219. data.otr_tooltip = __('Your messages are encrypted and your contact verified.');
  30220. } else if (data.otr_status == FINISHED){
  30221. data.otr_tooltip = __('Your contact has closed their end of the private session, you should do the same');
  30222. }
  30223. this.$el.find('.chat-toolbar').html(
  30224. converse.templates.toolbar(
  30225. _.extend(data, {
  30226. FINISHED: FINISHED,
  30227. UNENCRYPTED: UNENCRYPTED,
  30228. UNVERIFIED: UNVERIFIED,
  30229. VERIFIED: VERIFIED,
  30230. allow_otr: converse.allow_otr && !this.is_chatroom,
  30231. label_clear: __('Clear all messages'),
  30232. label_end_encrypted_conversation: __('End encrypted conversation'),
  30233. label_hide_participants: __('Hide the list of participants'),
  30234. label_refresh_encrypted_conversation: __('Refresh encrypted conversation'),
  30235. label_start_call: __('Start a call'),
  30236. label_start_encrypted_conversation: __('Start encrypted conversation'),
  30237. label_verify_with_fingerprints: __('Verify with fingerprints'),
  30238. label_verify_with_smp: __('Verify with SMP'),
  30239. label_whats_this: __("What\'s this?"),
  30240. otr_status_class: OTR_CLASS_MAPPING[data.otr_status],
  30241. otr_translated_status: OTR_TRANSLATED_MAPPING[data.otr_status],
  30242. show_call_button: converse.visible_toolbar_buttons.call,
  30243. show_clear_button: converse.visible_toolbar_buttons.clear,
  30244. show_emoticons: converse.visible_toolbar_buttons.emoticons,
  30245. show_participants_toggle: this.is_chatroom && converse.visible_toolbar_buttons.toggle_participants
  30246. })
  30247. )
  30248. );
  30249. }
  30250. return this;
  30251. },
  30252. renderAvatar: function () {
  30253. if (!this.model.get('image')) {
  30254. return;
  30255. }
  30256. var img_src = 'data:'+this.model.get('image_type')+';base64,'+this.model.get('image'),
  30257. canvas = $('<canvas height="31px" width="31px" class="avatar"></canvas>').get(0);
  30258. if (!(canvas.getContext && canvas.getContext('2d'))) {
  30259. return this;
  30260. }
  30261. var ctx = canvas.getContext('2d');
  30262. var img = new Image(); // Create new Image object
  30263. img.onload = function () {
  30264. var ratio = img.width/img.height;
  30265. ctx.drawImage(img, 0,0, 35*ratio, 35);
  30266. };
  30267. img.src = img_src;
  30268. this.$el.find('.chat-title').before(canvas);
  30269. return this;
  30270. },
  30271. focus: function () {
  30272. this.$el.find('.chat-textarea').focus();
  30273. converse.emit('chatBoxFocused', this);
  30274. return this;
  30275. },
  30276. hide: function () {
  30277. if (this.$el.is(':visible') && this.$el.css('opacity') == "1") {
  30278. this.$el.hide();
  30279. converse.refreshWebkit();
  30280. }
  30281. return this;
  30282. },
  30283. show: function (callback) {
  30284. if (this.$el.is(':visible') && this.$el.css('opacity') == "1") {
  30285. return this.focus();
  30286. }
  30287. this.$el.fadeIn(callback);
  30288. if (converse.connection.connected) {
  30289. // Without a connection, we haven't yet initialized
  30290. // localstorage
  30291. this.model.save();
  30292. this.initDragResize();
  30293. }
  30294. return this;
  30295. },
  30296. scrollDown: function () {
  30297. var $content = this.$('.chat-content');
  30298. if ($content.is(':visible')) {
  30299. $content.scrollTop($content[0].scrollHeight);
  30300. }
  30301. return this;
  30302. }
  30303. });
  30304. this.ContactsPanel = Backbone.View.extend({
  30305. tagName: 'div',
  30306. className: 'controlbox-pane',
  30307. id: 'users',
  30308. events: {
  30309. 'click a.toggle-xmpp-contact-form': 'toggleContactForm',
  30310. 'submit form.add-xmpp-contact': 'addContactFromForm',
  30311. 'submit form.search-xmpp-contact': 'searchContacts',
  30312. 'click a.subscribe-to-user': 'addContactFromList'
  30313. },
  30314. initialize: function (cfg) {
  30315. cfg.$parent.append(this.$el);
  30316. this.$tabs = cfg.$parent.parent().find('#controlbox-tabs');
  30317. },
  30318. render: function () {
  30319. var markup;
  30320. var widgets = converse.templates.contacts_panel({
  30321. label_online: __('Online'),
  30322. label_busy: __('Busy'),
  30323. label_away: __('Away'),
  30324. label_offline: __('Offline'),
  30325. label_logout: __('Log out'),
  30326. allow_logout: converse.allow_logout
  30327. });
  30328. this.$tabs.append(converse.templates.contacts_tab({label_contacts: LABEL_CONTACTS}));
  30329. if (converse.xhr_user_search) {
  30330. markup = converse.templates.search_contact({
  30331. label_contact_name: __('Contact name'),
  30332. label_search: __('Search')
  30333. });
  30334. } else {
  30335. markup = converse.templates.add_contact_form({
  30336. label_contact_username: __('Contact username'),
  30337. label_add: __('Add')
  30338. });
  30339. }
  30340. if (converse.allow_contact_requests) {
  30341. widgets += converse.templates.add_contact_dropdown({
  30342. label_click_to_chat: __('Click to add new chat contacts'),
  30343. label_add_contact: __('Add a contact')
  30344. });
  30345. }
  30346. this.$el.html(widgets);
  30347. this.$el.find('.search-xmpp ul').append(markup);
  30348. return this;
  30349. },
  30350. toggleContactForm: function (ev) {
  30351. ev.preventDefault();
  30352. this.$el.find('.search-xmpp').toggle('fast', function () {
  30353. if ($(this).is(':visible')) {
  30354. $(this).find('input.username').focus();
  30355. }
  30356. });
  30357. },
  30358. searchContacts: function (ev) {
  30359. ev.preventDefault();
  30360. $.getJSON(converse.xhr_user_search_url+ "?q=" + $(ev.target).find('input.username').val(), function (data) {
  30361. var $ul= $('.search-xmpp ul');
  30362. $ul.find('li.found-user').remove();
  30363. $ul.find('li.chat-info').remove();
  30364. if (!data.length) {
  30365. $ul.append('<li class="chat-info">'+__('No users found')+'</li>');
  30366. }
  30367. $(data).each(function (idx, obj) {
  30368. $ul.append(
  30369. $('<li class="found-user"></li>')
  30370. .append(
  30371. $('<a class="subscribe-to-user" href="#" title="'+__('Click to add as a chat contact')+'"></a>')
  30372. .attr('data-recipient', Strophe.escapeNode(obj.id)+'@'+converse.domain)
  30373. .text(obj.fullname)
  30374. )
  30375. );
  30376. });
  30377. });
  30378. },
  30379. addContactFromForm: function (ev) {
  30380. ev.preventDefault();
  30381. var $input = $(ev.target).find('input');
  30382. var jid = $input.val();
  30383. if (! jid) {
  30384. // this is not a valid JID
  30385. $input.addClass('error');
  30386. return;
  30387. }
  30388. this.addContact(jid);
  30389. $('.search-xmpp').hide();
  30390. },
  30391. addContactFromList: function (ev) {
  30392. ev.preventDefault();
  30393. var $target = $(ev.target),
  30394. jid = $target.attr('data-recipient'),
  30395. name = $target.text();
  30396. this.addContact(jid, name);
  30397. $target.parent().remove();
  30398. $('.search-xmpp').hide();
  30399. },
  30400. addContact: function (jid, name) {
  30401. name = _.isEmpty(name)? jid: name;
  30402. converse.connection.roster.add(jid, name, [], function (iq) {
  30403. converse.connection.roster.subscribe(jid, null, converse.xmppstatus.get('fullname'));
  30404. });
  30405. }
  30406. });
  30407. this.RoomsPanel = Backbone.View.extend({
  30408. tagName: 'div',
  30409. className: 'controlbox-pane',
  30410. id: 'chatrooms',
  30411. events: {
  30412. 'submit form.add-chatroom': 'createChatRoom',
  30413. 'click input#show-rooms': 'showRooms',
  30414. 'click a.open-room': 'createChatRoom',
  30415. 'click a.room-info': 'showRoomInfo',
  30416. 'change input[name=server]': 'setDomain',
  30417. 'change input[name=nick]': 'setNick'
  30418. },
  30419. initialize: function (cfg) {
  30420. this.$parent = cfg.$parent;
  30421. this.model.on('change:muc_domain', this.onDomainChange, this);
  30422. this.model.on('change:nick', this.onNickChange, this);
  30423. },
  30424. render: function () {
  30425. this.$parent.append(
  30426. this.$el.html(
  30427. converse.templates.room_panel({
  30428. 'server_input_type': converse.hide_muc_server && 'hidden' || 'text',
  30429. 'label_room_name': __('Room name'),
  30430. 'label_nickname': __('Nickname'),
  30431. 'label_server': __('Server'),
  30432. 'label_join': __('Join'),
  30433. 'label_show_rooms': __('Show rooms')
  30434. })
  30435. ).hide());
  30436. this.$tabs = this.$parent.parent().find('#controlbox-tabs');
  30437. this.$tabs.append(converse.templates.chatrooms_tab({label_rooms: __('Rooms')}));
  30438. return this;
  30439. },
  30440. onDomainChange: function (model) {
  30441. var $server = this.$el.find('input.new-chatroom-server');
  30442. $server.val(model.get('muc_domain'));
  30443. if (converse.auto_list_rooms) {
  30444. this.updateRoomsList();
  30445. }
  30446. },
  30447. onNickChange: function (model) {
  30448. var $nick = this.$el.find('input.new-chatroom-nick');
  30449. $nick.val(model.get('nick'));
  30450. },
  30451. informNoRoomsFound: function () {
  30452. var $available_chatrooms = this.$el.find('#available-chatrooms');
  30453. // # For translators: %1$s is a variable and will be replaced with the XMPP server name
  30454. $available_chatrooms.html('<dt>'+__('No rooms on %1$s',this.model.get('muc_domain'))+'</dt>');
  30455. $('input#show-rooms').show().siblings('span.spinner').remove();
  30456. },
  30457. updateRoomsList: function () {
  30458. converse.connection.muc.listRooms(
  30459. this.model.get('muc_domain'),
  30460. $.proxy(function (iq) { // Success
  30461. var name, jid, i, fragment,
  30462. that = this,
  30463. $available_chatrooms = this.$el.find('#available-chatrooms');
  30464. this.rooms = $(iq).find('query').find('item');
  30465. if (this.rooms.length) {
  30466. // # For translators: %1$s is a variable and will be
  30467. // # replaced with the XMPP server name
  30468. $available_chatrooms.html('<dt>'+__('Rooms on %1$s',this.model.get('muc_domain'))+'</dt>');
  30469. fragment = document.createDocumentFragment();
  30470. for (i=0; i<this.rooms.length; i++) {
  30471. name = Strophe.unescapeNode($(this.rooms[i]).attr('name')||$(this.rooms[i]).attr('jid'));
  30472. jid = $(this.rooms[i]).attr('jid');
  30473. fragment.appendChild($(
  30474. converse.templates.room_item({
  30475. 'name':name,
  30476. 'jid':jid,
  30477. 'open_title': __('Click to open this room'),
  30478. 'info_title': __('Show more information on this room')
  30479. })
  30480. )[0]);
  30481. }
  30482. $available_chatrooms.append(fragment);
  30483. $('input#show-rooms').show().siblings('span.spinner').remove();
  30484. } else {
  30485. this.informNoRoomsFound();
  30486. }
  30487. return true;
  30488. }, this),
  30489. $.proxy(function (iq) { // Failure
  30490. this.informNoRoomsFound();
  30491. }, this));
  30492. },
  30493. showRooms: function (ev) {
  30494. var $available_chatrooms = this.$el.find('#available-chatrooms');
  30495. var $server = this.$el.find('input.new-chatroom-server');
  30496. var server = $server.val();
  30497. if (!server) {
  30498. $server.addClass('error');
  30499. return;
  30500. }
  30501. this.$el.find('input.new-chatroom-name').removeClass('error');
  30502. $server.removeClass('error');
  30503. $available_chatrooms.empty();
  30504. $('input#show-rooms').hide().after('<span class="spinner"/>');
  30505. this.model.save({muc_domain: server});
  30506. this.updateRoomsList();
  30507. },
  30508. showRoomInfo: function (ev) {
  30509. var target = ev.target,
  30510. $dd = $(target).parent('dd'),
  30511. $div = $dd.find('div.room-info');
  30512. if ($div.length) {
  30513. $div.remove();
  30514. } else {
  30515. $dd.find('span.spinner').remove();
  30516. $dd.append('<span class="spinner hor_centered"/>');
  30517. converse.connection.disco.info(
  30518. $(target).attr('data-room-jid'),
  30519. null,
  30520. $.proxy(function (stanza) {
  30521. var $stanza = $(stanza);
  30522. // All MUC features found here: http://xmpp.org/registrar/disco-features.html
  30523. $dd.find('span.spinner').replaceWith(
  30524. converse.templates.room_description({
  30525. 'desc': $stanza.find('field[var="muc#roominfo_description"] value').text(),
  30526. 'occ': $stanza.find('field[var="muc#roominfo_occupants"] value').text(),
  30527. 'hidden': $stanza.find('feature[var="muc_hidden"]').length,
  30528. 'membersonly': $stanza.find('feature[var="muc_membersonly"]').length,
  30529. 'moderated': $stanza.find('feature[var="muc_moderated"]').length,
  30530. 'nonanonymous': $stanza.find('feature[var="muc_nonanonymous"]').length,
  30531. 'open': $stanza.find('feature[var="muc_open"]').length,
  30532. 'passwordprotected': $stanza.find('feature[var="muc_passwordprotected"]').length,
  30533. 'persistent': $stanza.find('feature[var="muc_persistent"]').length,
  30534. 'publicroom': $stanza.find('feature[var="muc_public"]').length,
  30535. 'semianonymous': $stanza.find('feature[var="muc_semianonymous"]').length,
  30536. 'temporary': $stanza.find('feature[var="muc_temporary"]').length,
  30537. 'unmoderated': $stanza.find('feature[var="muc_unmoderated"]').length,
  30538. 'label_desc': __('Description:'),
  30539. 'label_occ': __('Occupants:'),
  30540. 'label_features': __('Features:'),
  30541. 'label_requires_auth': __('Requires authentication'),
  30542. 'label_hidden': __('Hidden'),
  30543. 'label_requires_invite': __('Requires an invitation'),
  30544. 'label_moderated': __('Moderated'),
  30545. 'label_non_anon': __('Non-anonymous'),
  30546. 'label_open_room': __('Open room'),
  30547. 'label_permanent_room': __('Permanent room'),
  30548. 'label_public': __('Public'),
  30549. 'label_semi_anon': _('Semi-anonymous'),
  30550. 'label_temp_room': _('Temporary room'),
  30551. 'label_unmoderated': __('Unmoderated')
  30552. }));
  30553. }, this));
  30554. }
  30555. },
  30556. createChatRoom: function (ev) {
  30557. ev.preventDefault();
  30558. var name, $name,
  30559. server, $server,
  30560. jid,
  30561. $nick = this.$el.find('input.new-chatroom-nick'),
  30562. nick = $nick.val(),
  30563. chatroom;
  30564. if (!nick) { $nick.addClass('error'); }
  30565. else { $nick.removeClass('error'); }
  30566. if (ev.type === 'click') {
  30567. jid = $(ev.target).attr('data-room-jid');
  30568. } else {
  30569. $name = this.$el.find('input.new-chatroom-name');
  30570. $server= this.$el.find('input.new-chatroom-server');
  30571. server = $server.val();
  30572. name = $name.val().trim().toLowerCase();
  30573. $name.val(''); // Clear the input
  30574. if (name && server) {
  30575. jid = Strophe.escapeNode(name) + '@' + server;
  30576. $name.removeClass('error');
  30577. $server.removeClass('error');
  30578. this.model.save({muc_domain: server});
  30579. } else {
  30580. if (!name) { $name.addClass('error'); }
  30581. if (!server) { $server.addClass('error'); }
  30582. return;
  30583. }
  30584. }
  30585. if (!nick) { return; }
  30586. chatroom = converse.chatboxviews.showChat({
  30587. 'id': jid,
  30588. 'jid': jid,
  30589. 'name': Strophe.unescapeNode(Strophe.getNodeFromJid(jid)),
  30590. 'nick': nick,
  30591. 'chatroom': true,
  30592. 'box_id' : b64_sha1(jid)
  30593. });
  30594. },
  30595. setDomain: function (ev) {
  30596. this.model.save({muc_domain: ev.target.value});
  30597. },
  30598. setNick: function (ev) {
  30599. this.model.save({nick: ev.target.value});
  30600. }
  30601. });
  30602. this.ControlBoxView = converse.ChatBoxView.extend({
  30603. tagName: 'div',
  30604. className: 'chatbox',
  30605. id: 'controlbox',
  30606. events: {
  30607. 'click a.close-chatbox-button': 'close',
  30608. 'click ul#controlbox-tabs li a': 'switchTab',
  30609. 'mousedown .dragresize-tm': 'onDragResizeStart'
  30610. },
  30611. initialize: function () {
  30612. this.$el.insertAfter(converse.controlboxtoggle.$el);
  30613. this.model.on('change:connected', this.onConnected, this);
  30614. this.model.on('destroy', this.hide, this);
  30615. this.model.on('hide', this.hide, this);
  30616. this.model.on('show', this.show, this);
  30617. this.model.on('change:closed', this.ensureClosedState, this);
  30618. this.render();
  30619. if (this.model.get('connected')) {
  30620. this.initRoster();
  30621. }
  30622. if (!this.model.get('closed')) {
  30623. this.show();
  30624. } else {
  30625. this.hide();
  30626. }
  30627. },
  30628. giveFeedback: function (message, klass) {
  30629. var $el = this.$('.conn-feedback');
  30630. $el.addClass('conn-feedback').text(message);
  30631. if (klass) {
  30632. $el.addClass(klass);
  30633. }
  30634. },
  30635. onConnected: function () {
  30636. if (this.model.get('connected')) {
  30637. this.render().initRoster();
  30638. converse.features.off('add', this.featureAdded, this);
  30639. converse.features.on('add', this.featureAdded, this);
  30640. // Features could have been added before the controlbox was
  30641. // initialized. Currently we're only interested in MUC
  30642. var feature = converse.features.findWhere({'var': 'http://jabber.org/protocol/muc'});
  30643. if (feature) {
  30644. this.featureAdded(feature);
  30645. }
  30646. }
  30647. },
  30648. initRoster: function () {
  30649. /* We initialize the roster, which will appear inside the
  30650. * Contacts Panel.
  30651. */
  30652. converse.roster = new converse.RosterContacts();
  30653. converse.roster.browserStorage = new Backbone.BrowserStorage[converse.storage](
  30654. b64_sha1('converse.contacts-'+converse.bare_jid));
  30655. var rostergroups = new converse.RosterGroups();
  30656. rostergroups.browserStorage = new Backbone.BrowserStorage[converse.storage](
  30657. b64_sha1('converse.roster.groups'+converse.bare_jid));
  30658. converse.rosterview = new converse.RosterView({model: rostergroups});
  30659. this.contactspanel.$el.append(converse.rosterview.$el);
  30660. converse.rosterview.render().fetch().update();
  30661. return this;
  30662. },
  30663. render: function () {
  30664. if (!converse.connection.connected || !converse.connection.authenticated || converse.connection.disconnecting) {
  30665. // TODO: we might need to take prebinding into consideration here.
  30666. this.renderLoginPanel();
  30667. } else if (!this.contactspanel || !this.contactspanel.$el.is(':visible')) {
  30668. this.renderContactsPanel();
  30669. }
  30670. return this;
  30671. },
  30672. renderLoginPanel: function () {
  30673. var $feedback = this.$('.conn-feedback'); // we want to still show any existing feedback.
  30674. this.$el.html(converse.templates.controlbox(this.model.toJSON()));
  30675. var cfg = {'$parent': this.$el.find('.controlbox-panes'), 'model': this};
  30676. if (!this.loginpanel) {
  30677. this.loginpanel = new converse.LoginPanel(cfg);
  30678. if (converse.allow_registration) {
  30679. this.registerpanel = new converse.RegisterPanel(cfg);
  30680. }
  30681. } else {
  30682. this.loginpanel.delegateEvents().initialize(cfg);
  30683. if (converse.allow_registration) {
  30684. this.registerpanel.delegateEvents().initialize(cfg);
  30685. }
  30686. }
  30687. this.loginpanel.render();
  30688. if (converse.allow_registration) {
  30689. this.registerpanel.render().$el.hide();
  30690. }
  30691. this.initDragResize();
  30692. if ($feedback.length) {
  30693. this.$('.conn-feedback').replaceWith($feedback);
  30694. }
  30695. return this;
  30696. },
  30697. renderContactsPanel: function () {
  30698. var model;
  30699. this.$el.html(converse.templates.controlbox(this.model.toJSON()));
  30700. this.contactspanel = new converse.ContactsPanel({'$parent': this.$el.find('.controlbox-panes')});
  30701. this.contactspanel.render();
  30702. converse.xmppstatusview = new converse.XMPPStatusView({'model': converse.xmppstatus});
  30703. converse.xmppstatusview.render();
  30704. if (converse.allow_muc) {
  30705. this.roomspanel = new converse.RoomsPanel({
  30706. '$parent': this.$el.find('.controlbox-panes'),
  30707. 'model': new (Backbone.Model.extend({
  30708. id: b64_sha1('converse.roomspanel'+converse.bare_jid), // Required by sessionStorage
  30709. browserStorage: new Backbone.BrowserStorage[converse.storage](
  30710. b64_sha1('converse.roomspanel'+converse.bare_jid))
  30711. }))()
  30712. });
  30713. this.roomspanel.render().model.fetch();
  30714. if (!this.roomspanel.model.get('nick')) {
  30715. this.roomspanel.model.save({nick: Strophe.getNodeFromJid(converse.bare_jid)});
  30716. }
  30717. }
  30718. this.initDragResize();
  30719. },
  30720. close: function (ev) {
  30721. if (ev && ev.preventDefault) { ev.preventDefault(); }
  30722. if (converse.connection.connected) {
  30723. this.model.save({'closed': true});
  30724. } else {
  30725. this.model.trigger('hide');
  30726. }
  30727. converse.emit('controlBoxClosed', this);
  30728. return this;
  30729. },
  30730. ensureClosedState: function () {
  30731. if (this.model.get('closed')) {
  30732. this.hide();
  30733. } else {
  30734. this.show();
  30735. }
  30736. },
  30737. hide: function (callback) {
  30738. this.$el.hide('fast', function () {
  30739. converse.refreshWebkit();
  30740. converse.emit('chatBoxClosed', this);
  30741. converse.controlboxtoggle.show(function () {
  30742. if (typeof callback === "function") {
  30743. callback();
  30744. }
  30745. });
  30746. });
  30747. return this;
  30748. },
  30749. show: function () {
  30750. converse.controlboxtoggle.hide($.proxy(function () {
  30751. this.$el.show('fast', function () {
  30752. if (converse.rosterview) {
  30753. converse.rosterview.update();
  30754. }
  30755. converse.refreshWebkit();
  30756. }.bind(this));
  30757. converse.emit('controlBoxOpened', this);
  30758. }, this));
  30759. return this;
  30760. },
  30761. featureAdded: function (feature) {
  30762. if ((feature.get('var') == 'http://jabber.org/protocol/muc') && (converse.allow_muc)) {
  30763. this.roomspanel.model.save({muc_domain: feature.get('from')});
  30764. var $server= this.$el.find('input.new-chatroom-server');
  30765. if (! $server.is(':focus')) {
  30766. $server.val(this.roomspanel.model.get('muc_domain'));
  30767. }
  30768. }
  30769. },
  30770. switchTab: function (ev) {
  30771. // TODO: automatically focus the relevant input
  30772. if (ev && ev.preventDefault) { ev.preventDefault(); }
  30773. var $tab = $(ev.target),
  30774. $sibling = $tab.parent().siblings('li').children('a'),
  30775. $tab_panel = $($tab.attr('href'));
  30776. $($sibling.attr('href')).hide();
  30777. $sibling.removeClass('current');
  30778. $tab.addClass('current');
  30779. $tab_panel.show();
  30780. return this;
  30781. },
  30782. showHelpMessages: function (msgs) {
  30783. // Override showHelpMessages in ChatBoxView, for now do nothing.
  30784. return;
  30785. }
  30786. });
  30787. this.ChatRoomOccupant = Backbone.Model;
  30788. this.ChatRoomOccupantView = Backbone.View.extend({
  30789. tagName: 'li',
  30790. initialize: function () {
  30791. this.model.on('change', this.render, this);
  30792. this.model.on('destroy', this.destroy, this);
  30793. },
  30794. render: function () {
  30795. var $new = converse.templates.occupant(
  30796. _.extend(
  30797. this.model.toJSON(), {
  30798. 'desc_moderator': __('This user is a moderator'),
  30799. 'desc_participant': __('This user can send messages in this room'),
  30800. 'desc_visitor': __('This user can NOT send messages in this room')
  30801. })
  30802. );
  30803. this.$el.replaceWith($new);
  30804. this.setElement($new, true);
  30805. return this;
  30806. },
  30807. destroy: function () {
  30808. this.$el.remove();
  30809. }
  30810. });
  30811. this.ChatRoomOccupants = Backbone.Collection.extend({
  30812. model: converse.ChatRoomOccupant,
  30813. initialize: function (options) {
  30814. this.browserStorage = new Backbone.BrowserStorage[converse.storage](
  30815. b64_sha1('converse.occupants'+converse.bare_jid+options.nick));
  30816. }
  30817. });
  30818. this.ChatRoomOccupantsView = Backbone.Overview.extend({
  30819. tagName: 'div',
  30820. className: 'participants',
  30821. initialize: function () {
  30822. this.model.on("add", this.onOccupantAdded, this);
  30823. },
  30824. render: function () {
  30825. this.$el.html(
  30826. converse.templates.chatroom_sidebar({
  30827. 'label_invitation': __('Invite...'),
  30828. 'label_occupants': __('Occupants')
  30829. })
  30830. );
  30831. return this.initInviteWidget();
  30832. },
  30833. onOccupantAdded: function (item) {
  30834. var view = this.get(item.get('id'));
  30835. if (!view) {
  30836. view = this.add(item.get('id'), new converse.ChatRoomOccupantView({model: item}));
  30837. } else {
  30838. delete view.model; // Remove ref to old model to help garbage collection
  30839. view.model = item;
  30840. view.initialize();
  30841. }
  30842. this.$('.participant-list').append(view.render().$el);
  30843. },
  30844. onChatRoomRoster: function (roster, room) {
  30845. var roster_size = _.size(roster),
  30846. $participant_list = this.$('.participant-list'),
  30847. participants = [],
  30848. keys = _.keys(roster),
  30849. occupant, attrs, i, nick;
  30850. for (i=0; i<roster_size; i++) {
  30851. nick = Strophe.unescapeNode(keys[i]);
  30852. attrs = {
  30853. 'id': nick,
  30854. 'role': roster[keys[i]].role,
  30855. 'nick': nick
  30856. };
  30857. occupant = this.model.get(nick);
  30858. if (occupant) {
  30859. occupant.save(attrs);
  30860. } else {
  30861. this.model.create(attrs);
  30862. }
  30863. }
  30864. _.each(_.difference(this.model.pluck('id'), keys), function (id) {
  30865. this.model.get(id).destroy();
  30866. }, this);
  30867. return true;
  30868. },
  30869. initInviteWidget: function () {
  30870. var $el = this.$('input.invited-contact');
  30871. $el.typeahead({
  30872. minLength: 1,
  30873. highlight: true
  30874. }, {
  30875. name: 'contacts-dataset',
  30876. source: function (q, cb) {
  30877. var results = [];
  30878. _.each(converse.roster.filter(contains(['fullname', 'jid'], q)), function (n) {
  30879. results.push({value: n.get('fullname'), jid: n.get('jid')});
  30880. });
  30881. cb(results);
  30882. },
  30883. templates: {
  30884. suggestion: _.template('<p data-jid="{{jid}}">{{value}}</p>')
  30885. }
  30886. });
  30887. $el.on('typeahead:selected', $.proxy(function (ev, suggestion, dname) {
  30888. var reason = prompt(
  30889. __(___('You are about to invite %1$s to the chat room "%2$s". '), suggestion.value, this.model.get('id')) +
  30890. __("You may optionally include a message, explaining the reason for the invitation.")
  30891. );
  30892. if (reason !== null) {
  30893. converse.connection.muc.rooms[this.chatroomview.model.get('id')].directInvite(suggestion.jid, reason);
  30894. converse.emit('roomInviteSent', this, suggestion.jid, reason);
  30895. }
  30896. $(ev.target).typeahead('val', '');
  30897. }, this));
  30898. return this;
  30899. }
  30900. });
  30901. this.ChatRoomView = converse.ChatBoxView.extend({
  30902. length: 300,
  30903. tagName: 'div',
  30904. className: 'chatroom',
  30905. events: {
  30906. 'click .close-chatbox-button': 'close',
  30907. 'click .toggle-chatbox-button': 'minimize',
  30908. 'click .configure-chatroom-button': 'configureChatRoom',
  30909. 'click .toggle-smiley': 'toggleEmoticonMenu',
  30910. 'click .toggle-smiley ul li': 'insertEmoticon',
  30911. 'click .toggle-clear': 'clearChatRoomMessages',
  30912. 'click .toggle-participants a': 'toggleOccupants',
  30913. 'keypress textarea.chat-textarea': 'keyPressed',
  30914. 'mousedown .dragresize-tm': 'onDragResizeStart'
  30915. },
  30916. is_chatroom: true,
  30917. initialize: function () {
  30918. this.model.messages.on('add', this.onMessageAdded, this);
  30919. this.model.on('change:minimized', function (item) {
  30920. if (item.get('minimized')) {
  30921. this.hide();
  30922. } else {
  30923. this.maximize();
  30924. }
  30925. }, this);
  30926. this.model.on('destroy', function (model, response, options) {
  30927. this.hide();
  30928. converse.connection.muc.leave(
  30929. this.model.get('jid'),
  30930. this.model.get('nick'),
  30931. $.proxy(this.onLeave, this),
  30932. undefined);
  30933. },
  30934. this);
  30935. this.occupantsview = new converse.ChatRoomOccupantsView({
  30936. model: new converse.ChatRoomOccupants({nick: this.model.get('nick')})
  30937. });
  30938. this.occupantsview.chatroomview = this;
  30939. this.render();
  30940. this.occupantsview.model.fetch({add:true});
  30941. this.connect(null);
  30942. converse.emit('chatRoomOpened', this);
  30943. this.$el.insertAfter(converse.chatboxviews.get("controlbox").$el);
  30944. this.model.messages.fetch({add: true});
  30945. if (this.model.get('minimized')) {
  30946. this.hide();
  30947. } else {
  30948. this.show();
  30949. }
  30950. },
  30951. render: function () {
  30952. this.$el.attr('id', this.model.get('box_id'))
  30953. .html(converse.templates.chatroom(this.model.toJSON()));
  30954. this.renderChatArea();
  30955. setTimeout(function () {
  30956. converse.refreshWebkit();
  30957. }, 50);
  30958. return this;
  30959. },
  30960. renderChatArea: function () {
  30961. if (!this.$('.chat-area').length) {
  30962. this.$('.chat-body').empty()
  30963. .append(
  30964. converse.templates.chatarea({
  30965. 'show_toolbar': converse.show_toolbar,
  30966. 'label_message': __('Message')
  30967. }))
  30968. .append(this.occupantsview.render().$el);
  30969. this.renderToolbar();
  30970. }
  30971. // XXX: This is a bit of a hack, to make sure that the
  30972. // sidebar's state is remembered.
  30973. this.model.set({hidden_occupants: !this.model.get('hidden_occupants')});
  30974. this.toggleOccupants();
  30975. return this;
  30976. },
  30977. toggleOccupants: function (ev) {
  30978. if (ev) {
  30979. ev.preventDefault();
  30980. ev.stopPropagation();
  30981. }
  30982. var $el = this.$('.icon-hide-users');
  30983. if (!this.model.get('hidden_occupants')) {
  30984. this.model.save({hidden_occupants: true});
  30985. $el.removeClass('icon-hide-users').addClass('icon-show-users');
  30986. this.$('form.sendXMPPMessage, .chat-area').animate({width: '100%'});
  30987. this.$('div.participants').animate({width: 0}, $.proxy(function () {
  30988. this.scrollDown();
  30989. }, this));
  30990. } else {
  30991. this.model.save({hidden_occupants: false});
  30992. $el.removeClass('icon-show-users').addClass('icon-hide-users');
  30993. this.$('.chat-area, form.sendXMPPMessage').css({width: ''});
  30994. this.$('div.participants').show().animate({width: 'auto'}, $.proxy(function () {
  30995. this.scrollDown();
  30996. }, this));
  30997. }
  30998. },
  30999. onCommandError: function (stanza) {
  31000. this.showStatusNotification(__("Error: could not execute the command"), true);
  31001. },
  31002. createChatRoomMessage: function (text) {
  31003. var fullname = converse.xmppstatus.get('fullname');
  31004. this.model.messages.create({
  31005. fullname: _.isEmpty(fullname)? converse.bare_jid: fullname,
  31006. sender: 'me',
  31007. time: moment().format(),
  31008. message: text,
  31009. msgid: converse.connection.muc.groupchat(this.model.get('jid'), text, undefined, String((new Date()).getTime()))
  31010. });
  31011. },
  31012. sendChatRoomMessage: function (text) {
  31013. var match = text.replace(/^\s*/, "").match(/^\/(.*?)(?: (.*))?$/) || [false], args;
  31014. switch (match[1]) {
  31015. case 'ban':
  31016. args = match[2].splitOnce(' ');
  31017. converse.connection.muc.ban(this.model.get('jid'), args[0], args[1], undefined, $.proxy(this.onCommandError, this));
  31018. break;
  31019. case 'clear':
  31020. this.clearChatRoomMessages();
  31021. break;
  31022. case 'deop':
  31023. args = match[2].splitOnce(' ');
  31024. converse.connection.muc.deop(this.model.get('jid'), args[0], args[1], undefined, $.proxy(this.onCommandError, this));
  31025. break;
  31026. case 'help':
  31027. this.showHelpMessages([
  31028. '<strong>/ban</strong>: ' +__('Ban user from room'),
  31029. '<strong>/clear</strong>: ' +__('Remove messages'),
  31030. '<strong>/help</strong>: ' +__('Show this menu'),
  31031. '<strong>/kick</strong>: ' +__('Kick user from room'),
  31032. '<strong>/me</strong>: ' +__('Write in 3rd person'),
  31033. '<strong>/mute</strong>: ' +__("Remove user's ability to post messages"),
  31034. '<strong>/nick</strong>: ' +__('Change your nickname'),
  31035. '<strong>/topic</strong>: ' +__('Set room topic'),
  31036. '<strong>/voice</strong>: ' +__('Allow muted user to post messages')
  31037. ]);
  31038. break;
  31039. case 'kick':
  31040. args = match[2].splitOnce(' ');
  31041. converse.connection.muc.kick(this.model.get('jid'), args[0], args[1], undefined, $.proxy(this.onCommandError, this));
  31042. break;
  31043. case 'mute':
  31044. args = match[2].splitOnce(' ');
  31045. converse.connection.muc.mute(this.model.get('jid'), args[0], args[1], undefined, $.proxy(this.onCommandError, this));
  31046. break;
  31047. case 'nick':
  31048. converse.connection.muc.changeNick(this.model.get('jid'), match[2]);
  31049. break;
  31050. case 'op':
  31051. args = match[2].splitOnce(' ');
  31052. converse.connection.muc.op(this.model.get('jid'), args[0], args[1], undefined, $.proxy(this.onCommandError, this));
  31053. break;
  31054. case 'topic':
  31055. converse.connection.muc.setTopic(this.model.get('jid'), match[2]);
  31056. break;
  31057. case 'voice':
  31058. args = match[2].splitOnce(' ');
  31059. converse.connection.muc.voice(this.model.get('jid'), args[0], args[1], undefined, $.proxy(this.onCommandError, this));
  31060. break;
  31061. default:
  31062. this.createChatRoomMessage(text);
  31063. break;
  31064. }
  31065. },
  31066. connect: function (password) {
  31067. if (_.has(converse.connection.muc.rooms, this.model.get('jid'))) {
  31068. // If the room exists, it already has event listeners, so we
  31069. // don't add them again.
  31070. converse.connection.muc.join(
  31071. this.model.get('jid'), this.model.get('nick'), null, null, null, password);
  31072. } else {
  31073. converse.connection.muc.join(
  31074. this.model.get('jid'),
  31075. this.model.get('nick'),
  31076. $.proxy(this.onChatRoomMessage, this),
  31077. $.proxy(this.onChatRoomPresence, this),
  31078. $.proxy(this.onChatRoomRoster, this),
  31079. password);
  31080. }
  31081. },
  31082. onLeave: function () {
  31083. this.model.set('connected', false);
  31084. },
  31085. renderConfigurationForm: function (stanza) {
  31086. var $form= this.$el.find('form.chatroom-form'),
  31087. $stanza = $(stanza),
  31088. $fields = $stanza.find('field'),
  31089. title = $stanza.find('title').text(),
  31090. instructions = $stanza.find('instructions').text();
  31091. $form.find('span.spinner').remove();
  31092. $form.append($('<legend>').text(title));
  31093. if (instructions && instructions != title) {
  31094. $form.append($('<p class="instructions">').text(instructions));
  31095. }
  31096. _.each($fields, function (field) {
  31097. $form.append(utils.xForm2webForm($(field), $stanza));
  31098. });
  31099. $form.append('<input type="submit" class="save-submit" value="'+__('Save')+'"/>');
  31100. $form.append('<input type="button" class="cancel-submit" value="'+__('Cancel')+'"/>');
  31101. $form.on('submit', $.proxy(this.saveConfiguration, this));
  31102. $form.find('input[type=button]').on('click', $.proxy(this.cancelConfiguration, this));
  31103. },
  31104. saveConfiguration: function (ev) {
  31105. ev.preventDefault();
  31106. var that = this;
  31107. var $inputs = $(ev.target).find(':input:not([type=button]):not([type=submit])'),
  31108. count = $inputs.length,
  31109. configArray = [];
  31110. $inputs.each(function () {
  31111. configArray.push(utils.webForm2xForm(this));
  31112. if (!--count) {
  31113. converse.connection.muc.saveConfiguration(
  31114. that.model.get('jid'),
  31115. configArray,
  31116. $.proxy(that.onConfigSaved, that),
  31117. $.proxy(that.onErrorConfigSaved, that)
  31118. );
  31119. }
  31120. });
  31121. this.$el.find('div.chatroom-form-container').hide(
  31122. function () {
  31123. $(this).remove();
  31124. that.$el.find('.chat-area').show();
  31125. that.$el.find('.participants').show();
  31126. });
  31127. },
  31128. onConfigSaved: function (stanza) {
  31129. // TODO: provide feedback
  31130. },
  31131. onErrorConfigSaved: function (stanza) {
  31132. this.showStatusNotification(__("An error occurred while trying to save the form."));
  31133. },
  31134. cancelConfiguration: function (ev) {
  31135. ev.preventDefault();
  31136. var that = this;
  31137. this.$el.find('div.chatroom-form-container').hide(
  31138. function () {
  31139. $(this).remove();
  31140. that.$el.find('.chat-area').show();
  31141. that.$el.find('.participants').show();
  31142. });
  31143. },
  31144. configureChatRoom: function (ev) {
  31145. ev.preventDefault();
  31146. if (this.$el.find('div.chatroom-form-container').length) {
  31147. return;
  31148. }
  31149. this.$('.chat-body').children().hide();
  31150. this.$('.chat-body').append(
  31151. $('<div class="chatroom-form-container">'+
  31152. '<form class="chatroom-form">'+
  31153. '<span class="spinner centered"/>'+
  31154. '</form>'+
  31155. '</div>'));
  31156. converse.connection.muc.configure(
  31157. this.model.get('jid'),
  31158. $.proxy(this.renderConfigurationForm, this)
  31159. );
  31160. },
  31161. submitPassword: function (ev) {
  31162. ev.preventDefault();
  31163. var password = this.$el.find('.chatroom-form').find('input[type=password]').val();
  31164. this.$el.find('.chatroom-form-container').replaceWith('<span class="spinner centered"/>');
  31165. this.connect(password);
  31166. },
  31167. renderPasswordForm: function () {
  31168. this.$('.chat-body').children().hide();
  31169. this.$('span.centered.spinner').remove();
  31170. this.$('.chat-body').append(
  31171. converse.templates.chatroom_password_form({
  31172. heading: __('This chatroom requires a password'),
  31173. label_password: __('Password: '),
  31174. label_submit: __('Submit')
  31175. }));
  31176. this.$('.chatroom-form').on('submit', $.proxy(this.submitPassword, this));
  31177. },
  31178. showDisconnectMessage: function (msg) {
  31179. this.$('.chat-area').hide();
  31180. this.$('.participants').hide();
  31181. this.$('span.centered.spinner').remove();
  31182. this.$('.chat-body').append($('<p>'+msg+'</p>'));
  31183. },
  31184. /* http://xmpp.org/extensions/xep-0045.html
  31185. * ----------------------------------------
  31186. * 100 message Entering a room Inform user that any occupant is allowed to see the user's full JID
  31187. * 101 message (out of band) Affiliation change Inform user that his or her affiliation changed while not in the room
  31188. * 102 message Configuration change Inform occupants that room now shows unavailable members
  31189. * 103 message Configuration change Inform occupants that room now does not show unavailable members
  31190. * 104 message Configuration change Inform occupants that a non-privacy-related room configuration change has occurred
  31191. * 110 presence Any room presence Inform user that presence refers to one of its own room occupants
  31192. * 170 message or initial presence Configuration change Inform occupants that room logging is now enabled
  31193. * 171 message Configuration change Inform occupants that room logging is now disabled
  31194. * 172 message Configuration change Inform occupants that the room is now non-anonymous
  31195. * 173 message Configuration change Inform occupants that the room is now semi-anonymous
  31196. * 174 message Configuration change Inform occupants that the room is now fully-anonymous
  31197. * 201 presence Entering a room Inform user that a new room has been created
  31198. * 210 presence Entering a room Inform user that the service has assigned or modified the occupant's roomnick
  31199. * 301 presence Removal from room Inform user that he or she has been banned from the room
  31200. * 303 presence Exiting a room Inform all occupants of new room nickname
  31201. * 307 presence Removal from room Inform user that he or she has been kicked from the room
  31202. * 321 presence Removal from room Inform user that he or she is being removed from the room because of an affiliation change
  31203. * 322 presence Removal from room Inform user that he or she is being removed from the room because the room has been changed to members-only and the user is not a member
  31204. * 332 presence Removal from room Inform user that he or she is being removed from the room because of a system shutdown
  31205. */
  31206. infoMessages: {
  31207. 100: __('This room is not anonymous'),
  31208. 102: __('This room now shows unavailable members'),
  31209. 103: __('This room does not show unavailable members'),
  31210. 104: __('Non-privacy-related room configuration has changed'),
  31211. 170: __('Room logging is now enabled'),
  31212. 171: __('Room logging is now disabled'),
  31213. 172: __('This room is now non-anonymous'),
  31214. 173: __('This room is now semi-anonymous'),
  31215. 174: __('This room is now fully-anonymous'),
  31216. 201: __('A new room has been created')
  31217. },
  31218. disconnectMessages: {
  31219. 301: __('You have been banned from this room'),
  31220. 307: __('You have been kicked from this room'),
  31221. 321: __("You have been removed from this room because of an affiliation change"),
  31222. 322: __("You have been removed from this room because the room has changed to members-only and you're not a member"),
  31223. 332: __("You have been removed from this room because the MUC (Multi-user chat) service is being shut down.")
  31224. },
  31225. actionInfoMessages: {
  31226. /* XXX: Note the triple underscore function and not double
  31227. * underscore.
  31228. *
  31229. * This is a hack. We can't pass the strings to __ because we
  31230. * don't yet know what the variable to interpolate is.
  31231. *
  31232. * Triple underscore will just return the string again, but we
  31233. * can then at least tell gettext to scan for it so that these
  31234. * strings are picked up by the translation machinery.
  31235. */
  31236. 301: ___("<strong>%1$s</strong> has been banned"),
  31237. 303: ___("<strong>%1$s</strong>'s nickname has changed"),
  31238. 307: ___("<strong>%1$s</strong> has been kicked out"),
  31239. 321: ___("<strong>%1$s</strong> has been removed because of an affiliation change"),
  31240. 322: ___("<strong>%1$s</strong> has been removed for not being a member")
  31241. },
  31242. newNicknameMessages: {
  31243. 210: ___('Your nickname has been automatically changed to: <strong>%1$s</strong>'),
  31244. 303: ___('Your nickname has been changed to: <strong>%1$s</strong>')
  31245. },
  31246. showStatusMessages: function ($el, is_self) {
  31247. /* Check for status codes and communicate their purpose to the user.
  31248. * Allow user to configure chat room if they are the owner.
  31249. * See: http://xmpp.org/registrar/mucstatus.html
  31250. */
  31251. var $chat_content,
  31252. disconnect_msgs = [],
  31253. msgs = [],
  31254. reasons = [];
  31255. $el.find('x[xmlns="'+Strophe.NS.MUC_USER+'"]').each($.proxy(function (idx, x) {
  31256. var $item = $(x).find('item');
  31257. if (Strophe.getBareJidFromJid($item.attr('jid')) === converse.bare_jid && $item.attr('affiliation') === 'owner') {
  31258. this.$el.find('a.configure-chatroom-button').show();
  31259. }
  31260. $(x).find('item reason').each(function (idx, reason) {
  31261. if ($(reason).text()) {
  31262. reasons.push($(reason).text());
  31263. }
  31264. });
  31265. $(x).find('status').each($.proxy(function (idx, stat) {
  31266. var code = stat.getAttribute('code');
  31267. if (is_self && _.contains(_.keys(this.newNicknameMessages), code)) {
  31268. this.model.save({'nick': Strophe.getResourceFromJid($el.attr('from'))});
  31269. msgs.push(__(this.newNicknameMessages[code], $item.attr('nick')));
  31270. } else if (is_self && _.contains(_.keys(this.disconnectMessages), code)) {
  31271. disconnect_msgs.push(this.disconnectMessages[code]);
  31272. } else if (!is_self && _.contains(_.keys(this.actionInfoMessages), code)) {
  31273. msgs.push(
  31274. __(this.actionInfoMessages[code], Strophe.unescapeNode(Strophe.getResourceFromJid($el.attr('from'))))
  31275. );
  31276. } else if (_.contains(_.keys(this.infoMessages), code)) {
  31277. msgs.push(this.infoMessages[code]);
  31278. } else if (code !== '110') {
  31279. if ($(stat).text()) {
  31280. msgs.push($(stat).text()); // Sometimes the status contains human readable text and not a code.
  31281. }
  31282. }
  31283. }, this));
  31284. }, this));
  31285. if (disconnect_msgs.length > 0) {
  31286. for (i=0; i<disconnect_msgs.length; i++) {
  31287. this.showDisconnectMessage(disconnect_msgs[i]);
  31288. }
  31289. for (i=0; i<reasons.length; i++) {
  31290. this.showDisconnectMessage(__('The reason given is: "'+reasons[i]+'"'), true);
  31291. }
  31292. this.model.set('connected', false);
  31293. return;
  31294. }
  31295. $chat_content = this.$el.find('.chat-content');
  31296. for (i=0; i<msgs.length; i++) {
  31297. $chat_content.append(converse.templates.info({message: msgs[i]}));
  31298. }
  31299. for (i=0; i<reasons.length; i++) {
  31300. this.showStatusNotification(__('The reason given is: "'+reasons[i]+'"'), true);
  31301. }
  31302. return this.scrollDown();
  31303. },
  31304. showErrorMessage: function ($error, room) {
  31305. // We didn't enter the room, so we must remove it from the MUC
  31306. // add-on
  31307. delete converse.connection.muc[room.name];
  31308. if ($error.attr('type') == 'auth') {
  31309. if ($error.find('not-authorized').length) {
  31310. this.renderPasswordForm();
  31311. } else if ($error.find('registration-required').length) {
  31312. this.showDisconnectMessage(__('You are not on the member list of this room'));
  31313. } else if ($error.find('forbidden').length) {
  31314. this.showDisconnectMessage(__('You have been banned from this room'));
  31315. }
  31316. } else if ($error.attr('type') == 'modify') {
  31317. if ($error.find('jid-malformed').length) {
  31318. this.showDisconnectMessage(__('No nickname was specified'));
  31319. }
  31320. } else if ($error.attr('type') == 'cancel') {
  31321. if ($error.find('not-allowed').length) {
  31322. this.showDisconnectMessage(__('You are not allowed to create new rooms'));
  31323. } else if ($error.find('not-acceptable').length) {
  31324. this.showDisconnectMessage(__("Your nickname doesn't conform to this room's policies"));
  31325. } else if ($error.find('conflict').length) {
  31326. // TODO: give user the option of choosing a different
  31327. // nickname
  31328. this.showDisconnectMessage(__("Your nickname is already taken"));
  31329. } else if ($error.find('item-not-found').length) {
  31330. this.showDisconnectMessage(__("This room does not (yet) exist"));
  31331. } else if ($error.find('service-unavailable').length) {
  31332. this.showDisconnectMessage(__("This room has reached it's maximum number of occupants"));
  31333. }
  31334. }
  31335. },
  31336. onChatRoomPresence: function (presence, room) {
  31337. var $presence = $(presence), is_self;
  31338. if ($presence.attr('type') === 'error') {
  31339. this.model.set('connected', false);
  31340. this.showErrorMessage($presence.find('error'), room);
  31341. } else {
  31342. is_self = ($presence.find("status[code='110']").length) || ($presence.attr('from') == room.name+'/'+Strophe.escapeNode(room.nick));
  31343. if (!this.model.get('conneced')) {
  31344. this.model.set('connected', true);
  31345. this.$('span.centered.spinner').remove();
  31346. this.$el.find('.chat-body').children().show();
  31347. }
  31348. this.showStatusMessages($presence, is_self);
  31349. }
  31350. return true;
  31351. },
  31352. onChatRoomMessage: function (message) {
  31353. var $message = $(message),
  31354. body = $message.children('body').text(),
  31355. jid = $message.attr('from'),
  31356. msgid = $message.attr('id'),
  31357. resource = Strophe.getResourceFromJid(jid),
  31358. sender = resource && Strophe.unescapeNode(resource) || '',
  31359. delayed = $message.find('delay').length > 0,
  31360. subject = $message.children('subject').text();
  31361. if (msgid && this.model.messages.findWhere({msgid: msgid})) {
  31362. return true; // We already have this message stored.
  31363. }
  31364. this.showStatusMessages($message);
  31365. if (subject) {
  31366. this.$el.find('.chatroom-topic').text(subject).attr('title', subject);
  31367. // # For translators: the %1$s and %2$s parts will get replaced by the user and topic text respectively
  31368. // # Example: Topic set by JC Brand to: Hello World!
  31369. this.$el.find('.chat-content').append(
  31370. converse.templates.info({
  31371. 'message': __('Topic set by %1$s to: %2$s', sender, subject)
  31372. }));
  31373. }
  31374. if (sender === '') {
  31375. return true;
  31376. }
  31377. this.model.createMessage($message);
  31378. if (!delayed && sender !== this.model.get('nick') && (new RegExp("\\b"+this.model.get('nick')+"\\b")).test(body)) {
  31379. playNotification();
  31380. }
  31381. if (sender !== this.model.get('nick')) {
  31382. // We only emit an event if it's not our own message
  31383. converse.emit('message', message);
  31384. }
  31385. return true;
  31386. },
  31387. onChatRoomRoster: function (roster, room) {
  31388. return this.occupantsview.onChatRoomRoster(roster, room);
  31389. }
  31390. });
  31391. this.ChatBoxes = Backbone.Collection.extend({
  31392. model: converse.ChatBox,
  31393. comparator: 'time_opened',
  31394. registerMessageHandler: function () {
  31395. converse.connection.addHandler(
  31396. $.proxy(function (message) {
  31397. this.onMessage(message);
  31398. return true;
  31399. }, this), null, 'message', 'chat');
  31400. converse.connection.addHandler(
  31401. $.proxy(function (message) {
  31402. this.onInvite(message);
  31403. return true;
  31404. }, this), 'jabber:x:conference', 'message');
  31405. },
  31406. onConnected: function () {
  31407. this.browserStorage = new Backbone.BrowserStorage[converse.storage](
  31408. b64_sha1('converse.chatboxes-'+converse.bare_jid));
  31409. this.registerMessageHandler();
  31410. this.fetch({
  31411. add: true,
  31412. success: $.proxy(function (collection, resp) {
  31413. if (!_.include(_.pluck(resp, 'id'), 'controlbox')) {
  31414. this.add({
  31415. id: 'controlbox',
  31416. box_id: 'controlbox'
  31417. });
  31418. }
  31419. this.get('controlbox').save({connected:true});
  31420. }, this)
  31421. });
  31422. },
  31423. isOnlyChatStateNotification: function ($msg) {
  31424. // See XEP-0085 Chat State Notification
  31425. return (
  31426. $msg.find('body').length === 0 && (
  31427. $msg.find(ACTIVE).length !== 0 ||
  31428. $msg.find(COMPOSING).length !== 0 ||
  31429. $msg.find(INACTIVE).length !== 0 ||
  31430. $msg.find(PAUSED).length !== 0 ||
  31431. $msg.find(GONE).length !== 0
  31432. )
  31433. );
  31434. },
  31435. onInvite: function (message) {
  31436. var $message = $(message),
  31437. $x = $message.children('x[xmlns="jabber:x:conference"]'),
  31438. from = Strophe.getBareJidFromJid($message.attr('from')),
  31439. room_jid = $x.attr('jid'),
  31440. reason = $x.attr('reason'),
  31441. contact = converse.roster.get(from),
  31442. result;
  31443. if (!reason) {
  31444. result = confirm(
  31445. __(___("%1$s has invited you to join a chat room: %2$s"), contact.get('fullname'), room_jid)
  31446. );
  31447. } else {
  31448. result = confirm(
  31449. __(___('%1$s has invited you to join a chat room: %2$s, and left the following reason: "%3$s"'),
  31450. contact.get('fullname'), room_jid, reason)
  31451. );
  31452. }
  31453. if (result === true) {
  31454. var chatroom = converse.chatboxviews.showChat({
  31455. 'id': room_jid,
  31456. 'jid': room_jid,
  31457. 'name': Strophe.unescapeNode(Strophe.getNodeFromJid(room_jid)),
  31458. 'nick': Strophe.unescapeNode(Strophe.getNodeFromJid(converse.connection.jid)),
  31459. 'chatroom': true,
  31460. 'box_id' : b64_sha1(room_jid),
  31461. 'password': $x.attr('password')
  31462. });
  31463. if (!chatroom.get('connected')) {
  31464. converse.chatboxviews.get(room_jid).connect(null);
  31465. }
  31466. }
  31467. },
  31468. onMessage: function (message) {
  31469. var $message = $(message);
  31470. var contact_jid, $forwarded, $received, $sent,
  31471. msgid = $message.attr('id'),
  31472. chatbox, resource, roster_item,
  31473. message_from = $message.attr('from');
  31474. if (message_from === converse.connection.jid) {
  31475. // FIXME: Forwarded messages should be sent to specific resources,
  31476. // not broadcasted
  31477. return true;
  31478. }
  31479. $forwarded = $message.children('forwarded');
  31480. $received = $message.children('received[xmlns="urn:xmpp:carbons:2"]');
  31481. $sent = $message.children('sent[xmlns="urn:xmpp:carbons:2"]');
  31482. if ($forwarded.length) {
  31483. $message = $forwarded.children('message');
  31484. } else if ($received.length) {
  31485. $message = $received.children('forwarded').children('message');
  31486. message_from = $message.attr('from');
  31487. } else if ($sent.length) {
  31488. $message = $sent.children('forwarded').children('message');
  31489. message_from = $message.attr('from');
  31490. }
  31491. var from = Strophe.getBareJidFromJid(message_from),
  31492. to = Strophe.getBareJidFromJid($message.attr('to'));
  31493. if (from == converse.bare_jid) {
  31494. // I am the sender, so this must be a forwarded message...
  31495. contact_jid = to;
  31496. resource = Strophe.getResourceFromJid($message.attr('to'));
  31497. } else {
  31498. contact_jid = from; // XXX: Should we add toLowerCase here? See ticket #234
  31499. resource = Strophe.getResourceFromJid(message_from);
  31500. }
  31501. roster_item = converse.roster.get(contact_jid);
  31502. if (roster_item === undefined) {
  31503. // The contact was likely removed
  31504. converse.log('Could not get roster item for JID '+contact_jid, 'error');
  31505. return true;
  31506. }
  31507. chatbox = this.get(contact_jid);
  31508. if (!chatbox) {
  31509. var fullname = roster_item.get('fullname');
  31510. fullname = _.isEmpty(fullname)? contact_jid: fullname;
  31511. chatbox = this.create({
  31512. 'id': contact_jid,
  31513. 'jid': contact_jid,
  31514. 'fullname': fullname,
  31515. 'image_type': roster_item.get('image_type'),
  31516. 'image': roster_item.get('image'),
  31517. 'url': roster_item.get('url')
  31518. });
  31519. }
  31520. if (msgid && chatbox.messages.findWhere({msgid: msgid})) {
  31521. // FIXME: There's still a bug here..
  31522. // If a duplicate message is received just after the chat
  31523. // box was closed, then it'll open again (due to it being
  31524. // created here above), with now new messages.
  31525. // The solution is mostly likely to not let chat boxes show
  31526. // automatically when they are created, but to require
  31527. // "show" to be called explicitly.
  31528. return true; // We already have this message stored.
  31529. }
  31530. if (!this.isOnlyChatStateNotification($message) && from !== converse.bare_jid) {
  31531. playNotification();
  31532. }
  31533. chatbox.receiveMessage($message);
  31534. converse.roster.addResource(contact_jid, resource);
  31535. converse.emit('message', message);
  31536. return true;
  31537. }
  31538. });
  31539. this.ChatBoxViews = Backbone.Overview.extend({
  31540. initialize: function () {
  31541. this.model.on("add", this.onChatBoxAdded, this);
  31542. this.model.on("change:minimized", function (item) {
  31543. if (item.get('minimized') === false) {
  31544. this.trimChats(this.get(item.get('id')));
  31545. } else {
  31546. this.trimChats();
  31547. }
  31548. }, this);
  31549. },
  31550. _ensureElement: function () {
  31551. /* Override method from backbone.js
  31552. * If the #conversejs element doesn't exist, create it.
  31553. */
  31554. if (!this.el) {
  31555. var $el = $('#conversejs');
  31556. if (!$el.length) {
  31557. $el = $('<div id="conversejs">');
  31558. $('body').append($el);
  31559. }
  31560. $el.html(converse.templates.chats_panel());
  31561. this.setElement($el, false);
  31562. } else {
  31563. this.setElement(_.result(this, 'el'), false);
  31564. }
  31565. },
  31566. onChatBoxAdded: function (item) {
  31567. var view = this.get(item.get('id'));
  31568. if (!view) {
  31569. if (item.get('chatroom')) {
  31570. view = new converse.ChatRoomView({'model': item});
  31571. } else if (item.get('box_id') === 'controlbox') {
  31572. view = new converse.ControlBoxView({model: item});
  31573. } else {
  31574. view = new converse.ChatBoxView({model: item});
  31575. }
  31576. this.add(item.get('id'), view);
  31577. } else {
  31578. delete view.model; // Remove ref to old model to help garbage collection
  31579. view.model = item;
  31580. view.initialize();
  31581. }
  31582. this.trimChats(view);
  31583. },
  31584. trimChats: function (newchat) {
  31585. /* This method is called when a newly created chat box will
  31586. * be shown.
  31587. *
  31588. * It checks whether there is enough space on the page to show
  31589. * another chat box. Otherwise it minimize the oldest chat box
  31590. * to create space.
  31591. */
  31592. if (converse.no_trimming || (this.model.length <= 1)) {
  31593. return;
  31594. }
  31595. var oldest_chat,
  31596. controlbox_width = 0,
  31597. $minimized = converse.minimized_chats.$el,
  31598. minimized_width = _.contains(this.model.pluck('minimized'), true) ? $minimized.outerWidth(true) : 0,
  31599. boxes_width = newchat ? newchat.$el.outerWidth(true) : 0,
  31600. new_id = newchat ? newchat.model.get('id') : null,
  31601. controlbox = this.get('controlbox');
  31602. if (!controlbox || !controlbox.$el.is(':visible')) {
  31603. controlbox_width = converse.controlboxtoggle.$el.outerWidth(true);
  31604. } else {
  31605. controlbox_width = controlbox.$el.outerWidth(true);
  31606. }
  31607. _.each(this.getAll(), function (view) {
  31608. var id = view.model.get('id');
  31609. if ((id !== 'controlbox') && (id !== new_id) && (!view.model.get('minimized')) && view.$el.is(':visible')) {
  31610. boxes_width += view.$el.outerWidth(true);
  31611. }
  31612. });
  31613. if ((minimized_width + boxes_width + controlbox_width) > this.$el.outerWidth(true)) {
  31614. oldest_chat = this.getOldestMaximizedChat();
  31615. if (oldest_chat) {
  31616. oldest_chat.minimize();
  31617. }
  31618. }
  31619. },
  31620. getOldestMaximizedChat: function () {
  31621. // Get oldest view (which is not controlbox)
  31622. var i = 0;
  31623. var model = this.model.sort().at(i);
  31624. while (model.get('id') === 'controlbox' || model.get('minimized') === true) {
  31625. i++;
  31626. model = this.model.at(i);
  31627. if (!model) {
  31628. return null;
  31629. }
  31630. }
  31631. return model;
  31632. },
  31633. closeAllChatBoxes: function (include_controlbox) {
  31634. var i, chatbox;
  31635. // TODO: once Backbone.Overview has been refactored, we should
  31636. // be able to call .each on the views themselves.
  31637. this.model.each($.proxy(function (model) {
  31638. var id = model.get('id');
  31639. if (include_controlbox || id !== 'controlbox') {
  31640. if (this.get(id)) { // Should always resolve, but shit happens
  31641. this.get(id).close();
  31642. }
  31643. }
  31644. }, this));
  31645. return this;
  31646. },
  31647. showChat: function (attrs) {
  31648. /* Find the chat box and show it.
  31649. * If it doesn't exist, create it.
  31650. */
  31651. var chatbox = this.model.get(attrs.jid);
  31652. if (!chatbox) {
  31653. chatbox = this.model.create(attrs, {
  31654. 'error': function (model, response) {
  31655. converse.log(response.responseText);
  31656. }
  31657. });
  31658. }
  31659. if (chatbox.get('minimized')) {
  31660. chatbox.maximize();
  31661. } else {
  31662. chatbox.trigger('show');
  31663. }
  31664. return chatbox;
  31665. }
  31666. });
  31667. this.MinimizedChatBoxView = Backbone.View.extend({
  31668. tagName: 'div',
  31669. className: 'chat-head',
  31670. events: {
  31671. 'click .close-chatbox-button': 'close',
  31672. 'click .restore-chat': 'restore'
  31673. },
  31674. initialize: function () {
  31675. this.model.messages.on('add', function (m) {
  31676. if (!(m.get('composing') || m.get('paused'))) {
  31677. this.updateUnreadMessagesCounter();
  31678. }
  31679. }, this);
  31680. this.model.on('change:minimized', this.clearUnreadMessagesCounter, this);
  31681. this.model.on('showReceivedOTRMessage', this.updateUnreadMessagesCounter, this);
  31682. this.model.on('showSentOTRMessage', this.updateUnreadMessagesCounter, this);
  31683. },
  31684. render: function () {
  31685. var data = _.extend(
  31686. this.model.toJSON(),
  31687. { 'tooltip': __('Click to restore this chat') }
  31688. );
  31689. if (this.model.get('chatroom')) {
  31690. data.title = this.model.get('name');
  31691. this.$el.addClass('chat-head-chatroom');
  31692. } else {
  31693. data.title = this.model.get('fullname');
  31694. this.$el.addClass('chat-head-chatbox');
  31695. }
  31696. return this.$el.html(converse.templates.trimmed_chat(data));
  31697. },
  31698. clearUnreadMessagesCounter: function () {
  31699. this.model.set({'num_unread': 0});
  31700. this.render();
  31701. },
  31702. updateUnreadMessagesCounter: function () {
  31703. this.model.set({'num_unread': this.model.get('num_unread') + 1});
  31704. this.render();
  31705. },
  31706. close: function (ev) {
  31707. if (ev && ev.preventDefault) { ev.preventDefault(); }
  31708. this.remove();
  31709. this.model.destroy();
  31710. converse.emit('chatBoxClosed', this);
  31711. return this;
  31712. },
  31713. restore: _.debounce(function (ev) {
  31714. if (ev && ev.preventDefault) {
  31715. ev.preventDefault();
  31716. }
  31717. this.model.messages.off('add',null,this);
  31718. this.remove();
  31719. this.model.maximize();
  31720. }, 200)
  31721. });
  31722. this.MinimizedChats = Backbone.Overview.extend({
  31723. el: "#minimized-chats",
  31724. events: {
  31725. "click #toggle-minimized-chats": "toggle"
  31726. },
  31727. initialize: function () {
  31728. this.initToggle();
  31729. this.model.on("add", this.onChanged, this);
  31730. this.model.on("destroy", this.removeChat, this);
  31731. this.model.on("change:minimized", this.onChanged, this);
  31732. this.model.on('change:num_unread', this.updateUnreadMessagesCounter, this);
  31733. },
  31734. tearDown: function () {
  31735. this.model.off("add", this.onChanged);
  31736. this.model.off("destroy", this.removeChat);
  31737. this.model.off("change:minimized", this.onChanged);
  31738. this.model.off('change:num_unread', this.updateUnreadMessagesCounter);
  31739. return this;
  31740. },
  31741. initToggle: function () {
  31742. this.toggleview = new converse.MinimizedChatsToggleView({
  31743. model: new converse.MinimizedChatsToggle()
  31744. });
  31745. var id = b64_sha1('converse.minchatstoggle'+converse.bare_jid);
  31746. this.toggleview.model.id = id; // Appears to be necessary for backbone.browserStorage
  31747. this.toggleview.model.browserStorage = new Backbone.BrowserStorage[converse.storage](id);
  31748. this.toggleview.model.fetch();
  31749. },
  31750. render: function () {
  31751. if (this.keys().length === 0) {
  31752. this.$el.hide('fast');
  31753. } else if (this.keys().length === 1) {
  31754. this.$el.show('fast');
  31755. }
  31756. return this.$el;
  31757. },
  31758. toggle: function (ev) {
  31759. if (ev && ev.preventDefault) { ev.preventDefault(); }
  31760. this.toggleview.model.save({'collapsed': !this.toggleview.model.get('collapsed')});
  31761. this.$('.minimized-chats-flyout').toggle();
  31762. },
  31763. onChanged: function (item) {
  31764. if (item.get('id') !== 'controlbox' && item.get('minimized')) {
  31765. this.addChat(item);
  31766. } else if (this.get(item.get('id'))) {
  31767. this.removeChat(item);
  31768. }
  31769. },
  31770. addChat: function (item) {
  31771. var existing = this.get(item.get('id'));
  31772. if (existing && existing.$el.parent().length !== 0) {
  31773. return;
  31774. }
  31775. var view = new converse.MinimizedChatBoxView({model: item});
  31776. this.$('.minimized-chats-flyout').append(view.render());
  31777. this.add(item.get('id'), view);
  31778. this.toggleview.model.set({'num_minimized': this.keys().length});
  31779. this.render();
  31780. },
  31781. removeChat: function (item) {
  31782. this.remove(item.get('id'));
  31783. this.toggleview.model.set({'num_minimized': this.keys().length});
  31784. this.render();
  31785. },
  31786. updateUnreadMessagesCounter: function () {
  31787. var ls = this.model.pluck('num_unread'),
  31788. count = 0, i;
  31789. for (i=0; i<ls.length; i++) { count += ls[i]; }
  31790. this.toggleview.model.set({'num_unread': count});
  31791. this.render();
  31792. }
  31793. });
  31794. this.MinimizedChatsToggle = Backbone.Model.extend({
  31795. initialize: function () {
  31796. this.set({
  31797. 'collapsed': this.get('collapsed') || false,
  31798. 'num_minimized': this.get('num_minimized') || 0,
  31799. 'num_unread': this.get('num_unread') || 0
  31800. });
  31801. }
  31802. });
  31803. this.MinimizedChatsToggleView = Backbone.View.extend({
  31804. el: '#toggle-minimized-chats',
  31805. initialize: function () {
  31806. this.model.on('change:num_minimized', this.render, this);
  31807. this.model.on('change:num_unread', this.render, this);
  31808. this.$flyout = this.$el.siblings('.minimized-chats-flyout');
  31809. },
  31810. render: function () {
  31811. this.$el.html(converse.templates.toggle_chats(
  31812. _.extend(this.model.toJSON(), {
  31813. 'Minimized': __('Minimized')
  31814. })
  31815. ));
  31816. if (this.model.get('collapsed')) {
  31817. this.$flyout.hide();
  31818. } else {
  31819. this.$flyout.show();
  31820. }
  31821. return this.$el;
  31822. }
  31823. });
  31824. this.RosterContact = Backbone.Model.extend({
  31825. initialize: function (attributes, options) {
  31826. var jid = attributes.jid;
  31827. var attrs = _.extend({
  31828. 'id': jid,
  31829. 'fullname': jid,
  31830. 'chat_status': 'offline',
  31831. 'user_id': Strophe.getNodeFromJid(jid),
  31832. 'resources': [],
  31833. 'groups': [],
  31834. 'status': ''
  31835. }, attributes);
  31836. this.set(attrs);
  31837. },
  31838. showInRoster: function () {
  31839. var chatStatus = this.get('chat_status');
  31840. if (converse.show_only_online_users && chatStatus !== 'online')
  31841. return false;
  31842. if (converse.hide_offline_users && chatStatus === 'offline')
  31843. return false;
  31844. return true;
  31845. }
  31846. });
  31847. this.RosterContactView = Backbone.View.extend({
  31848. tagName: 'dd',
  31849. events: {
  31850. "click .accept-xmpp-request": "acceptRequest",
  31851. "click .decline-xmpp-request": "declineRequest",
  31852. "click .open-chat": "openChat",
  31853. "click .remove-xmpp-contact": "removeContact"
  31854. },
  31855. initialize: function () {
  31856. this.model.on("change", this.render, this);
  31857. this.model.on("remove", this.remove, this);
  31858. this.model.on("destroy", this.remove, this);
  31859. this.model.on("open", this.openChat, this);
  31860. },
  31861. render: function () {
  31862. if (!this.model.showInRoster()) {
  31863. this.$el.hide();
  31864. return this;
  31865. } else if (this.$el[0].style.display === "none") {
  31866. this.$el.show();
  31867. }
  31868. var item = this.model,
  31869. ask = item.get('ask'),
  31870. chat_status = item.get('chat_status'),
  31871. requesting = item.get('requesting'),
  31872. subscription = item.get('subscription');
  31873. var classes_to_remove = [
  31874. 'current-xmpp-contact',
  31875. 'pending-xmpp-contact',
  31876. 'requesting-xmpp-contact'
  31877. ].concat(_.keys(STATUSES));
  31878. _.each(classes_to_remove,
  31879. function (cls) {
  31880. if (this.el.className.indexOf(cls) !== -1) {
  31881. this.$el.removeClass(cls);
  31882. }
  31883. }, this);
  31884. this.$el.addClass(chat_status).data('status', chat_status);
  31885. if ((ask === 'subscribe') || (subscription === 'from')) {
  31886. /* ask === 'subscribe'
  31887. * Means we have asked to subscribe to them.
  31888. *
  31889. * subscription === 'from'
  31890. * They are subscribed to use, but not vice versa.
  31891. * We assume that there is a pending subscription
  31892. * from us to them (otherwise we're in a state not
  31893. * supported by converse.js).
  31894. *
  31895. * So in both cases the user is a "pending" contact.
  31896. */
  31897. this.$el.addClass('pending-xmpp-contact');
  31898. this.$el.html(converse.templates.pending_contact(
  31899. _.extend(item.toJSON(), {
  31900. 'desc_remove': __('Click to remove this contact')
  31901. })
  31902. ));
  31903. } else if (requesting === true) {
  31904. this.$el.addClass('requesting-xmpp-contact');
  31905. this.$el.html(converse.templates.requesting_contact(
  31906. _.extend(item.toJSON(), {
  31907. 'desc_accept': __("Click to accept this contact request"),
  31908. 'desc_decline': __("Click to decline this contact request")
  31909. })
  31910. ));
  31911. converse.controlboxtoggle.showControlBox();
  31912. } else if (subscription === 'both' || subscription === 'to') {
  31913. this.$el.addClass('current-xmpp-contact');
  31914. this.$el.html(converse.templates.roster_item(
  31915. _.extend(item.toJSON(), {
  31916. 'desc_status': STATUSES[chat_status||'offline'],
  31917. 'desc_chat': __('Click to chat with this contact'),
  31918. 'desc_remove': __('Click to remove this contact')
  31919. })
  31920. ));
  31921. }
  31922. return this;
  31923. },
  31924. openChat: function (ev) {
  31925. if (ev && ev.preventDefault) { ev.preventDefault(); }
  31926. // XXX: Can this.model.attributes be used here, instead of
  31927. // manually specifying all attributes?
  31928. return converse.chatboxviews.showChat({
  31929. 'id': this.model.get('jid'),
  31930. 'jid': this.model.get('jid'),
  31931. 'fullname': this.model.get('fullname'),
  31932. 'image_type': this.model.get('image_type'),
  31933. 'image': this.model.get('image'),
  31934. 'url': this.model.get('url'),
  31935. 'status': this.model.get('status')
  31936. });
  31937. },
  31938. removeContact: function (ev) {
  31939. if (ev && ev.preventDefault) { ev.preventDefault(); }
  31940. var result = confirm(__("Are you sure you want to remove this contact?"));
  31941. if (result === true) {
  31942. var bare_jid = this.model.get('jid');
  31943. converse.connection.roster.remove(bare_jid, $.proxy(function (iq) {
  31944. converse.connection.roster.unauthorize(bare_jid);
  31945. converse.rosterview.model.remove(bare_jid);
  31946. this.model.destroy();
  31947. this.remove();
  31948. }, this));
  31949. }
  31950. },
  31951. acceptRequest: function (ev) {
  31952. if (ev && ev.preventDefault) { ev.preventDefault(); }
  31953. var jid = this.model.get('jid');
  31954. converse.connection.roster.authorize(jid);
  31955. converse.connection.roster.add(jid, this.model.get('fullname'), [], function (iq) {
  31956. converse.connection.roster.subscribe(jid, null, converse.xmppstatus.get('fullname'));
  31957. });
  31958. },
  31959. declineRequest: function (ev) {
  31960. if (ev && ev.preventDefault) { ev.preventDefault(); }
  31961. var result = confirm(__("Are you sure you want to decline this contact request?"));
  31962. if (result === true) {
  31963. converse.connection.roster.unauthorize(this.model.get('jid'));
  31964. this.model.destroy();
  31965. }
  31966. return this;
  31967. }
  31968. });
  31969. this.RosterContacts = Backbone.Collection.extend({
  31970. model: converse.RosterContact,
  31971. comparator: function (contact1, contact2) {
  31972. var name1, name2;
  31973. var status1 = contact1.get('chat_status') || 'offline';
  31974. var status2 = contact2.get('chat_status') || 'offline';
  31975. if (STATUS_WEIGHTS[status1] === STATUS_WEIGHTS[status2]) {
  31976. name1 = contact1.get('fullname').toLowerCase();
  31977. name2 = contact2.get('fullname').toLowerCase();
  31978. return name1 < name2 ? -1 : (name1 > name2? 1 : 0);
  31979. } else {
  31980. return STATUS_WEIGHTS[status1] < STATUS_WEIGHTS[status2] ? -1 : 1;
  31981. }
  31982. },
  31983. subscribeToSuggestedItems: function (msg) {
  31984. $(msg).find('item').each(function (i, items) {
  31985. var $this = $(this),
  31986. jid = $this.attr('jid'),
  31987. action = $this.attr('action'),
  31988. fullname = $this.attr('name');
  31989. if (action === 'add') {
  31990. converse.connection.roster.subscribe(jid, null, converse.xmppstatus.get('fullname'));
  31991. }
  31992. });
  31993. return true;
  31994. },
  31995. isSelf: function (jid) {
  31996. return (Strophe.getBareJidFromJid(jid) === Strophe.getBareJidFromJid(converse.connection.jid));
  31997. },
  31998. addResource: function (bare_jid, resource) {
  31999. var item = this.get(bare_jid),
  32000. resources;
  32001. if (item) {
  32002. resources = item.get('resources');
  32003. if (resources) {
  32004. if (_.indexOf(resources, resource) == -1) {
  32005. resources.push(resource);
  32006. item.set({'resources': resources});
  32007. }
  32008. } else {
  32009. item.set({'resources': [resource]});
  32010. }
  32011. }
  32012. },
  32013. removeResource: function (bare_jid, resource) {
  32014. var item = this.get(bare_jid),
  32015. resources,
  32016. idx;
  32017. if (item) {
  32018. resources = item.get('resources');
  32019. idx = _.indexOf(resources, resource);
  32020. if (idx !== -1) {
  32021. resources.splice(idx, 1);
  32022. item.save({'resources': resources});
  32023. return resources.length;
  32024. }
  32025. }
  32026. return 0;
  32027. },
  32028. subscribeBack: function (jid) {
  32029. var bare_jid = Strophe.getBareJidFromJid(jid);
  32030. if (converse.connection.roster.findItem(bare_jid)) {
  32031. converse.connection.roster.authorize(bare_jid);
  32032. converse.connection.roster.subscribe(jid, null, converse.xmppstatus.get('fullname'));
  32033. } else {
  32034. converse.connection.roster.add(jid, '', [], function (iq) {
  32035. converse.connection.roster.authorize(bare_jid);
  32036. converse.connection.roster.subscribe(jid, null, converse.xmppstatus.get('fullname'));
  32037. });
  32038. }
  32039. },
  32040. unsubscribe: function (jid) {
  32041. /* Upon receiving the presence stanza of type "unsubscribed",
  32042. * the user SHOULD acknowledge receipt of that subscription state
  32043. * notification by sending a presence stanza of type "unsubscribe"
  32044. * this step lets the user's server know that it MUST no longer
  32045. * send notification of the subscription state change to the user.
  32046. */
  32047. converse.xmppstatus.sendPresence('unsubscribe');
  32048. if (converse.connection.roster.findItem(jid)) {
  32049. converse.connection.roster.remove(jid, function (iq) {
  32050. converse.rosterview.model.remove(jid);
  32051. });
  32052. }
  32053. },
  32054. getNumOnlineContacts: function () {
  32055. var count = 0,
  32056. ignored = ['offline', 'unavailable'],
  32057. models = this.models,
  32058. models_length = models.length,
  32059. i;
  32060. if (converse.show_only_online_users) {
  32061. ignored = _.union(ignored, ['dnd', 'xa', 'away']);
  32062. }
  32063. for (i=0; i<models_length; i++) {
  32064. if (_.indexOf(ignored, models[i].get('chat_status')) === -1) {
  32065. count++;
  32066. }
  32067. }
  32068. return count;
  32069. },
  32070. clearCache: function (items) {
  32071. /* The localstorage cache containing roster contacts might contain
  32072. * some contacts that aren't actually in our roster anymore. We
  32073. * therefore need to remove them now.
  32074. */
  32075. var id, i, contact;
  32076. for (i=0; i < this.models.length; ++i) {
  32077. id = this.models[i].get('id');
  32078. if (_.indexOf(_.pluck(items, 'jid'), id) === -1) {
  32079. contact = this.get(id);
  32080. if (contact && !contact.get('requesting')) {
  32081. contact.destroy();
  32082. }
  32083. }
  32084. }
  32085. },
  32086. // TODO: see if we can only use 2nd item par
  32087. rosterHandler: function (items, item) {
  32088. converse.emit('roster', items);
  32089. this.clearCache(items);
  32090. var new_items = item ? [item] : items;
  32091. _.each(new_items, function (item, index, items) {
  32092. if (this.isSelf(item.jid)) { return; }
  32093. var model = this.get(item.jid);
  32094. if (!model) {
  32095. var is_last = (index === (items.length-1)) ? true : false;
  32096. if ((item.subscription === 'none') && (item.ask === null) && !is_last) {
  32097. // We're not interested in zombies
  32098. // (Hack: except if it's the last item, then we still
  32099. // add it so that the roster will be shown).
  32100. return;
  32101. }
  32102. this.create({
  32103. ask: item.ask,
  32104. fullname: item.name || item.jid,
  32105. groups: item.groups,
  32106. jid: item.jid,
  32107. subscription: item.subscription
  32108. }, {sort: false});
  32109. } else {
  32110. if ((item.subscription === 'none') && (item.ask === null)) {
  32111. // This user is no longer in our roster
  32112. model.destroy();
  32113. } else {
  32114. // We only find out about requesting contacts via the
  32115. // presence handler, so if we receive a contact
  32116. // here, we know they aren't requesting anymore.
  32117. // see docs/DEVELOPER.rst
  32118. model.save({
  32119. subscription: item.subscription,
  32120. ask: item.ask,
  32121. requesting: null,
  32122. groups: item.groups
  32123. });
  32124. }
  32125. }
  32126. }, this);
  32127. if (!converse.initial_presence_sent) {
  32128. /* Once we've sent out our initial presence stanza, we'll
  32129. * start receiving presence stanzas from our contacts.
  32130. * We therefore only want to do this after our roster has
  32131. * been set up (otherwise we can't meaningfully process
  32132. * incoming presence stanzas).
  32133. */
  32134. converse.initial_presence_sent = 1;
  32135. converse.xmppstatus.sendPresence();
  32136. }
  32137. },
  32138. handleIncomingSubscription: function (jid) {
  32139. var bare_jid = Strophe.getBareJidFromJid(jid);
  32140. var item = this.get(bare_jid);
  32141. if (!converse.allow_contact_requests) {
  32142. converse.connection.roster.unauthorize(bare_jid);
  32143. return true;
  32144. }
  32145. if (converse.auto_subscribe) {
  32146. if ((!item) || (item.get('subscription') != 'to')) {
  32147. this.subscribeBack(jid);
  32148. } else {
  32149. converse.connection.roster.authorize(bare_jid);
  32150. }
  32151. } else {
  32152. if ((item) && (item.get('subscription') != 'none')) {
  32153. converse.connection.roster.authorize(bare_jid);
  32154. } else {
  32155. if (!this.get(bare_jid)) {
  32156. converse.getVCard(
  32157. bare_jid,
  32158. $.proxy(function (jid, fullname, img, img_type, url) {
  32159. this.create({
  32160. jid: bare_jid,
  32161. subscription: 'none',
  32162. ask: null,
  32163. requesting: true,
  32164. fullname: fullname || jid,
  32165. image: img,
  32166. image_type: img_type,
  32167. url: url,
  32168. vcard_updated: moment().format()
  32169. });
  32170. }, this),
  32171. $.proxy(function (jid, iq) {
  32172. converse.log("Error while retrieving vcard");
  32173. this.create({
  32174. jid: bare_jid,
  32175. subscription: 'none',
  32176. ask: null,
  32177. requesting: true,
  32178. fullname: bare_jid,
  32179. vcard_updated: moment().format()
  32180. });
  32181. }, this)
  32182. );
  32183. } else {
  32184. return true;
  32185. }
  32186. }
  32187. }
  32188. return true;
  32189. },
  32190. presenceHandler: function (presence) {
  32191. var $presence = $(presence),
  32192. presence_type = $presence.attr('type');
  32193. if (presence_type === 'error') {
  32194. return true;
  32195. }
  32196. var jid = $presence.attr('from'),
  32197. bare_jid = Strophe.getBareJidFromJid(jid),
  32198. resource = Strophe.getResourceFromJid(jid),
  32199. $show = $presence.find('show'),
  32200. chat_status = $show.text() || 'online',
  32201. status_message = $presence.find('status'),
  32202. contact;
  32203. if (this.isSelf(bare_jid)) {
  32204. if ((converse.connection.jid !== jid)&&(presence_type !== 'unavailable')) {
  32205. // Another resource has changed it's status, we'll update ours as well.
  32206. converse.xmppstatus.save({'status': chat_status});
  32207. }
  32208. return true;
  32209. } else if (($presence.find('x').attr('xmlns') || '').indexOf(Strophe.NS.MUC) === 0) {
  32210. return true; // Ignore MUC
  32211. }
  32212. contact = this.get(bare_jid);
  32213. if (contact && (status_message.text() != contact.get('status'))) {
  32214. contact.save({'status': status_message.text()});
  32215. }
  32216. if ((presence_type === 'subscribed') || (presence_type === 'unsubscribe')) {
  32217. return true;
  32218. } else if (presence_type === 'subscribe') {
  32219. return this.handleIncomingSubscription(jid);
  32220. } else if (presence_type === 'unsubscribed') {
  32221. this.unsubscribe(bare_jid);
  32222. } else if (presence_type === 'unavailable') {
  32223. if (this.removeResource(bare_jid, resource) === 0) {
  32224. chat_status = "offline";
  32225. }
  32226. if (contact && chat_status) {
  32227. contact.save({'chat_status': chat_status});
  32228. }
  32229. } else if (contact) {
  32230. // presence_type is undefined
  32231. this.addResource(bare_jid, resource);
  32232. contact.save({'chat_status': chat_status});
  32233. }
  32234. return true;
  32235. }
  32236. });
  32237. this.RosterGroup = Backbone.Model.extend({
  32238. initialize: function (attributes, options) {
  32239. this.set(_.extend({
  32240. description: DESC_GROUP_TOGGLE,
  32241. state: OPENED
  32242. }, attributes));
  32243. // Collection of contacts belonging to this group.
  32244. this.contacts = new converse.RosterContacts();
  32245. }
  32246. });
  32247. this.RosterGroupView = Backbone.Overview.extend({
  32248. tagName: 'dt',
  32249. className: 'roster-group',
  32250. events: {
  32251. "click a.group-toggle": "toggle"
  32252. },
  32253. initialize: function () {
  32254. this.model.contacts.on("add", this.addContact, this);
  32255. this.model.contacts.on("change:subscription", this.onContactSubscriptionChange, this);
  32256. this.model.contacts.on("change:requesting", this.onContactRequestChange, this);
  32257. this.model.contacts.on("change:chat_status", function (contact) {
  32258. // This might be optimized by instead of first sorting,
  32259. // finding the correct position in positionContact
  32260. this.model.contacts.sort();
  32261. this.positionContact(contact).render();
  32262. }, this);
  32263. this.model.contacts.on("destroy", this.onRemove, this);
  32264. this.model.contacts.on("remove", this.onRemove, this);
  32265. converse.roster.on('change:groups', this.onContactGroupChange, this);
  32266. },
  32267. render: function () {
  32268. this.$el.attr('data-group', this.model.get('name'));
  32269. this.$el.html(
  32270. $(converse.templates.group_header({
  32271. label_group: this.model.get('name'),
  32272. desc_group_toggle: this.model.get('description'),
  32273. toggle_state: this.model.get('state')
  32274. }))
  32275. );
  32276. return this;
  32277. },
  32278. addContact: function (contact) {
  32279. var view = new converse.RosterContactView({model: contact});
  32280. this.add(contact.get('id'), view);
  32281. view = this.positionContact(contact).render();
  32282. if (contact.showInRoster()) {
  32283. if (this.model.get('state') === CLOSED) {
  32284. if (view.$el[0].style.display !== "none") { view.$el.hide(); }
  32285. if (this.$el[0].style.display === "none") { this.$el.show(); }
  32286. } else {
  32287. if (this.$el[0].style.display !== "block") { this.show(); }
  32288. }
  32289. }
  32290. },
  32291. positionContact: function (contact) {
  32292. /* Place the contact's DOM element in the correct alphabetical
  32293. * position amongst the other contacts in this group.
  32294. */
  32295. var view = this.get(contact.get('id'));
  32296. var index = this.model.contacts.indexOf(contact);
  32297. view.$el.detach();
  32298. if (index === 0) {
  32299. this.$el.after(view.$el);
  32300. } else if (index == (this.model.contacts.length-1)) {
  32301. this.$el.nextUntil('dt').last().after(view.$el);
  32302. } else {
  32303. this.$el.nextUntil('dt').eq(index).before(view.$el);
  32304. }
  32305. return view;
  32306. },
  32307. show: function () {
  32308. // FIXME: There's a bug here, if show_only_online_users is true
  32309. // Possible solution, get the group, call _.each and check
  32310. // showInRoster
  32311. this.$el.nextUntil('dt').addBack().show();
  32312. },
  32313. hide: function () {
  32314. this.$el.nextUntil('dt').addBack().hide();
  32315. },
  32316. filter: function (q) {
  32317. /* Filter the group's contacts based on the query "q".
  32318. * The query is matched against the contact's full name.
  32319. * If all contacts are filtered out (i.e. hidden), then the
  32320. * group must be filtered out as well.
  32321. */
  32322. var matches, rejects;
  32323. if (q.length === 0) {
  32324. if (this.model.get('state') === OPENED) {
  32325. this.model.contacts.each($.proxy(function (item) {
  32326. if (item.showInRoster()) {
  32327. this.get(item.get('id')).$el.show();
  32328. }
  32329. }, this));
  32330. }
  32331. this.showIfNecessary();
  32332. } else {
  32333. q = q.toLowerCase();
  32334. matches = this.model.contacts.filter(contains.not('fullname', q));
  32335. if (matches.length === this.model.contacts.length) { // hide the whole group
  32336. this.hide();
  32337. } else {
  32338. _.each(matches, $.proxy(function (item) {
  32339. this.get(item.get('id')).$el.hide();
  32340. }, this));
  32341. _.each(this.model.contacts.reject(contains.not('fullname', q)), $.proxy(function (item) {
  32342. this.get(item.get('id')).$el.show();
  32343. }, this));
  32344. this.showIfNecessary();
  32345. }
  32346. }
  32347. },
  32348. showIfNecessary: function () {
  32349. if (!this.$el.is(':visible') && this.model.contacts.length > 0) {
  32350. this.$el.show();
  32351. }
  32352. },
  32353. toggle: function (ev) {
  32354. if (ev && ev.preventDefault) { ev.preventDefault(); }
  32355. var $el = $(ev.target);
  32356. if ($el.hasClass("icon-opened")) {
  32357. this.$el.nextUntil('dt').slideUp();
  32358. this.model.save({state: CLOSED});
  32359. $el.removeClass("icon-opened").addClass("icon-closed");
  32360. } else {
  32361. $el.removeClass("icon-closed").addClass("icon-opened");
  32362. this.model.save({state: OPENED});
  32363. this.filter(
  32364. converse.rosterview.$('.roster-filter').val(),
  32365. converse.rosterview.$('.filter-type').val()
  32366. );
  32367. }
  32368. },
  32369. onContactGroupChange: function (contact) {
  32370. var in_this_group = _.contains(contact.get('groups'), this.model.get('name'));
  32371. var cid = contact.get('id');
  32372. var in_this_overview = !this.get(cid);
  32373. if (in_this_group && !in_this_overview) {
  32374. this.model.contacts.remove(cid);
  32375. } else if (!in_this_group && in_this_overview) {
  32376. this.addContact(contact);
  32377. }
  32378. },
  32379. onContactSubscriptionChange: function (contact) {
  32380. if ((this.model.get('name') === HEADER_PENDING_CONTACTS) && contact.get('subscription') !== 'from') {
  32381. this.model.contacts.remove(contact.get('id'));
  32382. }
  32383. },
  32384. onContactRequestChange: function (contact) {
  32385. if ((this.model.get('name') === HEADER_REQUESTING_CONTACTS) && !contact.get('requesting')) {
  32386. this.model.contacts.remove(contact.get('id'));
  32387. }
  32388. },
  32389. onRemove: function (contact) {
  32390. this.remove(contact.get('id'));
  32391. if (this.model.contacts.length === 0) {
  32392. this.$el.hide();
  32393. }
  32394. }
  32395. });
  32396. this.RosterGroups = Backbone.Collection.extend({
  32397. model: converse.RosterGroup,
  32398. comparator: function (a, b) {
  32399. /* Groups are sorted alphabetically, ignoring case.
  32400. * However, Ungrouped, Requesting Contacts and Pending Contacts
  32401. * appear last and in that order. */
  32402. a = a.get('name');
  32403. b = b.get('name');
  32404. var special_groups = _.keys(HEADER_WEIGHTS);
  32405. var a_is_special = _.contains(special_groups, a);
  32406. var b_is_special = _.contains(special_groups, b);
  32407. if (!a_is_special && !b_is_special ) {
  32408. return a.toLowerCase() < b.toLowerCase() ? -1 : (a.toLowerCase() > b.toLowerCase() ? 1 : 0);
  32409. } else if (a_is_special && b_is_special) {
  32410. return HEADER_WEIGHTS[a] < HEADER_WEIGHTS[b] ? -1 : (HEADER_WEIGHTS[a] > HEADER_WEIGHTS[b] ? 1 : 0);
  32411. } else if (!a_is_special && b_is_special) {
  32412. return (b === HEADER_CURRENT_CONTACTS) ? 1 : -1;
  32413. } else if (a_is_special && !b_is_special) {
  32414. return (a === HEADER_CURRENT_CONTACTS) ? -1 : 1;
  32415. }
  32416. }
  32417. });
  32418. this.RosterView = Backbone.Overview.extend({
  32419. tagName: 'div',
  32420. id: 'converse-roster',
  32421. events: {
  32422. "keydown .roster-filter": "liveFilter",
  32423. "click .onX": "clearFilter",
  32424. "mousemove .x": "togglePointer",
  32425. "change .filter-type": "changeFilterType"
  32426. },
  32427. initialize: function () {
  32428. this.registerRosterHandler();
  32429. this.registerRosterXHandler();
  32430. this.registerPresenceHandler();
  32431. converse.roster.on("add", this.onContactAdd, this);
  32432. converse.roster.on('change', this.onContactChange, this);
  32433. converse.roster.on("destroy", this.update, this);
  32434. converse.roster.on("remove", this.update, this);
  32435. this.model.on("add", this.onGroupAdd, this);
  32436. this.model.on("reset", this.reset, this);
  32437. this.$roster = $('<dl class="roster-contacts" style="display: none;"></dl>');
  32438. },
  32439. update: _.debounce(function () {
  32440. var $count = $('#online-count');
  32441. $count.text('('+converse.roster.getNumOnlineContacts()+')');
  32442. if (!$count.is(':visible')) {
  32443. $count.show();
  32444. }
  32445. if (this.$roster.parent().length === 0) {
  32446. this.$el.append(this.$roster.show());
  32447. }
  32448. return this.showHideFilter();
  32449. }, converse.animate ? 100 : 0),
  32450. render: function () {
  32451. this.$el.html(converse.templates.roster({
  32452. placeholder: __('Type to filter'),
  32453. label_contacts: LABEL_CONTACTS,
  32454. label_groups: LABEL_GROUPS
  32455. }));
  32456. return this;
  32457. },
  32458. fetch: function () {
  32459. this.model.fetch({
  32460. silent: true, // We use the success handler to handle groups that were added,
  32461. // we need to first have all groups before positionFetchedGroups
  32462. // will work properly.
  32463. success: $.proxy(function (collection, resp, options) {
  32464. if (collection.length !== 0) {
  32465. this.positionFetchedGroups(collection, resp, options);
  32466. }
  32467. converse.roster.fetch({
  32468. add: true,
  32469. success: function (collection) {
  32470. // XXX: Bit of a hack.
  32471. // strophe.roster expects .get to be called for
  32472. // every page load so that its "items" attr
  32473. // gets populated.
  32474. // This is very inefficient for large rosters,
  32475. // and we already have the roster cached in
  32476. // sessionStorage.
  32477. // Therefore we manually populate the "items"
  32478. // attr.
  32479. // Ideally we should eventually replace
  32480. // strophe.roster with something better.
  32481. if (collection.length > 0) {
  32482. collection.each(function (item) {
  32483. converse.connection.roster.items.push({
  32484. name : item.get('fullname'),
  32485. jid : item.get('jid'),
  32486. subscription : item.get('subscription'),
  32487. ask : item.get('ask'),
  32488. groups : item.get('groups'),
  32489. resources : item.get('resources')
  32490. });
  32491. });
  32492. converse.initial_presence_sent = 1;
  32493. converse.xmppstatus.sendPresence();
  32494. } else {
  32495. converse.connection.roster.get();
  32496. }
  32497. }
  32498. });
  32499. }, this)
  32500. });
  32501. return this;
  32502. },
  32503. changeFilterType: function (ev) {
  32504. if (ev && ev.preventDefault) { ev.preventDefault(); }
  32505. this.clearFilter();
  32506. this.filter(
  32507. this.$('.roster-filter').val(),
  32508. ev.target.value
  32509. );
  32510. },
  32511. tog: function (v) {
  32512. return v?'addClass':'removeClass';
  32513. },
  32514. togglePointer: function (ev) {
  32515. if (ev && ev.preventDefault) { ev.preventDefault(); }
  32516. var el = ev.target;
  32517. $(el)[this.tog(el.offsetWidth-18 < ev.clientX-el.getBoundingClientRect().left)]('onX');
  32518. },
  32519. filter: function (query, type) {
  32520. var matches;
  32521. query = query.toLowerCase();
  32522. if (type === 'groups') {
  32523. _.each(this.getAll(), function (view, idx) {
  32524. if (view.model.get('name').toLowerCase().indexOf(query.toLowerCase()) === -1) {
  32525. view.hide();
  32526. } else if (view.model.contacts.length > 0) {
  32527. view.show();
  32528. }
  32529. });
  32530. } else {
  32531. _.each(this.getAll(), function (view) {
  32532. view.filter(query, type);
  32533. });
  32534. }
  32535. },
  32536. liveFilter: _.debounce(function (ev) {
  32537. if (ev && ev.preventDefault) { ev.preventDefault(); }
  32538. var $filter = this.$('.roster-filter');
  32539. var q = $filter.val();
  32540. var t = this.$('.filter-type').val();
  32541. $filter[this.tog(q)]('x');
  32542. this.filter(q, t);
  32543. }, 300),
  32544. clearFilter: function (ev) {
  32545. if (ev && ev.preventDefault) {
  32546. ev.preventDefault();
  32547. $(ev.target).removeClass('x onX').val('');
  32548. }
  32549. this.filter('');
  32550. },
  32551. showHideFilter: function () {
  32552. if (!this.$el.is(':visible')) {
  32553. return;
  32554. }
  32555. var $filter = this.$('.roster-filter');
  32556. var $type = this.$('.filter-type');
  32557. var visible = $filter.is(':visible');
  32558. if (visible && $filter.val().length > 0) {
  32559. // Don't hide if user is currently filtering.
  32560. return;
  32561. }
  32562. if (this.$roster.hasScrollBar()) {
  32563. if (!visible) {
  32564. $filter.show();
  32565. $type.show();
  32566. }
  32567. } else {
  32568. $filter.hide();
  32569. $type.hide();
  32570. }
  32571. return this;
  32572. },
  32573. reset: function () {
  32574. converse.roster.reset();
  32575. this.removeAll();
  32576. this.$roster = $('<dl class="roster-contacts" style="display: none;"></dl>');
  32577. this.render().update();
  32578. return this;
  32579. },
  32580. registerRosterHandler: function () {
  32581. // Register handlers that depend on the roster
  32582. converse.connection.roster.registerCallback(
  32583. $.proxy(converse.roster.rosterHandler, converse.roster)
  32584. );
  32585. },
  32586. registerRosterXHandler: function () {
  32587. var t = 0;
  32588. converse.connection.addHandler(
  32589. function (msg) {
  32590. window.setTimeout(
  32591. function () {
  32592. converse.connection.flush();
  32593. $.proxy(converse.roster.subscribeToSuggestedItems, converse.roster)(msg);
  32594. },
  32595. t
  32596. );
  32597. t += $(msg).find('item').length*250;
  32598. return true;
  32599. },
  32600. 'http://jabber.org/protocol/rosterx', 'message', null);
  32601. },
  32602. registerPresenceHandler: function () {
  32603. converse.connection.addHandler(
  32604. $.proxy(function (presence) {
  32605. converse.roster.presenceHandler(presence);
  32606. return true;
  32607. }, this), null, 'presence', null);
  32608. },
  32609. onGroupAdd: function (group) {
  32610. var view = new converse.RosterGroupView({model: group});
  32611. this.add(group.get('name'), view.render());
  32612. this.positionGroup(view);
  32613. },
  32614. onContactAdd: function (contact) {
  32615. this.addRosterContact(contact).update();
  32616. if (!contact.get('vcard_updated')) {
  32617. // This will update the vcard, which triggers a change
  32618. // request which will rerender the roster contact.
  32619. converse.getVCard(contact.get('jid'));
  32620. }
  32621. },
  32622. onContactChange: function (contact) {
  32623. this.updateChatBox(contact).update();
  32624. if (_.has(contact.changed, 'subscription')) {
  32625. if (contact.changed.subscription == 'from') {
  32626. this.addContactToGroup(contact, HEADER_PENDING_CONTACTS);
  32627. } else if (contact.get('subscription') === 'both') {
  32628. this.addExistingContact(contact);
  32629. }
  32630. }
  32631. if (_.has(contact.changed, 'ask') && contact.changed.ask == 'subscribe') {
  32632. this.addContactToGroup(contact, HEADER_PENDING_CONTACTS);
  32633. }
  32634. if (_.has(contact.changed, 'subscription') && contact.changed.requesting == 'true') {
  32635. this.addContactToGroup(contact, HEADER_REQUESTING_CONTACTS);
  32636. }
  32637. this.liveFilter();
  32638. },
  32639. updateChatBox: function (contact) {
  32640. var chatbox = converse.chatboxes.get(contact.get('jid')),
  32641. changes = {};
  32642. if (!chatbox) {
  32643. return this;
  32644. }
  32645. if (_.has(contact.changed, 'chat_status')) {
  32646. changes.chat_status = contact.get('chat_status');
  32647. }
  32648. if (_.has(contact.changed, 'status')) {
  32649. changes.status = contact.get('status');
  32650. }
  32651. chatbox.save(changes);
  32652. return this;
  32653. },
  32654. positionFetchedGroups: function (model, resp, options) {
  32655. /* Instead of throwing an add event for each group
  32656. * fetched, we wait until they're all fetched and then
  32657. * we position them.
  32658. * Works around the problem of positionGroup not
  32659. * working when all groups besides the one being
  32660. * positioned aren't already in inserted into the
  32661. * roster DOM element.
  32662. */
  32663. model.sort();
  32664. model.each($.proxy(function (group, idx) {
  32665. var view = this.get(group.get('name'));
  32666. if (!view) {
  32667. view = new converse.RosterGroupView({model: group});
  32668. this.add(group.get('name'), view.render());
  32669. }
  32670. if (idx === 0) {
  32671. this.$roster.append(view.$el);
  32672. } else {
  32673. this.appendGroup(view);
  32674. }
  32675. }, this));
  32676. },
  32677. positionGroup: function (view) {
  32678. /* Place the group's DOM element in the correct alphabetical
  32679. * position amongst the other groups in the roster.
  32680. */
  32681. var $groups = this.$roster.find('.roster-group'),
  32682. index = $groups.length ? this.model.indexOf(view.model) : 0;
  32683. if (index === 0) {
  32684. this.$roster.prepend(view.$el);
  32685. } else if (index == (this.model.length-1)) {
  32686. this.appendGroup(view);
  32687. } else {
  32688. $($groups.eq(index)).before(view.$el);
  32689. }
  32690. return this;
  32691. },
  32692. appendGroup: function (view) {
  32693. /* Add the group at the bottom of the roster
  32694. */
  32695. var $last = this.$roster.find('.roster-group').last();
  32696. var $siblings = $last.siblings('dd');
  32697. if ($siblings.length > 0) {
  32698. $siblings.last().after(view.$el);
  32699. } else {
  32700. $last.after(view.$el);
  32701. }
  32702. return this;
  32703. },
  32704. getGroup: function (name) {
  32705. /* Returns the group as specified by name.
  32706. * Creates the group if it doesn't exist.
  32707. */
  32708. var view = this.get(name);
  32709. if (view) {
  32710. return view.model;
  32711. }
  32712. return this.model.create({name: name, id: b64_sha1(name)});
  32713. },
  32714. addContactToGroup: function (contact, name) {
  32715. this.getGroup(name).contacts.add(contact);
  32716. },
  32717. addExistingContact: function (contact) {
  32718. var groups;
  32719. if (converse.roster_groups) {
  32720. groups = contact.get('groups');
  32721. if (groups.length === 0) {
  32722. groups = [HEADER_UNGROUPED];
  32723. }
  32724. } else {
  32725. groups = [HEADER_CURRENT_CONTACTS];
  32726. }
  32727. _.each(groups, $.proxy(function (name) {
  32728. this.addContactToGroup(contact, name);
  32729. }, this));
  32730. },
  32731. addRosterContact: function (contact) {
  32732. if (contact.get('subscription') === 'both' || contact.get('subscription') === 'to') {
  32733. this.addExistingContact(contact);
  32734. } else {
  32735. if ((contact.get('ask') === 'subscribe') || (contact.get('subscription') === 'from')) {
  32736. this.addContactToGroup(contact, HEADER_PENDING_CONTACTS);
  32737. } else if (contact.get('requesting') === true) {
  32738. this.addContactToGroup(contact, HEADER_REQUESTING_CONTACTS);
  32739. }
  32740. }
  32741. return this;
  32742. }
  32743. });
  32744. this.XMPPStatus = Backbone.Model.extend({
  32745. initialize: function () {
  32746. this.set({
  32747. 'status' : this.get('status') || 'online'
  32748. });
  32749. this.on('change', $.proxy(function (item) {
  32750. if (this.get('fullname') === undefined) {
  32751. converse.getVCard(
  32752. null, // No 'to' attr when getting one's own vCard
  32753. $.proxy(function (jid, fullname, image, image_type, url) {
  32754. this.save({'fullname': fullname});
  32755. }, this)
  32756. );
  32757. }
  32758. if (_.has(item.changed, 'status')) {
  32759. converse.emit('statusChanged', this.get('status'));
  32760. }
  32761. if (_.has(item.changed, 'status_message')) {
  32762. converse.emit('statusMessageChanged', this.get('status_message'));
  32763. }
  32764. }, this));
  32765. },
  32766. sendPresence: function (type) {
  32767. if (type === undefined) {
  32768. type = this.get('status') || 'online';
  32769. }
  32770. var status_message = this.get('status_message'),
  32771. presence;
  32772. // Most of these presence types are actually not explicitly sent,
  32773. // but I add all of them here fore reference and future proofing.
  32774. if ((type === 'unavailable') ||
  32775. (type === 'probe') ||
  32776. (type === 'error') ||
  32777. (type === 'unsubscribe') ||
  32778. (type === 'unsubscribed') ||
  32779. (type === 'subscribe') ||
  32780. (type === 'subscribed')) {
  32781. presence = $pres({'type': type});
  32782. } else if (type === 'offline') {
  32783. presence = $pres({'type': 'unavailable'});
  32784. if (status_message) {
  32785. presence.c('show').t(type);
  32786. }
  32787. } else {
  32788. if (type === 'online') {
  32789. presence = $pres();
  32790. } else {
  32791. presence = $pres().c('show').t(type).up();
  32792. }
  32793. if (status_message) {
  32794. presence.c('status').t(status_message);
  32795. }
  32796. }
  32797. converse.connection.send(presence);
  32798. },
  32799. setStatus: function (value) {
  32800. this.sendPresence(value);
  32801. this.save({'status': value});
  32802. },
  32803. setStatusMessage: function (status_message) {
  32804. converse.connection.send($pres().c('show').t(this.get('status')).up().c('status').t(status_message));
  32805. this.save({'status_message': status_message});
  32806. if (this.xhr_custom_status) {
  32807. $.ajax({
  32808. url: this.xhr_custom_status_url,
  32809. type: 'POST',
  32810. data: {'msg': status_message}
  32811. });
  32812. }
  32813. }
  32814. });
  32815. this.XMPPStatusView = Backbone.View.extend({
  32816. el: "span#xmpp-status-holder",
  32817. events: {
  32818. "click a.choose-xmpp-status": "toggleOptions",
  32819. "click #fancy-xmpp-status-select a.change-xmpp-status-message": "renderStatusChangeForm",
  32820. "submit #set-custom-xmpp-status": "setStatusMessage",
  32821. "click .dropdown dd ul li a": "setStatus"
  32822. },
  32823. initialize: function () {
  32824. this.model.on("change", this.updateStatusUI, this);
  32825. },
  32826. render: function () {
  32827. // Replace the default dropdown with something nicer
  32828. var $select = this.$el.find('select#select-xmpp-status'),
  32829. chat_status = this.model.get('status') || 'offline',
  32830. options = $('option', $select),
  32831. $options_target,
  32832. options_list = [],
  32833. that = this;
  32834. this.$el.html(converse.templates.choose_status());
  32835. this.$el.find('#fancy-xmpp-status-select')
  32836. .html(converse.templates.chat_status({
  32837. 'status_message': this.model.get('status_message') || __("I am %1$s", this.getPrettyStatus(chat_status)),
  32838. 'chat_status': chat_status,
  32839. 'desc_custom_status': __('Click here to write a custom status message'),
  32840. 'desc_change_status': __('Click to change your chat status')
  32841. }));
  32842. // iterate through all the <option> elements and add option values
  32843. options.each(function (){
  32844. options_list.push(converse.templates.status_option({
  32845. 'value': $(this).val(),
  32846. 'text': this.text
  32847. }));
  32848. });
  32849. $options_target = this.$el.find("#target dd ul").hide();
  32850. $options_target.append(options_list.join(''));
  32851. $select.remove();
  32852. return this;
  32853. },
  32854. toggleOptions: function (ev) {
  32855. ev.preventDefault();
  32856. $(ev.target).parent().parent().siblings('dd').find('ul').toggle('fast');
  32857. },
  32858. renderStatusChangeForm: function (ev) {
  32859. ev.preventDefault();
  32860. var status_message = this.model.get('status') || 'offline';
  32861. var input = converse.templates.change_status_message({
  32862. 'status_message': status_message,
  32863. 'label_custom_status': __('Custom status'),
  32864. 'label_save': __('Save')
  32865. });
  32866. this.$el.find('.xmpp-status').replaceWith(input);
  32867. this.$el.find('.custom-xmpp-status').focus().focus();
  32868. },
  32869. setStatusMessage: function (ev) {
  32870. ev.preventDefault();
  32871. var status_message = $(ev.target).find('input').val();
  32872. this.model.setStatusMessage(status_message);
  32873. },
  32874. setStatus: function (ev) {
  32875. ev.preventDefault();
  32876. var $el = $(ev.target),
  32877. value = $el.attr('data-value');
  32878. if (value === 'logout') {
  32879. this.$el.find(".dropdown dd ul").hide();
  32880. converse.logOut();
  32881. } else {
  32882. this.model.setStatus(value);
  32883. this.$el.find(".dropdown dd ul").hide();
  32884. }
  32885. },
  32886. getPrettyStatus: function (stat) {
  32887. var pretty_status;
  32888. if (stat === 'chat') {
  32889. pretty_status = __('online');
  32890. } else if (stat === 'dnd') {
  32891. pretty_status = __('busy');
  32892. } else if (stat === 'xa') {
  32893. pretty_status = __('away for long');
  32894. } else if (stat === 'away') {
  32895. pretty_status = __('away');
  32896. } else {
  32897. pretty_status = __(stat) || __('online');
  32898. }
  32899. return pretty_status;
  32900. },
  32901. updateStatusUI: function (model) {
  32902. if (!(_.has(model.changed, 'status')) && !(_.has(model.changed, 'status_message'))) {
  32903. return;
  32904. }
  32905. var stat = model.get('status');
  32906. // # For translators: the %1$s part gets replaced with the status
  32907. // # Example, I am online
  32908. var status_message = model.get('status_message') || __("I am %1$s", this.getPrettyStatus(stat));
  32909. this.$el.find('#fancy-xmpp-status-select').html(
  32910. converse.templates.chat_status({
  32911. 'chat_status': stat,
  32912. 'status_message': status_message,
  32913. 'desc_custom_status': __('Click here to write a custom status message'),
  32914. 'desc_change_status': __('Click to change your chat status')
  32915. }));
  32916. }
  32917. });
  32918. this.BOSHSession = Backbone.Model;
  32919. this.Feature = Backbone.Model;
  32920. this.Features = Backbone.Collection.extend({
  32921. /* Service Discovery
  32922. * -----------------
  32923. * This collection stores Feature Models, representing features
  32924. * provided by available XMPP entities (e.g. servers)
  32925. * See XEP-0030 for more details: http://xmpp.org/extensions/xep-0030.html
  32926. * All features are shown here: http://xmpp.org/registrar/disco-features.html
  32927. */
  32928. model: converse.Feature,
  32929. initialize: function () {
  32930. this.addClientIdentities().addClientFeatures();
  32931. this.browserStorage = new Backbone.BrowserStorage[converse.storage](
  32932. b64_sha1('converse.features'+converse.bare_jid));
  32933. if (this.browserStorage.records.length === 0) {
  32934. // browserStorage is empty, so we've likely never queried this
  32935. // domain for features yet
  32936. converse.connection.disco.info(converse.domain, null, $.proxy(this.onInfo, this));
  32937. converse.connection.disco.items(converse.domain, null, $.proxy(this.onItems, this));
  32938. } else {
  32939. this.fetch({add:true});
  32940. }
  32941. },
  32942. addClientIdentities: function () {
  32943. /* See http://xmpp.org/registrar/disco-categories.html
  32944. */
  32945. converse.connection.disco.addIdentity('client', 'web', 'Converse.js');
  32946. return this;
  32947. },
  32948. addClientFeatures: function () {
  32949. /* The strophe.disco.js plugin keeps a list of features which
  32950. * it will advertise to any #info queries made to it.
  32951. *
  32952. * See: http://xmpp.org/extensions/xep-0030.html#info
  32953. *
  32954. * TODO: these features need to be added in the relevant
  32955. * feature-providing Models, not here
  32956. */
  32957. converse.connection.disco.addFeature('http://jabber.org/protocol/chatstates'); // Limited support
  32958. converse.connection.disco.addFeature('http://jabber.org/protocol/rosterx'); // Limited support
  32959. converse.connection.disco.addFeature('jabber:x:conference');
  32960. converse.connection.disco.addFeature('urn:xmpp:carbons:2');
  32961. converse.connection.disco.addFeature(Strophe.NS.VCARD);
  32962. converse.connection.disco.addFeature(Strophe.NS.BOSH);
  32963. converse.connection.disco.addFeature(Strophe.NS.DISCO_INFO);
  32964. converse.connection.disco.addFeature(Strophe.NS.MUC);
  32965. return this;
  32966. },
  32967. onItems: function (stanza) {
  32968. $(stanza).find('query item').each($.proxy(function (idx, item) {
  32969. converse.connection.disco.info(
  32970. $(item).attr('jid'),
  32971. null,
  32972. $.proxy(this.onInfo, this));
  32973. }, this));
  32974. },
  32975. onInfo: function (stanza) {
  32976. var $stanza = $(stanza);
  32977. if (($stanza.find('identity[category=server][type=im]').length === 0) &&
  32978. ($stanza.find('identity[category=conference][type=text]').length === 0)) {
  32979. // This isn't an IM server component
  32980. return;
  32981. }
  32982. $stanza.find('feature').each($.proxy(function (idx, feature) {
  32983. this.create({
  32984. 'var': $(feature).attr('var'),
  32985. 'from': $stanza.attr('from')
  32986. });
  32987. }, this));
  32988. }
  32989. });
  32990. this.RegisterPanel = Backbone.View.extend({
  32991. tagName: 'div',
  32992. id: "register",
  32993. className: 'controlbox-pane',
  32994. events: {
  32995. 'submit form#converse-register': 'onProviderChosen'
  32996. },
  32997. initialize: function (cfg) {
  32998. this.reset();
  32999. this.$parent = cfg.$parent;
  33000. this.$tabs = cfg.$parent.parent().find('#controlbox-tabs');
  33001. this.registerHooks();
  33002. },
  33003. render: function () {
  33004. this.$parent.append(this.$el.html(
  33005. converse.templates.register_panel({
  33006. 'label_domain': __("Your XMPP provider's domain name:"),
  33007. 'label_register': __('Fetch registration form'),
  33008. 'help_providers': __('Tip: A list of public XMPP providers is available'),
  33009. 'help_providers_link': __('here'),
  33010. 'href_providers': converse.providers_link,
  33011. 'domain_placeholder': converse.domain_placeholder
  33012. })
  33013. ));
  33014. this.$tabs.append(converse.templates.register_tab({label_register: __('Register')}));
  33015. return this;
  33016. },
  33017. registerHooks: function () {
  33018. /* Hook into Strophe's _connect_cb, so that we can send an IQ
  33019. * requesting the registration fields.
  33020. */
  33021. var conn = converse.connection;
  33022. var connect_cb = conn._connect_cb.bind(conn);
  33023. conn._connect_cb = $.proxy(function (req, callback, raw) {
  33024. if (!this._registering) {
  33025. connect_cb(req, callback, raw);
  33026. } else {
  33027. if (this.getRegistrationFields(req, callback, raw)) {
  33028. this._registering = false;
  33029. }
  33030. }
  33031. }, this);
  33032. },
  33033. getRegistrationFields: function (req, _callback, raw) {
  33034. /* Send an IQ stanza to the XMPP server asking for the
  33035. * registration fields.
  33036. *
  33037. * Parameters:
  33038. * (Strophe.Request) req - The current request
  33039. * (Function) callback
  33040. */
  33041. converse.log("sendQueryStanza was called");
  33042. var conn = converse.connection;
  33043. conn.connected = true;
  33044. var body = conn._proto._reqToData(req);
  33045. if (!body) { return; }
  33046. if (conn._proto._connect_cb(body) === Strophe.Status.CONNFAIL) {
  33047. return false;
  33048. }
  33049. var register = body.getElementsByTagName("register");
  33050. var mechanisms = body.getElementsByTagName("mechanism");
  33051. if (register.length === 0 && mechanisms.length === 0) {
  33052. conn._proto._no_auth_received(_callback);
  33053. return false;
  33054. }
  33055. if (register.length === 0) {
  33056. conn._changeConnectStatus(
  33057. Strophe.Status.REGIFAIL,
  33058. __('Sorry, the given provider does not support in band account registration. Please try with a different provider.')
  33059. );
  33060. return true;
  33061. }
  33062. // Send an IQ stanza to get all required data fields
  33063. conn._addSysHandler(this.onRegistrationFields.bind(this), null, "iq", null, null);
  33064. conn.send($iq({type: "get"}).c("query", {xmlns: Strophe.NS.REGISTER}).tree());
  33065. return true;
  33066. },
  33067. onRegistrationFields: function (stanza) {
  33068. /* Handler for Registration Fields Request.
  33069. *
  33070. * Parameters:
  33071. * (XMLElement) elem - The query stanza.
  33072. */
  33073. if (stanza.getElementsByTagName("query").length !== 1) {
  33074. converse.connection._changeConnectStatus(Strophe.Status.REGIFAIL, "unknown");
  33075. return false;
  33076. }
  33077. this.setFields(stanza);
  33078. this.renderRegistrationForm(stanza);
  33079. return false;
  33080. },
  33081. reset: function (settings) {
  33082. var defaults = {
  33083. fields: {},
  33084. urls: [],
  33085. title: "",
  33086. instructions: "",
  33087. registered: false,
  33088. _registering: false,
  33089. domain: null,
  33090. form_type: null
  33091. };
  33092. _.extend(this, defaults);
  33093. if (settings) {
  33094. _.extend(this, _.pick(settings, Object.keys(defaults)));
  33095. }
  33096. },
  33097. onProviderChosen: function (ev) {
  33098. /* Callback method that gets called when the user has chosen an
  33099. * XMPP provider.
  33100. *
  33101. * Parameters:
  33102. * (Submit Event) ev - Form submission event.
  33103. */
  33104. if (ev && ev.preventDefault) { ev.preventDefault(); }
  33105. var $form = $(ev.target),
  33106. $domain_input = $form.find('input[name=domain]'),
  33107. domain = $domain_input.val(),
  33108. errors = false;
  33109. if (!domain) {
  33110. $domain_input.addClass('error');
  33111. return;
  33112. }
  33113. $form.find('input[type=submit]').hide()
  33114. .after(converse.templates.registration_request({
  33115. cancel: __('Cancel'),
  33116. info_message: __('Requesting a registration form from the XMPP server')
  33117. }));
  33118. $form.find('button.cancel').on('click', $.proxy(this.cancelRegistration, this));
  33119. this.reset({
  33120. domain: Strophe.getDomainFromJid(domain),
  33121. _registering: true
  33122. });
  33123. converse.connection.connect(this.domain, "", $.proxy(this.onRegistering, this));
  33124. return false;
  33125. },
  33126. giveFeedback: function (message, klass) {
  33127. this.$('.reg-feedback').attr('class', 'reg-feedback').text(message);
  33128. if (klass) {
  33129. $('.reg-feedback').addClass(klass);
  33130. }
  33131. },
  33132. onRegistering: function (status, error) {
  33133. var that;
  33134. console.log('onRegistering');
  33135. if (_.contains([
  33136. Strophe.Status.DISCONNECTED,
  33137. Strophe.Status.CONNFAIL,
  33138. Strophe.Status.REGIFAIL,
  33139. Strophe.Status.NOTACCEPTABLE,
  33140. Strophe.Status.CONFLICT
  33141. ], status)) {
  33142. converse.log('Problem during registration: Strophe.Status is: '+status);
  33143. this.cancelRegistration();
  33144. if (error) {
  33145. this.giveFeedback(error, 'error');
  33146. } else {
  33147. this.giveFeedback(__(
  33148. 'Something went wrong while establishing a connection with "%1$s". Are you sure it exists?',
  33149. this.domain
  33150. ), 'error');
  33151. }
  33152. } else if (status == Strophe.Status.REGISTERED) {
  33153. converse.log("Registered successfully.");
  33154. converse.connection.reset();
  33155. that = this;
  33156. this.$('form').hide(function () {
  33157. $(this).replaceWith('<span class="spinner centered"/>');
  33158. if (that.fields.password && that.fields.username) {
  33159. // automatically log the user in
  33160. converse.connection.connect(
  33161. that.fields.username+'@'+that.domain,
  33162. that.fields.password,
  33163. converse.onConnect
  33164. );
  33165. converse.chatboxviews.get('controlbox')
  33166. .switchTab({target: that.$tabs.find('.current')})
  33167. .giveFeedback(__('Now logging you in'));
  33168. } else {
  33169. converse.chatboxviews.get('controlbox')
  33170. .renderLoginPanel()
  33171. .giveFeedback(__('Registered successfully'));
  33172. }
  33173. that.reset();
  33174. });
  33175. }
  33176. },
  33177. renderRegistrationForm: function (stanza) {
  33178. /* Renders the registration form based on the XForm fields
  33179. * received from the XMPP server.
  33180. *
  33181. * Parameters:
  33182. * (XMLElement) stanza - The IQ stanza received from the XMPP server.
  33183. */
  33184. var $form= this.$('form'),
  33185. $stanza = $(stanza),
  33186. $fields;
  33187. $form.empty().append(converse.templates.registration_form({
  33188. 'domain': this.domain,
  33189. 'title': this.title,
  33190. 'instructions': this.instructions
  33191. }));
  33192. if (this.form_type == 'xform') {
  33193. $fields = $stanza.find('field');
  33194. _.each($fields, $.proxy(function (field) {
  33195. $form.append(utils.xForm2webForm.bind(this, $(field), $stanza));
  33196. }, this));
  33197. } else {
  33198. // Show fields
  33199. _.each(Object.keys(this.fields), $.proxy(function (key) {
  33200. $form.append('<label>'+key+'</label>');
  33201. var $input = $('<input placeholder="'+key+'" name="'+key+'"></input>');
  33202. if (key === 'password' || key === 'email') {
  33203. $input.attr('type', key);
  33204. }
  33205. $form.append($input);
  33206. }, this));
  33207. // Show urls
  33208. _.each(this.urls, $.proxy(function (url) {
  33209. $form.append($('<a target="blank"></a>').attr('href', url).text(url));
  33210. }, this));
  33211. }
  33212. if (this.fields) {
  33213. $form.append('<input type="submit" class="save-submit" value="'+__('Register')+'"/>');
  33214. $form.on('submit', $.proxy(this.submitRegistrationForm, this));
  33215. $form.append('<input type="button" class="cancel-submit" value="'+__('Cancel')+'"/>');
  33216. $form.find('input[type=button]').on('click', $.proxy(this.cancelRegistration, this));
  33217. } else {
  33218. $form.append('<input type="button" class="submit" value="'+__('Return')+'"/>');
  33219. $form.find('input[type=button]').on('click', $.proxy(this.cancelRegistration, this));
  33220. }
  33221. },
  33222. reportErrors: function (stanza) {
  33223. /* Report back to the user any error messages received from the
  33224. * XMPP server after attempted registration.
  33225. *
  33226. * Parameters:
  33227. * (XMLElement) stanza - The IQ stanza received from the
  33228. * XMPP server.
  33229. */
  33230. var $form= this.$('form'), flash;
  33231. var $errmsgs = $(stanza).find('error text');
  33232. var $flash = $form.find('.form-errors');
  33233. if (!$flash.length) {
  33234. flash = '<legend class="form-errors"></legend>';
  33235. if ($form.find('p.instructions').length) {
  33236. $form.find('p.instructions').append(flash);
  33237. } else {
  33238. $form.prepend(flash);
  33239. }
  33240. $flash = $form.find('.form-errors');
  33241. } else {
  33242. $flash.empty();
  33243. }
  33244. $errmsgs.each(function (idx, txt) {
  33245. $flash.append($('<p>').text($(txt).text()));
  33246. });
  33247. if (!$errmsgs.length) {
  33248. $flash.append($('<p>').text(
  33249. __('The provider rejected your registration attempt. '+
  33250. 'Please check the values you entered for correctness.')));
  33251. }
  33252. $flash.show();
  33253. },
  33254. cancelRegistration: function (ev) {
  33255. /* Handler, when the user cancels the registration form.
  33256. */
  33257. if (ev && ev.preventDefault) { ev.preventDefault(); }
  33258. converse.connection.reset();
  33259. this.render();
  33260. },
  33261. submitRegistrationForm : function (ev) {
  33262. /* Handler, when the user submits the registration form.
  33263. * Provides form error feedback or starts the registration
  33264. * process.
  33265. *
  33266. * Parameters:
  33267. * (Event) ev - the submit event.
  33268. */
  33269. if (ev && ev.preventDefault) { ev.preventDefault(); }
  33270. var $empty_inputs = this.$('input.required:emptyVal');
  33271. if ($empty_inputs.length) {
  33272. $empty_inputs.addClass('error');
  33273. return;
  33274. }
  33275. var $inputs = $(ev.target).find(':input:not([type=button]):not([type=submit])'),
  33276. iq = $iq({type: "set"})
  33277. .c("query", {xmlns:Strophe.NS.REGISTER})
  33278. .c("x", {xmlns: Strophe.NS.XFORM, type: 'submit'});
  33279. $inputs.each(function () {
  33280. iq.cnode(utils.webForm2xForm(this)).up();
  33281. });
  33282. converse.connection._addSysHandler(this._onRegisterIQ.bind(this), null, "iq", null, null);
  33283. converse.connection.send(iq);
  33284. this.setFields(iq.tree());
  33285. },
  33286. setFields: function (stanza) {
  33287. /* Stores the values that will be sent to the XMPP server
  33288. * during attempted registration.
  33289. *
  33290. * Parameters:
  33291. * (XMLElement) stanza - the IQ stanza that will be sent to the XMPP server.
  33292. */
  33293. var $query = $(stanza).find('query'), $xform;
  33294. if ($query.length > 0) {
  33295. $xform = $query.find('x[xmlns="'+Strophe.NS.XFORM+'"]');
  33296. if ($xform.length > 0) {
  33297. this._setFieldsFromXForm($xform);
  33298. } else {
  33299. this._setFieldsFromLegacy($query);
  33300. }
  33301. }
  33302. },
  33303. _setFieldsFromLegacy: function ($query) {
  33304. $query.children().each($.proxy(function (idx, field) {
  33305. var $field = $(field);
  33306. if (field.tagName.toLowerCase() === 'instructions') {
  33307. this.instructions = Strophe.getText(field);
  33308. return;
  33309. } else if (field.tagName.toLowerCase() === 'x') {
  33310. if ($field.attr('xmlns') === 'jabber:x:oob') {
  33311. $field.find('url').each($.proxy(function (idx, url) {
  33312. this.urls.push($(url).text());
  33313. }, this));
  33314. }
  33315. return;
  33316. }
  33317. this.fields[field.tagName.toLowerCase()] = Strophe.getText(field);
  33318. }, this));
  33319. this.form_type = 'legacy';
  33320. },
  33321. _setFieldsFromXForm: function ($xform) {
  33322. this.title = $xform.find('title').text();
  33323. this.instructions = $xform.find('instructions').text();
  33324. $xform.find('field').each($.proxy(function (idx, field) {
  33325. var _var = field.getAttribute('var');
  33326. if (_var) {
  33327. this.fields[_var.toLowerCase()] = $(field).children('value').text();
  33328. } else {
  33329. // TODO: other option seems to be type="fixed"
  33330. console.log("WARNING: Found field we couldn't parse");
  33331. }
  33332. }, this));
  33333. this.form_type = 'xform';
  33334. },
  33335. _onRegisterIQ: function (stanza) {
  33336. /* Callback method that gets called when a return IQ stanza
  33337. * is received from the XMPP server, after attempting to
  33338. * register a new user.
  33339. *
  33340. * Parameters:
  33341. * (XMLElement) stanza - The IQ stanza.
  33342. */
  33343. var i, field, error = null, that,
  33344. query = stanza.getElementsByTagName("query");
  33345. if (query.length > 0) {
  33346. query = query[0];
  33347. }
  33348. if (stanza.getAttribute("type") === "error") {
  33349. converse.log("Registration failed.");
  33350. error = stanza.getElementsByTagName("error");
  33351. if (error.length !== 1) {
  33352. converse.connection._changeConnectStatus(Strophe.Status.REGIFAIL, "unknown");
  33353. return false;
  33354. }
  33355. error = error[0].firstChild.tagName.toLowerCase();
  33356. if (error === 'conflict') {
  33357. converse.connection._changeConnectStatus(Strophe.Status.CONFLICT, error);
  33358. } else if (error === 'not-acceptable') {
  33359. converse.connection._changeConnectStatus(Strophe.Status.NOTACCEPTABLE, error);
  33360. } else {
  33361. converse.connection._changeConnectStatus(Strophe.Status.REGIFAIL, error);
  33362. }
  33363. this.reportErrors(stanza);
  33364. } else {
  33365. converse.connection._changeConnectStatus(Strophe.Status.REGISTERED, null);
  33366. }
  33367. return false;
  33368. },
  33369. remove: function () {
  33370. this.$tabs.empty();
  33371. this.$el.parent().empty();
  33372. }
  33373. });
  33374. this.LoginPanel = Backbone.View.extend({
  33375. tagName: 'div',
  33376. id: "login-dialog",
  33377. className: 'controlbox-pane',
  33378. events: {
  33379. 'submit form#converse-login': 'authenticate'
  33380. },
  33381. initialize: function (cfg) {
  33382. cfg.$parent.html(this.$el.html(
  33383. converse.templates.login_panel({
  33384. 'label_username': __('XMPP Username:'),
  33385. 'label_password': __('Password:'),
  33386. 'label_login': __('Log In')
  33387. })
  33388. ));
  33389. this.$tabs = cfg.$parent.parent().find('#controlbox-tabs');
  33390. },
  33391. render: function () {
  33392. this.$tabs.append(converse.templates.login_tab({label_sign_in: __('Sign in')}));
  33393. this.$el.find('input#jid').focus();
  33394. if (!this.$el.is(':visible')) {
  33395. this.$el.show();
  33396. }
  33397. return this;
  33398. },
  33399. authenticate: function (ev) {
  33400. if (ev && ev.preventDefault) { ev.preventDefault(); }
  33401. var $form = $(ev.target),
  33402. $jid_input = $form.find('input[name=jid]'),
  33403. jid = $jid_input.val(),
  33404. $pw_input = $form.find('input[name=password]'),
  33405. password = $pw_input.val(),
  33406. $bsu_input = null,
  33407. errors = false;
  33408. if (! converse.bosh_service_url) {
  33409. $bsu_input = $form.find('input#bosh_service_url');
  33410. converse.bosh_service_url = $bsu_input.val();
  33411. if (! converse.bosh_service_url) {
  33412. errors = true;
  33413. $bsu_input.addClass('error');
  33414. }
  33415. }
  33416. if (! jid) {
  33417. errors = true;
  33418. $jid_input.addClass('error');
  33419. }
  33420. if (! password) {
  33421. errors = true;
  33422. $pw_input.addClass('error');
  33423. }
  33424. if (errors) { return; }
  33425. this.connect($form, jid, password);
  33426. return false;
  33427. },
  33428. connect: function ($form, jid, password) {
  33429. if ($form) {
  33430. $form.find('input[type=submit]').hide().after('<span class="spinner login-submit"/>');
  33431. }
  33432. var resource = Strophe.getResourceFromJid(jid);
  33433. if (!resource) {
  33434. jid += '/converse.js-' + Math.floor(Math.random()*139749825).toString();
  33435. }
  33436. converse.connection.connect(jid, password, converse.onConnect);
  33437. },
  33438. remove: function () {
  33439. this.$tabs.empty();
  33440. this.$el.parent().empty();
  33441. }
  33442. });
  33443. this.ControlBoxToggle = Backbone.View.extend({
  33444. tagName: 'a',
  33445. className: 'toggle-controlbox',
  33446. id: 'toggle-controlbox',
  33447. events: {
  33448. 'click': 'onClick'
  33449. },
  33450. attributes: {
  33451. 'href': "#"
  33452. },
  33453. initialize: function () {
  33454. this.render();
  33455. },
  33456. render: function () {
  33457. $('#conversejs').prepend(this.$el.html(
  33458. converse.templates.controlbox_toggle({
  33459. 'label_toggle': __('Toggle chat')
  33460. })
  33461. ));
  33462. // We let the render method of ControlBoxView decide whether
  33463. // the ControlBox or the Toggle must be shown. This prevents
  33464. // artifacts (i.e. on page load the toggle is shown only to then
  33465. // seconds later be hidden in favor of the control box).
  33466. this.$el.hide();
  33467. return this;
  33468. },
  33469. hide: function (callback) {
  33470. this.$el.fadeOut('fast', callback);
  33471. },
  33472. show: function (callback) {
  33473. this.$el.show('fast', callback);
  33474. },
  33475. showControlBox: function () {
  33476. var controlbox = converse.chatboxes.get('controlbox');
  33477. if (!controlbox) {
  33478. controlbox = converse.addControlBox();
  33479. }
  33480. if (converse.connection.connected) {
  33481. controlbox.save({closed: false});
  33482. } else {
  33483. controlbox.trigger('show');
  33484. }
  33485. },
  33486. onClick: function (e) {
  33487. e.preventDefault();
  33488. if ($("div#controlbox").is(':visible')) {
  33489. var controlbox = converse.chatboxes.get('controlbox');
  33490. if (converse.connection.connected) {
  33491. controlbox.save({closed: true});
  33492. } else {
  33493. controlbox.trigger('hide');
  33494. }
  33495. } else {
  33496. this.showControlBox();
  33497. }
  33498. }
  33499. });
  33500. this.addControlBox = function () {
  33501. return this.chatboxes.add({
  33502. id: 'controlbox',
  33503. box_id: 'controlbox',
  33504. height: this.default_box_height,
  33505. closed: !this.show_controlbox_by_default
  33506. });
  33507. };
  33508. this.setUpXMLLogging = function () {
  33509. if (this.debug) {
  33510. this.connection.xmlInput = function (body) { console.log(body); };
  33511. this.connection.xmlOutput = function (body) { console.log(body); };
  33512. }
  33513. };
  33514. this.initConnection = function () {
  33515. var rid, sid, jid;
  33516. if (this.connection && this.connection.connected) {
  33517. this.setUpXMLLogging();
  33518. this.onConnected();
  33519. } else {
  33520. // XXX: it's not yet clear what the order of preference should
  33521. // be between RID and SID received via the initialize method or
  33522. // those received from sessionStorage.
  33523. //
  33524. // What do you we if we receive values from both avenues?
  33525. //
  33526. // Also, what do we do when the keepalive session values are
  33527. // expired? Do we try to fall back?
  33528. if (!this.bosh_service_url) {
  33529. throw("Error: you must supply a value for the bosh_service_url");
  33530. }
  33531. this.connection = new Strophe.Connection(this.bosh_service_url);
  33532. this.setUpXMLLogging();
  33533. if (this.prebind) {
  33534. if (this.jid && this.sid && this.rid) {
  33535. this.connection.attach(this.jid, this.sid, this.rid, this.onConnect);
  33536. }
  33537. if (!this.keepalive) {
  33538. throw("If you use prebind and don't use keepalive, "+
  33539. "then you MUST supply JID, RID and SID values");
  33540. }
  33541. }
  33542. if (this.keepalive) {
  33543. rid = this.session.get('rid');
  33544. sid = this.session.get('sid');
  33545. jid = this.session.get('jid');
  33546. if (rid && jid && sid) {
  33547. this.session.save({rid: rid}); // The RID needs to be increased with each request.
  33548. this.connection.attach(jid, sid, rid, this.onConnect);
  33549. } else if (this.prebind) {
  33550. delete this.connection;
  33551. this.emit('noResumeableSession');
  33552. }
  33553. }
  33554. }
  33555. };
  33556. this._tearDown = function () {
  33557. /* Remove those views which are only allowed with a valid
  33558. * connection.
  33559. */
  33560. this.initial_presence_sent = false;
  33561. if (this.roster) {
  33562. this.roster.off().reset(); // Removes roster contacts
  33563. }
  33564. this.connection.roster._callbacks = []; // Remove all Roster handlers (e.g. rosterHandler)
  33565. if (this.rosterview) {
  33566. this.rosterview.model.off().reset(); // Removes roster groups
  33567. this.rosterview.undelegateEvents().remove();
  33568. }
  33569. this.chatboxes.remove(); // Don't call off(), events won't get re-registered upon reconnect.
  33570. if (this.features) {
  33571. this.features.reset();
  33572. }
  33573. if (this.minimized_chats) {
  33574. this.minimized_chats.undelegateEvents().model.reset();
  33575. this.minimized_chats.removeAll(); // Remove sub-views
  33576. this.minimized_chats.tearDown().remove(); // Remove overview
  33577. delete this.minimized_chats;
  33578. }
  33579. return this;
  33580. };
  33581. this._initialize = function () {
  33582. this.chatboxes = new this.ChatBoxes();
  33583. this.chatboxviews = new this.ChatBoxViews({model: this.chatboxes});
  33584. this.controlboxtoggle = new this.ControlBoxToggle();
  33585. this.otr = new this.OTR();
  33586. this.initSession();
  33587. this.initConnection();
  33588. if (this.connection) {
  33589. this.addControlBox();
  33590. }
  33591. return this;
  33592. };
  33593. this._initializePlugins = function () {
  33594. _.each(this.plugins, $.proxy(function (plugin) {
  33595. $.proxy(plugin, this)(this);
  33596. }, this));
  33597. };
  33598. // Initialization
  33599. // --------------
  33600. // This is the end of the initialize method.
  33601. if (settings.connection) {
  33602. this.connection = settings.connection;
  33603. }
  33604. this._initializePlugins();
  33605. this._initialize();
  33606. this.registerGlobalEventHandlers();
  33607. converse.emit('initialized');
  33608. };
  33609. var wrappedChatBox = function (chatbox) {
  33610. return {
  33611. 'endOTR': $.proxy(chatbox.endOTR, chatbox),
  33612. 'get': $.proxy(chatbox.get, chatbox),
  33613. 'initiateOTR': $.proxy(chatbox.initiateOTR, chatbox),
  33614. 'maximize': $.proxy(chatbox.maximize, chatbox),
  33615. 'minimize': $.proxy(chatbox.minimize, chatbox),
  33616. 'set': $.proxy(chatbox.set, chatbox),
  33617. 'open': chatbox.trigger.bind(chatbox, 'show')
  33618. };
  33619. };
  33620. return {
  33621. 'initialize': function (settings, callback) {
  33622. converse.initialize(settings, callback);
  33623. },
  33624. 'contacts': {
  33625. 'get': function (jids) {
  33626. var _transform = function (jid) {
  33627. var contact = converse.roster.get(Strophe.getBareJidFromJid(jid));
  33628. if (contact) {
  33629. return contact.attributes;
  33630. }
  33631. return null;
  33632. };
  33633. if (typeof jids === "string") {
  33634. return _transform(jids);
  33635. }
  33636. return _.map(jids, _transform);
  33637. }
  33638. },
  33639. 'chats': {
  33640. 'get': function (jids) {
  33641. var _transform = function (jid) {
  33642. var chatbox = converse.chatboxes.get(jid);
  33643. if (!chatbox) {
  33644. var roster_item = converse.roster.get(jid);
  33645. if (roster_item === undefined) {
  33646. converse.log('Could not get roster item for JID '+jid, 'error');
  33647. return null;
  33648. }
  33649. chatbox = converse.chatboxes.create({
  33650. 'id': jid,
  33651. 'jid': jid,
  33652. 'fullname': _.isEmpty(roster_item.get('fullname'))? jid: roster_item.get('fullname'),
  33653. 'image_type': roster_item.get('image_type'),
  33654. 'image': roster_item.get('image'),
  33655. 'url': roster_item.get('url')
  33656. });
  33657. }
  33658. return wrappedChatBox(chatbox);
  33659. };
  33660. if (typeof jids === "string") {
  33661. return _transform(jids);
  33662. }
  33663. return _.map(jids, _transform);
  33664. }
  33665. },
  33666. 'tokens': {
  33667. 'get': function (id) {
  33668. if (!converse.expose_rid_and_sid || typeof converse.connection === "undefined") {
  33669. return null;
  33670. }
  33671. if (id.toLowerCase() === 'rid') {
  33672. return converse.connection.rid || converse.connection._proto.rid;
  33673. } else if (id.toLowerCase() === 'sid') {
  33674. return converse.connection.sid || converse.connection._proto.sid;
  33675. }
  33676. }
  33677. },
  33678. 'listen': {
  33679. 'once': function (evt, handler) {
  33680. converse.once(evt, handler);
  33681. },
  33682. 'on': function (evt, handler) {
  33683. converse.on(evt, handler);
  33684. },
  33685. 'not': function (evt, handler) {
  33686. converse.off(evt, handler);
  33687. },
  33688. },
  33689. 'plugins': {
  33690. 'add': function (name, callback) {
  33691. converse.plugins[name] = callback;
  33692. },
  33693. 'remove': function (name) {
  33694. delete converse.plugins[name];
  33695. },
  33696. 'extend': function (obj, attributes) {
  33697. /* Helper method for overriding or extending Converse's Backbone Views or Models
  33698. *
  33699. * When a method is overriden, the original will still be available
  33700. * on the _super attribute of the object being overridden.
  33701. *
  33702. * obj: The Backbone View or Model
  33703. * attributes: A hash of attributes, such as you would pass to Backbone.Model.extend or Backbone.View.extend
  33704. */
  33705. if (!obj.prototype._super) {
  33706. obj.prototype._super = {};
  33707. }
  33708. _.each(attributes, function (value, key) {
  33709. if (key === 'events') {
  33710. obj.prototype[key] = _.extend(value, obj.prototype[key]);
  33711. } else {
  33712. if (typeof key === 'function') {
  33713. obj.prototype._super[key] = obj.prototype[key];
  33714. }
  33715. obj.prototype[key] = value;
  33716. }
  33717. });
  33718. }
  33719. },
  33720. 'env': {
  33721. 'jQuery': $,
  33722. 'Strophe': Strophe,
  33723. '_': _
  33724. },
  33725. // Deprecated API methods
  33726. 'getBuddy': function (jid) {
  33727. converse.log('WARNING: the "getBuddy" API method has been deprecated. Please use "contacts.get" instead');
  33728. return this.contacts.get(jid);
  33729. },
  33730. 'getChatBox': function (jid) {
  33731. converse.log('WARNING: the "getChatBox" API method has been deprecated. Please use "chats.get" instead');
  33732. return this.chats.get(jid);
  33733. },
  33734. 'openChatBox': function (jid) {
  33735. converse.log('WARNING: the "openChatBox" API method has been deprecated. Please use "chats.get(jid).open()" instead');
  33736. var chat = this.chats.get(jid);
  33737. if (chat) { chat.open(); }
  33738. return chat;
  33739. },
  33740. 'getRID': function () {
  33741. converse.log('WARNING: the "getRID" API method has been deprecated. Please use "tokens.get(\'rid\')" instead');
  33742. return this.tokens.get('rid');
  33743. },
  33744. 'getSID': function () {
  33745. converse.log('WARNING: the "getSID" API method has been deprecated. Please use "tokens.get(\'sid\')" instead');
  33746. return this.tokens.get('sid');
  33747. },
  33748. 'once': function (evt, handler) {
  33749. converse.log('WARNING: the "one" API method has been deprecated. Please use "listen.once" instead');
  33750. return this.listen.once(evt, handler);
  33751. },
  33752. 'on': function (evt, handler) {
  33753. converse.log('WARNING: the "on" API method has been deprecated. Please use "listen.on" instead');
  33754. return this.listen.on(evt, handler);
  33755. },
  33756. 'off': function (evt, handler) {
  33757. converse.log('WARNING: the "off" API method has been deprecated. Please use "listen.not" instead');
  33758. return this.listen.not(evt, handler);
  33759. }
  33760. };
  33761. }));
  33762. var config;
  33763. if (typeof(require) === 'undefined') {
  33764. /* XXX: Hack to work around r.js's stupid parsing.
  33765. * We want to save the configuration in a variable so that we can reuse it in
  33766. * tests/main.js.
  33767. */
  33768. require = {
  33769. config: function (c) {
  33770. config = c;
  33771. }
  33772. };
  33773. }
  33774. require.config({
  33775. baseUrl: '.',
  33776. paths: {
  33777. "backbone": "components/backbone/backbone",
  33778. "backbone.browserStorage": "components/backbone.browserStorage/backbone.browserStorage",
  33779. "backbone.overview": "components/backbone.overview/backbone.overview",
  33780. "bootstrap": "components/bootstrap/dist/js/bootstrap", // XXX: Only required for https://conversejs.org website
  33781. "bootstrapJS": "components/bootstrapJS/index", // XXX: Only required for https://conversejs.org website
  33782. "converse-dependencies": "src/deps-website",
  33783. "converse-templates": "src/templates",
  33784. "eventemitter": "components/otr/build/dep/eventemitter",
  33785. "jquery": "components/jquery/dist/jquery",
  33786. "jquery-private": "src/jquery-private",
  33787. "jquery.browser": "components/jquery.browser/index",
  33788. "jquery.easing": "components/jquery-easing-original/index", // XXX: Only required for https://conversejs.org website
  33789. "moment": "components/momentjs/moment",
  33790. "strophe": "components/strophe/strophe",
  33791. "strophe.disco": "components/strophejs-plugins/disco/strophe.disco",
  33792. "strophe.muc": "components/strophe.muc/index",
  33793. "strophe.roster": "src/strophe.roster",
  33794. "strophe.vcard": "components/strophejs-plugins/vcard/strophe.vcard",
  33795. "text": 'components/requirejs-text/text',
  33796. "tpl": 'components/requirejs-tpl-jcbrand/tpl',
  33797. "typeahead": "components/typeahead.js/index",
  33798. "underscore": "components/underscore/underscore",
  33799. "utils": "src/utils",
  33800. // Off-the-record-encryption
  33801. "bigint": "src/bigint",
  33802. "crypto": "src/crypto",
  33803. "crypto.aes": "components/otr/vendor/cryptojs/aes",
  33804. "crypto.cipher-core": "components/otr/vendor/cryptojs/cipher-core",
  33805. "crypto.core": "components/otr/vendor/cryptojs/core",
  33806. "crypto.enc-base64": "components/otr/vendor/cryptojs/enc-base64",
  33807. "crypto.evpkdf": "components/crypto-js-evanvosberg/src/evpkdf",
  33808. "crypto.hmac": "components/otr/vendor/cryptojs/hmac",
  33809. "crypto.md5": "components/crypto-js-evanvosberg/src/md5",
  33810. "crypto.mode-ctr": "components/otr/vendor/cryptojs/mode-ctr",
  33811. "crypto.pad-nopadding": "components/otr/vendor/cryptojs/pad-nopadding",
  33812. "crypto.sha1": "components/otr/vendor/cryptojs/sha1",
  33813. "crypto.sha256": "components/otr/vendor/cryptojs/sha256",
  33814. "salsa20": "components/otr/build/dep/salsa20",
  33815. "otr": "src/otr",
  33816. // Locales paths
  33817. "locales": "locale/locales",
  33818. "jed": "components/jed/jed",
  33819. "af": "locale/af/LC_MESSAGES/af",
  33820. "de": "locale/de/LC_MESSAGES/de",
  33821. "en": "locale/en/LC_MESSAGES/en",
  33822. "es": "locale/es/LC_MESSAGES/es",
  33823. "fr": "locale/fr/LC_MESSAGES/fr",
  33824. "he": "locale/he/LC_MESSAGES/he",
  33825. "hu": "locale/hu/LC_MESSAGES/hu",
  33826. "id": "locale/id/LC_MESSAGES/id",
  33827. "it": "locale/it/LC_MESSAGES/it",
  33828. "ja": "locale/ja/LC_MESSAGES/ja",
  33829. "nl": "locale/nl/LC_MESSAGES/nl",
  33830. "pt_BR": "locale/pt_BR/LC_MESSAGES/pt_BR",
  33831. "ru": "locale/ru/LC_MESSAGES/ru",
  33832. "zh": "locale/zh/LC_MESSAGES/zh",
  33833. // Templates
  33834. "action": "src/templates/action",
  33835. "add_contact_dropdown": "src/templates/add_contact_dropdown",
  33836. "add_contact_form": "src/templates/add_contact_form",
  33837. "change_status_message": "src/templates/change_status_message",
  33838. "chat_status": "src/templates/chat_status",
  33839. "chatarea": "src/templates/chatarea",
  33840. "chatbox": "src/templates/chatbox",
  33841. "chatroom": "src/templates/chatroom",
  33842. "chatroom_password_form": "src/templates/chatroom_password_form",
  33843. "chatroom_sidebar": "src/templates/chatroom_sidebar",
  33844. "chatrooms_tab": "src/templates/chatrooms_tab",
  33845. "chats_panel": "src/templates/chats_panel",
  33846. "choose_status": "src/templates/choose_status",
  33847. "contacts_panel": "src/templates/contacts_panel",
  33848. "contacts_tab": "src/templates/contacts_tab",
  33849. "controlbox": "src/templates/controlbox",
  33850. "controlbox_toggle": "src/templates/controlbox_toggle",
  33851. "field": "src/templates/field",
  33852. "form_captcha": "src/templates/form_captcha",
  33853. "form_checkbox": "src/templates/form_checkbox",
  33854. "form_input": "src/templates/form_input",
  33855. "form_select": "src/templates/form_select",
  33856. "form_textarea": "src/templates/form_textarea",
  33857. "form_username": "src/templates/form_username",
  33858. "group_header": "src/templates/group_header",
  33859. "info": "src/templates/info",
  33860. "login_panel": "src/templates/login_panel",
  33861. "login_tab": "src/templates/login_tab",
  33862. "message": "src/templates/message",
  33863. "new_day": "src/templates/new_day",
  33864. "occupant": "src/templates/occupant",
  33865. "pending_contact": "src/templates/pending_contact",
  33866. "pending_contacts": "src/templates/pending_contacts",
  33867. "register_panel": "src/templates/register_panel",
  33868. "register_tab": "src/templates/register_tab",
  33869. "registration_form": "src/templates/registration_form",
  33870. "registration_request": "src/templates/registration_request",
  33871. "requesting_contact": "src/templates/requesting_contact",
  33872. "requesting_contacts": "src/templates/requesting_contacts",
  33873. "room_description": "src/templates/room_description",
  33874. "room_item": "src/templates/room_item",
  33875. "room_panel": "src/templates/room_panel",
  33876. "roster": "src/templates/roster",
  33877. "roster_item": "src/templates/roster_item",
  33878. "search_contact": "src/templates/search_contact",
  33879. "select_option": "src/templates/select_option",
  33880. "status_option": "src/templates/status_option",
  33881. "toggle_chats": "src/templates/toggle_chats",
  33882. "toolbar": "src/templates/toolbar",
  33883. "trimmed_chat": "src/templates/trimmed_chat"
  33884. },
  33885. map: {
  33886. // '*' means all modules will get 'jquery-private'
  33887. // for their 'jquery' dependency.
  33888. '*': { 'jquery': 'jquery-private' },
  33889. // 'jquery-private' wants the real jQuery module
  33890. // though. If this line was not here, there would
  33891. // be an unresolvable cyclic dependency.
  33892. 'jquery-private': { 'jquery': 'jquery' }
  33893. },
  33894. tpl: {
  33895. // Configuration for requirejs-tpl
  33896. // Use Mustache style syntax for variable interpolation
  33897. templateSettings: {
  33898. evaluate : /\{\[([\s\S]+?)\]\}/g,
  33899. interpolate : /\{\{([\s\S]+?)\}\}/g
  33900. }
  33901. },
  33902. // define module dependencies for modules not using define
  33903. shim: {
  33904. 'underscore': { exports: '_' },
  33905. 'crypto.aes': { deps: ['crypto.cipher-core'] },
  33906. 'crypto.cipher-core': { deps: ['crypto.enc-base64', 'crypto.evpkdf'] },
  33907. 'crypto.enc-base64': { deps: ['crypto.core'] },
  33908. 'crypto.evpkdf': { deps: ['crypto.md5'] },
  33909. 'crypto.hmac': { deps: ['crypto.core'] },
  33910. 'crypto.md5': { deps: ['crypto.core'] },
  33911. 'crypto.mode-ctr': { deps: ['crypto.cipher-core'] },
  33912. 'crypto.pad-nopadding': { deps: ['crypto.cipher-core'] },
  33913. 'crypto.sha1': { deps: ['crypto.core'] },
  33914. 'crypto.sha256': { deps: ['crypto.core'] },
  33915. 'bigint': { deps: ['crypto'] },
  33916. 'strophe': { exports: 'Strophe' },
  33917. 'strophe.disco': { deps: ['strophe'] },
  33918. 'strophe.muc': { deps: ['strophe'] },
  33919. 'strophe.register': { deps: ['strophe'] },
  33920. 'strophe.roster': { deps: ['strophe'] },
  33921. 'strophe.vcard': { deps: ['strophe'] }
  33922. }
  33923. });
  33924. if (typeof(require) === 'function') {
  33925. require(["converse"], function(converse) {
  33926. window.converse = converse;
  33927. });
  33928. }
  33929. ;
  33930. define("main", function(){});