OpenAjaxManagedHub-all.uncompressed.js 192 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125
  1. /*******************************************************************************
  2. * OpenAjax-mashup.js
  3. *
  4. * Reference implementation of the OpenAjax Hub, as specified by OpenAjax Alliance.
  5. * Specification is under development at:
  6. *
  7. * http://www.openajax.org/member/wiki/OpenAjax_Hub_Specification
  8. *
  9. * Copyright 2006-2009 OpenAjax Alliance
  10. *
  11. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  12. * use this file except in compliance with the License. You may obtain a copy
  13. * of the License at http://www.apache.org/licenses/LICENSE-2.0 . Unless
  14. * required by applicable law or agreed to in writing, software distributed
  15. * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  16. * CONDITIONS OF ANY KIND, either express or implied. See the License for the
  17. * specific language governing permissions and limitations under the License.
  18. *
  19. ******************************************************************************/
  20. var OpenAjax = OpenAjax || {};
  21. if ( !OpenAjax.hub ) { // prevent re-definition of the OpenAjax.hub object
  22. OpenAjax.hub = function() {
  23. var libs = {};
  24. var ooh = "org.openajax.hub.";
  25. return /** @scope OpenAjax.hub */ {
  26. implementer: "http://openajax.org",
  27. implVersion: "2.0.4",
  28. specVersion: "2.0",
  29. implExtraData: {},
  30. libraries: libs,
  31. registerLibrary: function(prefix, nsURL, version, extra) {
  32. libs[prefix] = {
  33. prefix: prefix,
  34. namespaceURI: nsURL,
  35. version: version,
  36. extraData: extra
  37. };
  38. this.publish(ooh+"registerLibrary", libs[prefix]);
  39. },
  40. unregisterLibrary: function(prefix) {
  41. this.publish(ooh+"unregisterLibrary", libs[prefix]);
  42. delete libs[prefix];
  43. }
  44. };
  45. }();
  46. /**
  47. * Error
  48. *
  49. * Standard Error names used when the standard functions need to throw Errors.
  50. */
  51. OpenAjax.hub.Error = {
  52. // Either a required argument is missing or an invalid argument was provided
  53. BadParameters: "OpenAjax.hub.Error.BadParameters",
  54. // The specified hub has been disconnected and cannot perform the requested
  55. // operation:
  56. Disconnected: "OpenAjax.hub.Error.Disconnected",
  57. // Container with specified ID already exists:
  58. Duplicate: "OpenAjax.hub.Error.Duplicate",
  59. // The specified ManagedHub has no such Container (or it has been removed)
  60. NoContainer: "OpenAjax.hub.Error.NoContainer",
  61. // The specified ManagedHub or Container has no such subscription
  62. NoSubscription: "OpenAjax.hub.Error.NoSubscription",
  63. // Permission denied by manager's security policy
  64. NotAllowed: "OpenAjax.hub.Error.NotAllowed",
  65. // Wrong communications protocol identifier provided by Container or HubClient
  66. WrongProtocol: "OpenAjax.hub.Error.WrongProtocol",
  67. // A 'tunnelURI' param was specified, but current browser does not support security features
  68. IncompatBrowser: "OpenAjax.hub.Error.IncompatBrowser"
  69. };
  70. /**
  71. * SecurityAlert
  72. *
  73. * Standard codes used when attempted security violations are detected. Unlike
  74. * Errors, these codes are not thrown as exceptions but rather passed into the
  75. * SecurityAlertHandler function registered with the Hub instance.
  76. */
  77. OpenAjax.hub.SecurityAlert = {
  78. // Container did not load (possible frame phishing attack)
  79. LoadTimeout: "OpenAjax.hub.SecurityAlert.LoadTimeout",
  80. // Hub suspects a frame phishing attack against the specified container
  81. FramePhish: "OpenAjax.hub.SecurityAlert.FramePhish",
  82. // Hub detected a message forgery that purports to come to a specified
  83. // container
  84. ForgedMsg: "OpenAjax.hub.SecurityAlert.ForgedMsg"
  85. };
  86. /**
  87. * Debugging Help
  88. *
  89. * OpenAjax.hub.enableDebug
  90. *
  91. * If OpenAjax.hub.enableDebug is set to true, then the "debugger" keyword
  92. * will get hit whenever a user callback throws an exception, thereby
  93. * bringing up the JavaScript debugger.
  94. */
  95. OpenAjax.hub._debugger = function() {
  96. };
  97. ////////////////////////////////////////////////////////////////////////////////
  98. /**
  99. * Hub interface
  100. *
  101. * Hub is implemented on the manager side by ManagedHub and on the client side
  102. * by ClientHub.
  103. */
  104. //OpenAjax.hub.Hub = function() {}
  105. /**
  106. * Subscribe to a topic.
  107. *
  108. * @param {String} topic
  109. * A valid topic string. MAY include wildcards.
  110. * @param {Function} onData
  111. * Callback function that is invoked whenever an event is
  112. * published on the topic
  113. * @param {Object} [scope]
  114. * When onData callback or onComplete callback is invoked,
  115. * the JavaScript "this" keyword refers to this scope object.
  116. * If no scope is provided, default is window.
  117. * @param {Function} [onComplete]
  118. * Invoked to tell the client application whether the
  119. * subscribe operation succeeded or failed.
  120. * @param {*} [subscriberData]
  121. * Client application provides this data, which is handed
  122. * back to the client application in the subscriberData
  123. * parameter of the onData callback function.
  124. *
  125. * @returns subscriptionID
  126. * Identifier representing the subscription. This identifier is an
  127. * arbitrary ID string that is unique within this Hub instance
  128. * @type {String}
  129. *
  130. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  131. * @throws {OpenAjax.hub.Error.BadParameters} if the topic is invalid (e.g. contains an empty token)
  132. */
  133. //OpenAjax.hub.Hub.prototype.subscribe = function( topic, onData, scope, onComplete, subscriberData ) {}
  134. /**
  135. * Publish an event on a topic
  136. *
  137. * @param {String} topic
  138. * A valid topic string. MUST NOT include wildcards.
  139. * @param {*} data
  140. * Valid publishable data. To be portable across different
  141. * Container implementations, this value SHOULD be serializable
  142. * as JSON.
  143. *
  144. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  145. * @throws {OpenAjax.hub.Error.BadParameters} if the topic cannot be published (e.g. contains
  146. * wildcards or empty tokens) or if the data cannot be published (e.g. cannot be serialized as JSON)
  147. */
  148. //OpenAjax.hub.Hub.prototype.publish = function( topic, data ) {}
  149. /**
  150. * Unsubscribe from a subscription
  151. *
  152. * @param {String} subscriptionID
  153. * A subscriptionID returned by Hub.subscribe()
  154. * @param {Function} [onComplete]
  155. * Callback function invoked when unsubscribe completes
  156. * @param {Object} [scope]
  157. * When onComplete callback function is invoked, the JavaScript "this"
  158. * keyword refers to this scope object.
  159. * If no scope is provided, default is window.
  160. *
  161. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  162. * @throws {OpenAjax.hub.Error.NoSubscription} if no such subscription is found
  163. */
  164. //OpenAjax.hub.Hub.prototype.unsubscribe = function( subscriptionID, onComplete, scope ) {}
  165. /**
  166. * Return true if this Hub instance is in the Connected state.
  167. * Else returns false.
  168. *
  169. * This function can be called even if the Hub is not in a CONNECTED state.
  170. *
  171. * @returns Boolean
  172. * @type {Boolean}
  173. */
  174. //OpenAjax.hub.Hub.prototype.isConnected = function() {}
  175. /**
  176. * Returns the scope associated with this Hub instance and which will be used
  177. * with callback functions.
  178. *
  179. * This function can be called even if the Hub is not in a CONNECTED state.
  180. *
  181. * @returns scope object
  182. * @type {Object}
  183. */
  184. //OpenAjax.hub.Hub.prototype.getScope = function() {}
  185. /**
  186. * Returns the subscriberData parameter that was provided when
  187. * Hub.subscribe was called.
  188. *
  189. * @param {String} subscriptionID
  190. * The subscriberID of a subscription
  191. *
  192. * @returns subscriberData
  193. * @type {*}
  194. *
  195. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  196. * @throws {OpenAjax.hub.Error.NoSubscription} if there is no such subscription
  197. */
  198. //OpenAjax.hub.Hub.prototype.getSubscriberData = function(subscriptionID) {}
  199. /**
  200. * Returns the scope associated with a specified subscription. This scope will
  201. * be used when invoking the 'onData' callback supplied to Hub.subscribe().
  202. *
  203. * @param {String} subscriberID
  204. * The subscriberID of a subscription
  205. *
  206. * @returns scope
  207. * @type {*}
  208. *
  209. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  210. * @throws {OpenAjax.hub.Error.NoSubscription} if there is no such subscription
  211. */
  212. //OpenAjax.hub.Hub.prototype.getSubscriberScope = function(subscriberID) {}
  213. /**
  214. * Returns the params object associated with this Hub instance.
  215. *
  216. * @returns params
  217. * The params object associated with this Hub instance
  218. * @type {Object}
  219. */
  220. //OpenAjax.hub.Hub.prototype.getParameters = function() {}
  221. ////////////////////////////////////////////////////////////////////////////////
  222. /**
  223. * HubClient interface
  224. *
  225. * Extends Hub interface.
  226. *
  227. * A HubClient implementation is typically specific to a particular
  228. * implementation of Container.
  229. */
  230. /**
  231. * Create a new HubClient. All HubClient constructors MUST have this
  232. * signature.
  233. * @constructor
  234. *
  235. * @param {Object} params
  236. * Parameters used to instantiate the HubClient.
  237. * Once the constructor is called, the params object belongs to the
  238. * HubClient. The caller MUST not modify it.
  239. * Implementations of HubClient may specify additional properties
  240. * for the params object, besides those identified below.
  241. *
  242. * @param {Function} params.HubClient.onSecurityAlert
  243. * Called when an attempted security breach is thwarted
  244. * @param {Object} [params.HubClient.scope]
  245. * Whenever one of the HubClient's callback functions is called,
  246. * references to "this" in the callback will refer to the scope object.
  247. * If not provided, the default is window.
  248. * @param {Function} [params.HubClient.log]
  249. * Optional logger function. Would be used to log to console.log or
  250. * equivalent.
  251. *
  252. * @throws {OpenAjax.hub.Error.BadParameters} if any of the required
  253. * parameters is missing, or if a parameter value is invalid in
  254. * some way.
  255. */
  256. //OpenAjax.hub.HubClient = function( params ) {}
  257. /**
  258. * Requests a connection to the ManagedHub, via the Container
  259. * associated with this HubClient.
  260. *
  261. * If the Container accepts the connection request, the HubClient's
  262. * state is set to CONNECTED and the HubClient invokes the
  263. * onComplete callback function.
  264. *
  265. * If the Container refuses the connection request, the HubClient
  266. * invokes the onComplete callback function with an error code.
  267. * The error code might, for example, indicate that the Container
  268. * is being destroyed.
  269. *
  270. * In most implementations, this function operates asynchronously,
  271. * so the onComplete callback function is the only reliable way to
  272. * determine when this function completes and whether it has succeeded
  273. * or failed.
  274. *
  275. * A client application may call HubClient.disconnect and then call
  276. * HubClient.connect.
  277. *
  278. * @param {Function} [onComplete]
  279. * Callback function to call when this operation completes.
  280. * @param {Object} [scope]
  281. * When the onComplete function is invoked, the JavaScript "this"
  282. * keyword refers to this scope object.
  283. * If no scope is provided, default is window.
  284. *
  285. * @throws {OpenAjax.hub.Error.Duplicate} if the HubClient is already connected
  286. */
  287. //OpenAjax.hub.HubClient.prototype.connect = function( onComplete, scope ) {}
  288. /**
  289. * Disconnect from the ManagedHub
  290. *
  291. * Disconnect immediately:
  292. *
  293. * 1. Sets the HubClient's state to DISCONNECTED.
  294. * 2. Causes the HubClient to send a Disconnect request to the
  295. * associated Container.
  296. * 3. Ensures that the client application will receive no more
  297. * onData or onComplete callbacks associated with this
  298. * connection, except for the disconnect function's own
  299. * onComplete callback.
  300. * 4. Automatically destroys all of the HubClient's subscriptions.
  301. *
  302. * In most implementations, this function operates asynchronously,
  303. * so the onComplete callback function is the only reliable way to
  304. * determine when this function completes and whether it has succeeded
  305. * or failed.
  306. *
  307. * A client application is allowed to call HubClient.disconnect and
  308. * then call HubClient.connect.
  309. *
  310. * @param {Function} [onComplete]
  311. * Callback function to call when this operation completes.
  312. * @param {Object} [scope]
  313. * When the onComplete function is invoked, the JavaScript "this"
  314. * keyword refers to the scope object.
  315. * If no scope is provided, default is window.
  316. *
  317. * @throws {OpenAjax.hub.Error.Disconnected} if the HubClient is already
  318. * disconnected
  319. */
  320. //OpenAjax.hub.HubClient.prototype.disconnect = function( onComplete, scope ) {}
  321. /**
  322. * If DISCONNECTED: Returns null
  323. * If CONNECTED: Returns the origin associated with the window containing the
  324. * Container associated with this HubClient instance. The origin has the format
  325. *
  326. * [protocol]://[host]
  327. *
  328. * where:
  329. *
  330. * [protocol] is "http" or "https"
  331. * [host] is the hostname of the partner page.
  332. *
  333. * @returns Partner's origin
  334. * @type {String}
  335. */
  336. //OpenAjax.hub.HubClient.prototype.getPartnerOrigin = function() {}
  337. /**
  338. * Returns the client ID of this HubClient
  339. *
  340. * @returns clientID
  341. * @type {String}
  342. */
  343. //OpenAjax.hub.HubClient.prototype.getClientID = function() {}
  344. ////////////////////////////////////////////////////////////////////////////////
  345. /**
  346. * OpenAjax.hub.ManagedHub
  347. *
  348. * Managed hub API for the manager application and for Containers.
  349. *
  350. * Implements OpenAjax.hub.Hub.
  351. */
  352. /**
  353. * Create a new ManagedHub instance
  354. * @constructor
  355. *
  356. * This constructor automatically sets the ManagedHub's state to
  357. * CONNECTED.
  358. *
  359. * @param {Object} params
  360. * Parameters used to instantiate the ManagedHub.
  361. * Once the constructor is called, the params object belongs exclusively to
  362. * the ManagedHub. The caller MUST not modify it.
  363. *
  364. * The params object may contain the following properties:
  365. *
  366. * @param {Function} params.onPublish
  367. * Callback function that is invoked whenever a
  368. * data value published by a Container is about
  369. * to be delivered to some (possibly the same) Container.
  370. * This callback function implements a security policy;
  371. * it returns true if the delivery of the data is
  372. * permitted and false if permission is denied.
  373. * @param {Function} params.onSubscribe
  374. * Called whenever a Container tries to subscribe
  375. * on behalf of its client.
  376. * This callback function implements a security policy;
  377. * it returns true if the subscription is permitted
  378. * and false if permission is denied.
  379. * @param {Function} [params.onUnsubscribe]
  380. * Called whenever a Container unsubscribes on behalf of its client.
  381. * Unlike the other callbacks, onUnsubscribe is intended only for
  382. * informative purposes, and is not used to implement a security
  383. * policy.
  384. * @param {Object} [params.scope]
  385. * Whenever one of the ManagedHub's callback functions is called,
  386. * references to the JavaScript "this" keyword in the callback
  387. * function refer to this scope object
  388. * If no scope is provided, default is window.
  389. * @param {Function} [params.log] Optional logger function. Would
  390. * be used to log to console.log or equivalent.
  391. *
  392. * @throws {OpenAjax.hub.Error.BadParameters} if any of the required
  393. * parameters are missing
  394. */
  395. OpenAjax.hub.ManagedHub = function( params ) {
  396. if ( ! params || ! params.onPublish || ! params.onSubscribe )
  397. throw new Error( OpenAjax.hub.Error.BadParameters );
  398. this._p = params;
  399. this._onUnsubscribe = params.onUnsubscribe ? params.onUnsubscribe : null;
  400. this._scope = params.scope || window;
  401. if ( params.log ) {
  402. var that = this;
  403. this._log = function( msg ) {
  404. try {
  405. params.log.call( that._scope, "ManagedHub: " + msg );
  406. } catch( e ) {
  407. OpenAjax.hub._debugger();
  408. }
  409. };
  410. } else {
  411. this._log = function() {};
  412. }
  413. this._subscriptions = { c:{}, s:null };
  414. this._containers = {};
  415. // Sequence # used to create IDs that are unique within this hub
  416. this._seq = 0;
  417. this._active = true;
  418. this._isPublishing = false;
  419. this._pubQ = [];
  420. };
  421. /**
  422. * Subscribe to a topic on behalf of a Container. Called only by
  423. * Container implementations, NOT by manager applications.
  424. *
  425. * This function:
  426. * 1. Checks with the ManagedHub's onSubscribe security policy
  427. * to determine whether this Container is allowed to subscribe
  428. * to this topic.
  429. * 2. If the subscribe operation is permitted, subscribes to the
  430. * topic and returns the ManagedHub's subscription ID for this
  431. * subscription.
  432. * 3. If the subscribe operation is not permitted, throws
  433. * OpenAjax.hub.Error.NotAllowed.
  434. *
  435. * When data is published on the topic, the ManagedHub's
  436. * onPublish security policy will be invoked to ensure that
  437. * this Container is permitted to receive the published data.
  438. * If the Container is allowed to receive the data, then the
  439. * Container's sendToClient function will be invoked.
  440. *
  441. * When a Container needs to create a subscription on behalf of
  442. * its client, the Container MUST use this function to create
  443. * the subscription.
  444. *
  445. * @param {OpenAjax.hub.Container} container
  446. * A Container
  447. * @param {String} topic
  448. * A valid topic
  449. * @param {String} containerSubID
  450. * Arbitrary string ID that the Container uses to
  451. * represent the subscription. Must be unique within the
  452. * context of the Container
  453. *
  454. * @returns managerSubID
  455. * Arbitrary string ID that this ManagedHub uses to
  456. * represent the subscription. Will be unique within the
  457. * context of this ManagedHub
  458. * @type {String}
  459. *
  460. * @throws {OpenAjax.hub.Error.Disconnected} if this.isConnected() returns false
  461. * @throws {OpenAjax.hub.Error.NotAllowed} if subscription request is denied by the onSubscribe security policy
  462. * @throws {OpenAjax.hub.Error.BadParameters} if one of the parameters, e.g. the topic, is invalid
  463. */
  464. OpenAjax.hub.ManagedHub.prototype.subscribeForClient = function( container, topic, containerSubID )
  465. {
  466. this._assertConn();
  467. // check subscribe permission
  468. if ( this._invokeOnSubscribe( topic, container ) ) {
  469. // return ManagedHub's subscriptionID for this subscription
  470. return this._subscribe( topic, this._sendToClient, this, { c: container, sid: containerSubID } );
  471. }
  472. throw new Error(OpenAjax.hub.Error.NotAllowed);
  473. };
  474. /**
  475. * Unsubscribe from a subscription on behalf of a Container. Called only by
  476. * Container implementations, NOT by manager application code.
  477. *
  478. * This function:
  479. * 1. Destroys the specified subscription
  480. * 2. Calls the ManagedHub's onUnsubscribe callback function
  481. *
  482. * This function can be called even if the ManagedHub is not in a CONNECTED state.
  483. *
  484. * @param {OpenAjax.hub.Container} container
  485. * container instance that is unsubscribing
  486. * @param {String} managerSubID
  487. * opaque ID of a subscription, returned by previous call to subscribeForClient()
  488. *
  489. * @throws {OpenAjax.hub.Error.NoSubscription} if subscriptionID does not refer to a valid subscription
  490. */
  491. OpenAjax.hub.ManagedHub.prototype.unsubscribeForClient = function( container, managerSubID )
  492. {
  493. this._unsubscribe( managerSubID );
  494. this._invokeOnUnsubscribe( container, managerSubID );
  495. };
  496. /**
  497. * Publish data on a topic on behalf of a Container. Called only by
  498. * Container implementations, NOT by manager application code.
  499. *
  500. * @param {OpenAjax.hub.Container} container
  501. * Container on whose behalf data should be published
  502. * @param {String} topic
  503. * Valid topic string. Must NOT contain wildcards.
  504. * @param {*} data
  505. * Valid publishable data. To be portable across different
  506. * Container implementations, this value SHOULD be serializable
  507. * as JSON.
  508. *
  509. * @throws {OpenAjax.hub.Error.Disconnected} if this.isConnected() returns false
  510. * @throws {OpenAjax.hub.Error.BadParameters} if one of the parameters, e.g. the topic, is invalid
  511. */
  512. OpenAjax.hub.ManagedHub.prototype.publishForClient = function( container, topic, data )
  513. {
  514. this._assertConn();
  515. this._publish( topic, data, container );
  516. };
  517. /**
  518. * Destroy this ManagedHub
  519. *
  520. * 1. Sets state to DISCONNECTED. All subsequent attempts to add containers,
  521. * publish or subscribe will throw the Disconnected error. We will
  522. * continue to allow "cleanup" operations such as removeContainer
  523. * and unsubscribe, as well as read-only operations such as
  524. * isConnected
  525. * 2. Remove all Containers associated with this ManagedHub
  526. */
  527. OpenAjax.hub.ManagedHub.prototype.disconnect = function()
  528. {
  529. this._active = false;
  530. for (var c in this._containers) {
  531. this.removeContainer( this._containers[c] );
  532. }
  533. };
  534. /**
  535. * Get a container belonging to this ManagedHub by its clientID, or null
  536. * if this ManagedHub has no such container
  537. *
  538. * This function can be called even if the ManagedHub is not in a CONNECTED state.
  539. *
  540. * @param {String} containerId
  541. * Arbitrary string ID associated with the container
  542. *
  543. * @returns container associated with given ID
  544. * @type {OpenAjax.hub.Container}
  545. */
  546. OpenAjax.hub.ManagedHub.prototype.getContainer = function( containerId )
  547. {
  548. var container = this._containers[containerId];
  549. return container ? container : null;
  550. };
  551. /**
  552. * Returns an array listing all containers belonging to this ManagedHub.
  553. * The order of the Containers in this array is arbitrary.
  554. *
  555. * This function can be called even if the ManagedHub is not in a CONNECTED state.
  556. *
  557. * @returns container array
  558. * @type {OpenAjax.hub.Container[]}
  559. */
  560. OpenAjax.hub.ManagedHub.prototype.listContainers = function()
  561. {
  562. var res = [];
  563. for (var c in this._containers) {
  564. res.push(this._containers[c]);
  565. }
  566. return res;
  567. };
  568. /**
  569. * Add a container to this ManagedHub.
  570. *
  571. * This function should only be called by a Container constructor.
  572. *
  573. * @param {OpenAjax.hub.Container} container
  574. * A Container to be added to this ManagedHub
  575. *
  576. * @throws {OpenAjax.hub.Error.Duplicate} if there is already a Container
  577. * in this ManagedHub whose clientId is the same as that of container
  578. * @throws {OpenAjax.hub.Error.Disconnected} if this.isConnected() returns false
  579. */
  580. OpenAjax.hub.ManagedHub.prototype.addContainer = function( container )
  581. {
  582. this._assertConn();
  583. var containerId = container.getClientID();
  584. if ( this._containers[containerId] ) {
  585. throw new Error(OpenAjax.hub.Error.Duplicate);
  586. }
  587. this._containers[containerId] = container;
  588. };
  589. /**
  590. * Remove a container from this ManagedHub immediately
  591. *
  592. * This function can be called even if the ManagedHub is not in a CONNECTED state.
  593. *
  594. * @param {OpenAjax.hub.Container} container
  595. * A Container to be removed from this ManagedHub
  596. *
  597. * @throws {OpenAjax.hub.Error.NoContainer} if no such container is found
  598. */
  599. OpenAjax.hub.ManagedHub.prototype.removeContainer = function( container )
  600. {
  601. var containerId = container.getClientID();
  602. if ( ! this._containers[ containerId ] ) {
  603. throw new Error(OpenAjax.hub.Error.NoContainer);
  604. }
  605. container.remove();
  606. delete this._containers[ containerId ];
  607. };
  608. /*** OpenAjax.hub.Hub interface implementation ***/
  609. /**
  610. * Subscribe to a topic.
  611. *
  612. * This implementation of Hub.subscribe is synchronous. When subscribe
  613. * is called:
  614. *
  615. * 1. The ManagedHub's onSubscribe callback is invoked. The
  616. * container parameter is null, because the manager application,
  617. * rather than a container, is subscribing.
  618. * 2. If onSubscribe returns true, then the subscription is created.
  619. * 3. The onComplete callback is invoked.
  620. * 4. Then this function returns.
  621. *
  622. * @param {String} topic
  623. * A valid topic string. MAY include wildcards.
  624. * @param {Function} onData
  625. * Callback function that is invoked whenever an event is
  626. * published on the topic
  627. * @param {Object} [scope]
  628. * When onData callback or onComplete callback is invoked,
  629. * the JavaScript "this" keyword refers to this scope object.
  630. * If no scope is provided, default is window.
  631. * @param {Function} [onComplete]
  632. * Invoked to tell the client application whether the
  633. * subscribe operation succeeded or failed.
  634. * @param {*} [subscriberData]
  635. * Client application provides this data, which is handed
  636. * back to the client application in the subscriberData
  637. * parameter of the onData and onComplete callback functions.
  638. *
  639. * @returns subscriptionID
  640. * Identifier representing the subscription. This identifier is an
  641. * arbitrary ID string that is unique within this Hub instance
  642. * @type {String}
  643. *
  644. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  645. * @throws {OpenAjax.hub.Error.BadParameters} if the topic is invalid (e.g. contains an empty token)
  646. */
  647. OpenAjax.hub.ManagedHub.prototype.subscribe = function( topic, onData, scope, onComplete, subscriberData )
  648. {
  649. this._assertConn();
  650. this._assertSubTopic(topic);
  651. if ( ! onData ) {
  652. throw new Error( OpenAjax.hub.Error.BadParameters );
  653. }
  654. scope = scope || window;
  655. // check subscribe permission
  656. if ( ! this._invokeOnSubscribe( topic, null ) ) {
  657. this._invokeOnComplete( onComplete, scope, null, false, OpenAjax.hub.Error.NotAllowed );
  658. return;
  659. }
  660. // on publish event, check publish permissions
  661. var that = this;
  662. function publishCB( topic, data, sd, pcont ) {
  663. if ( that._invokeOnPublish( topic, data, pcont, null ) ) {
  664. try {
  665. onData.call( scope, topic, data, subscriberData );
  666. } catch( e ) {
  667. OpenAjax.hub._debugger();
  668. that._log( "caught error from onData callback to Hub.subscribe(): " + e.message );
  669. }
  670. }
  671. }
  672. var subID = this._subscribe( topic, publishCB, scope, subscriberData );
  673. this._invokeOnComplete( onComplete, scope, subID, true );
  674. return subID;
  675. };
  676. /**
  677. * Publish an event on a topic
  678. *
  679. * This implementation of Hub.publish is synchronous. When publish
  680. * is called:
  681. *
  682. * 1. The target subscriptions are identified.
  683. * 2. For each target subscription, the ManagedHub's onPublish
  684. * callback is invoked. Data is only delivered to a target
  685. * subscription if the onPublish callback returns true.
  686. * The pcont parameter of the onPublish callback is null.
  687. * This is because the ManagedHub, rather than a container,
  688. * is publishing the data.
  689. *
  690. * @param {String} topic
  691. * A valid topic string. MUST NOT include wildcards.
  692. * @param {*} data
  693. * Valid publishable data. To be portable across different
  694. * Container implementations, this value SHOULD be serializable
  695. * as JSON.
  696. *
  697. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  698. * @throws {OpenAjax.hub.Error.BadParameters} if the topic cannot be published (e.g. contains
  699. * wildcards or empty tokens) or if the data cannot be published (e.g. cannot be serialized as JSON)
  700. */
  701. OpenAjax.hub.ManagedHub.prototype.publish = function( topic, data )
  702. {
  703. this._assertConn();
  704. this._assertPubTopic(topic);
  705. this._publish( topic, data, null );
  706. };
  707. /**
  708. * Unsubscribe from a subscription
  709. *
  710. * This implementation of Hub.unsubscribe is synchronous. When unsubscribe
  711. * is called:
  712. *
  713. * 1. The subscription is destroyed.
  714. * 2. The ManagedHub's onUnsubscribe callback is invoked, if there is one.
  715. * 3. The onComplete callback is invoked.
  716. * 4. Then this function returns.
  717. *
  718. * @param {String} subscriptionID
  719. * A subscriptionID returned by Hub.subscribe()
  720. * @param {Function} [onComplete]
  721. * Callback function invoked when unsubscribe completes
  722. * @param {Object} [scope]
  723. * When onComplete callback function is invoked, the JavaScript "this"
  724. * keyword refers to this scope object.
  725. * If no scope is provided, default is window.
  726. *
  727. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  728. * @throws {OpenAjax.hub.Error.NoSubscription} if no such subscription is found
  729. */
  730. OpenAjax.hub.ManagedHub.prototype.unsubscribe = function( subscriptionID, onComplete, scope )
  731. {
  732. this._assertConn();
  733. if ( ! subscriptionID ) {
  734. throw new Error( OpenAjax.hub.Error.BadParameters );
  735. }
  736. this._unsubscribe( subscriptionID );
  737. this._invokeOnUnsubscribe( null, subscriptionID );
  738. this._invokeOnComplete( onComplete, scope, subscriptionID, true );
  739. };
  740. /**
  741. * Returns true if disconnect() has NOT been called on this ManagedHub,
  742. * else returns false
  743. *
  744. * @returns Boolean
  745. * @type {Boolean}
  746. */
  747. OpenAjax.hub.ManagedHub.prototype.isConnected = function()
  748. {
  749. return this._active;
  750. };
  751. /**
  752. * Returns the scope associated with this Hub instance and which will be used
  753. * with callback functions.
  754. *
  755. * This function can be called even if the Hub is not in a CONNECTED state.
  756. *
  757. * @returns scope object
  758. * @type {Object}
  759. */
  760. OpenAjax.hub.ManagedHub.prototype.getScope = function()
  761. {
  762. return this._scope;
  763. };
  764. /**
  765. * Returns the subscriberData parameter that was provided when
  766. * Hub.subscribe was called.
  767. *
  768. * @param subscriberID
  769. * The subscriberID of a subscription
  770. *
  771. * @returns subscriberData
  772. * @type {*}
  773. *
  774. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  775. * @throws {OpenAjax.hub.Error.NoSubscription} if there is no such subscription
  776. */
  777. OpenAjax.hub.ManagedHub.prototype.getSubscriberData = function( subscriberID )
  778. {
  779. this._assertConn();
  780. var path = subscriberID.split(".");
  781. var sid = path.pop();
  782. var sub = this._getSubscriptionObject( this._subscriptions, path, 0, sid );
  783. if ( sub )
  784. return sub.data;
  785. throw new Error( OpenAjax.hub.Error.NoSubscription );
  786. };
  787. /**
  788. * Returns the scope associated with a specified subscription. This scope will
  789. * be used when invoking the 'onData' callback supplied to Hub.subscribe().
  790. *
  791. * @param subscriberID
  792. * The subscriberID of a subscription
  793. *
  794. * @returns scope
  795. * @type {*}
  796. *
  797. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  798. * @throws {OpenAjax.hub.Error.NoSubscription} if there is no such subscription
  799. */
  800. OpenAjax.hub.ManagedHub.prototype.getSubscriberScope = function( subscriberID )
  801. {
  802. this._assertConn();
  803. var path = subscriberID.split(".");
  804. var sid = path.pop();
  805. var sub = this._getSubscriptionObject( this._subscriptions, path, 0, sid );
  806. if ( sub ) {
  807. return sub.scope;
  808. }
  809. throw new Error( OpenAjax.hub.Error.NoSubscription );
  810. };
  811. /**
  812. * Returns the params object associated with this Hub instance.
  813. * Allows mix-in code to access parameters passed into constructor that created
  814. * this Hub instance.
  815. *
  816. * @returns params the params object associated with this Hub instance
  817. * @type {Object}
  818. */
  819. OpenAjax.hub.ManagedHub.prototype.getParameters = function()
  820. {
  821. return this._p;
  822. };
  823. /* PRIVATE FUNCTIONS */
  824. /**
  825. * Send a message to a container's client.
  826. * This is an OAH subscriber's data callback. It is private to ManagedHub
  827. * and serves as an adapter between the OAH 1.0 API and Container.sendToClient.
  828. *
  829. * @param {String} topic Topic on which data was published
  830. * @param {Object} data Data to be delivered to the client
  831. * @param {Object} sd Object containing properties
  832. * c: container to which data must be sent
  833. * sid: subscription ID within that container
  834. * @param {Object} pcont Publishing container, or null if this data was
  835. * published by the manager
  836. */
  837. OpenAjax.hub.ManagedHub.prototype._sendToClient = function(topic, data, sd, pcont)
  838. {
  839. if (!this.isConnected()) {
  840. return;
  841. }
  842. if ( this._invokeOnPublish( topic, data, pcont, sd.c ) ) {
  843. sd.c.sendToClient( topic, data, sd.sid );
  844. }
  845. };
  846. OpenAjax.hub.ManagedHub.prototype._assertConn = function()
  847. {
  848. if (!this.isConnected()) {
  849. throw new Error(OpenAjax.hub.Error.Disconnected);
  850. }
  851. };
  852. OpenAjax.hub.ManagedHub.prototype._assertPubTopic = function(topic)
  853. {
  854. if ( !topic || topic === "" || (topic.indexOf("*") != -1) ||
  855. (topic.indexOf("..") != -1) || (topic.charAt(0) == ".") ||
  856. (topic.charAt(topic.length-1) == "."))
  857. {
  858. throw new Error(OpenAjax.hub.Error.BadParameters);
  859. }
  860. };
  861. OpenAjax.hub.ManagedHub.prototype._assertSubTopic = function(topic)
  862. {
  863. if ( ! topic ) {
  864. throw new Error(OpenAjax.hub.Error.BadParameters);
  865. }
  866. var path = topic.split(".");
  867. var len = path.length;
  868. for (var i = 0; i < len; i++) {
  869. var p = path[i];
  870. if ((p === "") ||
  871. ((p.indexOf("*") != -1) && (p != "*") && (p != "**"))) {
  872. throw new Error(OpenAjax.hub.Error.BadParameters);
  873. }
  874. if ((p == "**") && (i < len - 1)) {
  875. throw new Error(OpenAjax.hub.Error.BadParameters);
  876. }
  877. }
  878. };
  879. OpenAjax.hub.ManagedHub.prototype._invokeOnComplete = function( func, scope, item, success, errorCode )
  880. {
  881. if ( func ) { // onComplete is optional
  882. try {
  883. scope = scope || window;
  884. func.call( scope, item, success, errorCode );
  885. } catch( e ) {
  886. OpenAjax.hub._debugger();
  887. this._log( "caught error from onComplete callback: " + e.message );
  888. }
  889. }
  890. };
  891. OpenAjax.hub.ManagedHub.prototype._invokeOnPublish = function( topic, data, pcont, scont )
  892. {
  893. try {
  894. return this._p.onPublish.call( this._scope, topic, data, pcont, scont );
  895. } catch( e ) {
  896. OpenAjax.hub._debugger();
  897. this._log( "caught error from onPublish callback to constructor: " + e.message );
  898. }
  899. return false;
  900. };
  901. OpenAjax.hub.ManagedHub.prototype._invokeOnSubscribe = function( topic, container )
  902. {
  903. try {
  904. return this._p.onSubscribe.call( this._scope, topic, container );
  905. } catch( e ) {
  906. OpenAjax.hub._debugger();
  907. this._log( "caught error from onSubscribe callback to constructor: " + e.message );
  908. }
  909. return false;
  910. };
  911. OpenAjax.hub.ManagedHub.prototype._invokeOnUnsubscribe = function( container, managerSubID )
  912. {
  913. if ( this._onUnsubscribe ) {
  914. var topic = managerSubID.slice( 0, managerSubID.lastIndexOf(".") );
  915. try {
  916. this._onUnsubscribe.call( this._scope, topic, container );
  917. } catch( e ) {
  918. OpenAjax.hub._debugger();
  919. this._log( "caught error from onUnsubscribe callback to constructor: " + e.message );
  920. }
  921. }
  922. };
  923. OpenAjax.hub.ManagedHub.prototype._subscribe = function( topic, onData, scope, subscriberData )
  924. {
  925. var handle = topic + "." + this._seq;
  926. var sub = { scope: scope, cb: onData, data: subscriberData, sid: this._seq++ };
  927. var path = topic.split(".");
  928. this._recursiveSubscribe( this._subscriptions, path, 0, sub );
  929. return handle;
  930. };
  931. OpenAjax.hub.ManagedHub.prototype._recursiveSubscribe = function(tree, path, index, sub)
  932. {
  933. var token = path[index];
  934. if (index == path.length) {
  935. sub.next = tree.s;
  936. tree.s = sub;
  937. } else {
  938. if (typeof tree.c == "undefined") {
  939. tree.c = {};
  940. }
  941. if (typeof tree.c[token] == "undefined") {
  942. tree.c[token] = { c: {}, s: null };
  943. this._recursiveSubscribe(tree.c[token], path, index + 1, sub);
  944. } else {
  945. this._recursiveSubscribe( tree.c[token], path, index + 1, sub);
  946. }
  947. }
  948. };
  949. OpenAjax.hub.ManagedHub.prototype._publish = function( topic, data, pcont )
  950. {
  951. // if we are currently handling a publish event, then queue this request
  952. // and handle later, one by one
  953. if ( this._isPublishing ) {
  954. this._pubQ.push( { t: topic, d: data, p: pcont } );
  955. return;
  956. }
  957. this._safePublish( topic, data, pcont );
  958. while ( this._pubQ.length > 0 ) {
  959. var pub = this._pubQ.shift();
  960. this._safePublish( pub.t, pub.d, pub.p );
  961. }
  962. };
  963. OpenAjax.hub.ManagedHub.prototype._safePublish = function( topic, data, pcont )
  964. {
  965. this._isPublishing = true;
  966. var path = topic.split(".");
  967. this._recursivePublish( this._subscriptions, path, 0, topic, data, pcont );
  968. this._isPublishing = false;
  969. };
  970. OpenAjax.hub.ManagedHub.prototype._recursivePublish = function(tree, path, index, name, msg, pcont)
  971. {
  972. if (typeof tree != "undefined") {
  973. var node;
  974. if (index == path.length) {
  975. node = tree;
  976. } else {
  977. this._recursivePublish(tree.c[path[index]], path, index + 1, name, msg, pcont);
  978. this._recursivePublish(tree.c["*"], path, index + 1, name, msg, pcont);
  979. node = tree.c["**"];
  980. }
  981. if (typeof node != "undefined") {
  982. var sub = node.s;
  983. while ( sub ) {
  984. var sc = sub.scope;
  985. var cb = sub.cb;
  986. var d = sub.data;
  987. if (typeof cb == "string") {
  988. // get a function object
  989. cb = sc[cb];
  990. }
  991. cb.call(sc, name, msg, d, pcont);
  992. sub = sub.next;
  993. }
  994. }
  995. }
  996. };
  997. OpenAjax.hub.ManagedHub.prototype._unsubscribe = function( subscriptionID )
  998. {
  999. var path = subscriptionID.split(".");
  1000. var sid = path.pop();
  1001. if ( ! this._recursiveUnsubscribe( this._subscriptions, path, 0, sid ) ) {
  1002. throw new Error( OpenAjax.hub.Error.NoSubscription );
  1003. }
  1004. };
  1005. /**
  1006. * @returns 'true' if properly unsubscribed; 'false' otherwise
  1007. */
  1008. OpenAjax.hub.ManagedHub.prototype._recursiveUnsubscribe = function(tree, path, index, sid)
  1009. {
  1010. if ( typeof tree == "undefined" ) {
  1011. return false;
  1012. }
  1013. if (index < path.length) {
  1014. var childNode = tree.c[path[index]];
  1015. if ( ! childNode ) {
  1016. return false;
  1017. }
  1018. this._recursiveUnsubscribe(childNode, path, index + 1, sid);
  1019. if ( ! childNode.s ) {
  1020. for (var x in childNode.c) {
  1021. return true;
  1022. }
  1023. delete tree.c[path[index]];
  1024. }
  1025. } else {
  1026. var sub = tree.s;
  1027. var sub_prev = null;
  1028. var found = false;
  1029. while ( sub ) {
  1030. if ( sid == sub.sid ) {
  1031. found = true;
  1032. if ( sub == tree.s ) {
  1033. tree.s = sub.next;
  1034. } else {
  1035. sub_prev.next = sub.next;
  1036. }
  1037. break;
  1038. }
  1039. sub_prev = sub;
  1040. sub = sub.next;
  1041. }
  1042. if ( ! found ) {
  1043. return false;
  1044. }
  1045. }
  1046. return true;
  1047. };
  1048. OpenAjax.hub.ManagedHub.prototype._getSubscriptionObject = function( tree, path, index, sid )
  1049. {
  1050. if (typeof tree != "undefined") {
  1051. if (index < path.length) {
  1052. var childNode = tree.c[path[index]];
  1053. return this._getSubscriptionObject(childNode, path, index + 1, sid);
  1054. }
  1055. var sub = tree.s;
  1056. while ( sub ) {
  1057. if ( sid == sub.sid ) {
  1058. return sub;
  1059. }
  1060. sub = sub.next;
  1061. }
  1062. }
  1063. return null;
  1064. };
  1065. ////////////////////////////////////////////////////////////////////////////////
  1066. /**
  1067. * Container
  1068. * @constructor
  1069. *
  1070. * Container represents an instance of a manager-side object that contains and
  1071. * communicates with a single client of the hub. The container might be an inline
  1072. * container, an iframe FIM container, or an iframe PostMessage container, or
  1073. * it might be an instance of some other implementation.
  1074. *
  1075. * @param {OpenAjax.hub.ManagedHub} hub
  1076. * Managed Hub instance
  1077. * @param {String} clientID
  1078. * A string ID that identifies a particular client of a Managed Hub. Unique
  1079. * within the context of the ManagedHub.
  1080. * @param {Object} params
  1081. * Parameters used to instantiate the Container.
  1082. * Once the constructor is called, the params object belongs exclusively to
  1083. * the Container. The caller MUST not modify it.
  1084. * Implementations of Container may specify additional properties
  1085. * for the params object, besides those identified below.
  1086. * The following params properties MUST be supported by all Container
  1087. * implementations:
  1088. * @param {Function} params.Container.onSecurityAlert
  1089. * Called when an attempted security breach is thwarted. Function is defined
  1090. * as follows: function(container, securityAlert)
  1091. * @param {Function} [params.Container.onConnect]
  1092. * Called when the client connects to the Managed Hub. Function is defined
  1093. * as follows: function(container)
  1094. * @param {Function} [params.Container.onDisconnect]
  1095. * Called when the client disconnects from the Managed Hub. Function is
  1096. * defined as follows: function(container)
  1097. * @param {Object} [params.Container.scope]
  1098. * Whenever one of the Container's callback functions is called, references
  1099. * to "this" in the callback will refer to the scope object. If no scope is
  1100. * provided, default is window.
  1101. * @param {Function} [params.Container.log]
  1102. * Optional logger function. Would be used to log to console.log or
  1103. * equivalent.
  1104. *
  1105. * @throws {OpenAjax.hub.Error.BadParameters} if required params are not
  1106. * present or null
  1107. * @throws {OpenAjax.hub.Error.Duplicate} if a Container with this clientID
  1108. * already exists in the given Managed Hub
  1109. * @throws {OpenAjax.hub.Error.Disconnected} if ManagedHub is not connected
  1110. */
  1111. //OpenAjax.hub.Container = function( hub, clientID, params ) {}
  1112. /**
  1113. * Send a message to the client inside this container. This function MUST only
  1114. * be called by ManagedHub.
  1115. *
  1116. * @param {String} topic
  1117. * The topic name for the published message
  1118. * @param {*} data
  1119. * The payload. Can be any JSON-serializable value.
  1120. * @param {String} containerSubscriptionId
  1121. * Container's ID for a subscription, from previous call to
  1122. * subscribeForClient()
  1123. */
  1124. //OpenAjax.hub.Container.prototype.sendToClient = function( topic, data, containerSubscriptionId ) {}
  1125. /**
  1126. * Shut down a container. remove does all of the following:
  1127. * - disconnects container from HubClient
  1128. * - unsubscribes from all of its existing subscriptions in the ManagedHub
  1129. *
  1130. * This function is only called by ManagedHub.removeContainer
  1131. * Calling this function does NOT cause the container's onDisconnect callback to
  1132. * be invoked.
  1133. */
  1134. //OpenAjax.hub.Container.prototype.remove = function() {}
  1135. /**
  1136. * Returns true if the given client is connected to the managed hub.
  1137. * Else returns false.
  1138. *
  1139. * @returns true if the client is connected to the managed hub
  1140. * @type boolean
  1141. */
  1142. //OpenAjax.hub.Container.prototype.isConnected = function() {}
  1143. /**
  1144. * Returns the clientID passed in when this Container was instantiated.
  1145. *
  1146. * @returns The clientID
  1147. * @type {String}
  1148. */
  1149. //OpenAjax.hub.Container.prototype.getClientID = function() {}
  1150. /**
  1151. * If DISCONNECTED:
  1152. * Returns null
  1153. * If CONNECTED:
  1154. * Returns the origin associated with the window containing the HubClient
  1155. * associated with this Container instance. The origin has the format
  1156. *
  1157. * [protocol]://[host]
  1158. *
  1159. * where:
  1160. *
  1161. * [protocol] is "http" or "https"
  1162. * [host] is the hostname of the partner page.
  1163. *
  1164. * @returns Partner's origin
  1165. * @type {String}
  1166. */
  1167. //OpenAjax.hub.Container.prototype.getPartnerOrigin = function() {}
  1168. /**
  1169. * Returns the params object associated with this Container instance.
  1170. *
  1171. * @returns params
  1172. * The params object associated with this Container instance
  1173. * @type {Object}
  1174. */
  1175. //OpenAjax.hub.Container.prototype.getParameters = function() {}
  1176. /**
  1177. * Returns the ManagedHub to which this Container belongs.
  1178. *
  1179. * @returns ManagedHub
  1180. * The ManagedHub object associated with this Container instance
  1181. * @type {OpenAjax.hub.ManagedHub}
  1182. */
  1183. //OpenAjax.hub.Container.prototype.getHub = function() {}
  1184. ////////////////////////////////////////////////////////////////////////////////
  1185. /*
  1186. * Unmanaged Hub
  1187. */
  1188. /**
  1189. * OpenAjax.hub._hub is the default ManagedHub instance that we use to
  1190. * provide OAH 1.0 behavior.
  1191. */
  1192. OpenAjax.hub._hub = new OpenAjax.hub.ManagedHub({
  1193. onSubscribe: function(topic, ctnr) { return true; },
  1194. onPublish: function(topic, data, pcont, scont) { return true; }
  1195. });
  1196. /**
  1197. * Subscribe to a topic.
  1198. *
  1199. * @param {String} topic
  1200. * A valid topic string. MAY include wildcards.
  1201. * @param {Function|String} onData
  1202. * Callback function that is invoked whenever an event is published on the
  1203. * topic. If 'onData' is a string, then it represents the name of a
  1204. * function on the 'scope' object.
  1205. * @param {Object} [scope]
  1206. * When onData callback is invoked,
  1207. * the JavaScript "this" keyword refers to this scope object.
  1208. * If no scope is provided, default is window.
  1209. * @param {*} [subscriberData]
  1210. * Client application provides this data, which is handed
  1211. * back to the client application in the subscriberData
  1212. * parameter of the onData callback function.
  1213. *
  1214. * @returns {String} Identifier representing the subscription.
  1215. *
  1216. * @throws {OpenAjax.hub.Error.BadParameters} if the topic is invalid
  1217. * (e.g.contains an empty token)
  1218. */
  1219. OpenAjax.hub.subscribe = function(topic, onData, scope, subscriberData)
  1220. {
  1221. // resolve the 'onData' function if it is a string
  1222. if ( typeof onData === "string" ) {
  1223. scope = scope || window;
  1224. onData = scope[ onData ] || null;
  1225. }
  1226. return OpenAjax.hub._hub.subscribe( topic, onData, scope, null, subscriberData );
  1227. };
  1228. /**
  1229. * Unsubscribe from a subscription.
  1230. *
  1231. * @param {String} subscriptionID
  1232. * Subscription identifier returned by subscribe()
  1233. *
  1234. * @throws {OpenAjax.hub.Error.NoSubscription} if no such subscription is found
  1235. */
  1236. OpenAjax.hub.unsubscribe = function(subscriptionID)
  1237. {
  1238. return OpenAjax.hub._hub.unsubscribe( subscriptionID );
  1239. };
  1240. /**
  1241. * Publish an event on a topic.
  1242. *
  1243. * @param {String} topic
  1244. * A valid topic string. MUST NOT include wildcards.
  1245. * @param {*} data
  1246. * Valid publishable data.
  1247. *
  1248. * @throws {OpenAjax.hub.Error.BadParameters} if the topic cannot be published
  1249. * (e.g. contains wildcards or empty tokens)
  1250. */
  1251. OpenAjax.hub.publish = function(topic, data)
  1252. {
  1253. OpenAjax.hub._hub.publish(topic, data);
  1254. };
  1255. ////////////////////////////////////////////////////////////////////////////////
  1256. // Register the OpenAjax Hub itself as a library.
  1257. OpenAjax.hub.registerLibrary("OpenAjax", "http://openajax.org/hub", "2.0", {});
  1258. } // !OpenAjax.hub
  1259. /*
  1260. Copyright 2006-2009 OpenAjax Alliance
  1261. Licensed under the Apache License, Version 2.0 (the "License");
  1262. you may not use this file except in compliance with the License.
  1263. You may obtain a copy of the License at
  1264. http://www.apache.org/licenses/LICENSE-2.0
  1265. Unless required by applicable law or agreed to in writing, software
  1266. distributed under the License is distributed on an "AS IS" BASIS,
  1267. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1268. See the License for the specific language governing permissions and
  1269. limitations under the License.
  1270. */
  1271. /**
  1272. * Create a new Inline Container.
  1273. * @constructor
  1274. * @extends OpenAjax.hub.Container
  1275. *
  1276. * InlineContainer implements the Container interface to provide a container
  1277. * that places components within the same browser frame as the main mashup
  1278. * application. As such, this container does not isolate client components into
  1279. * secure sandboxes.
  1280. *
  1281. * @param {OpenAjax.hub.ManagedHub} hub
  1282. * Managed Hub instance to which this Container belongs
  1283. * @param {String} clientID
  1284. * A string ID that identifies a particular client of a Managed Hub. Unique
  1285. * within the context of the ManagedHub.
  1286. * @param {Object} params
  1287. * Parameters used to instantiate the InlineContainer.
  1288. * Once the constructor is called, the params object belongs exclusively to
  1289. * the InlineContainer. The caller MUST not modify it.
  1290. * The following are the pre-defined properties on params:
  1291. * @param {Function} params.Container.onSecurityAlert
  1292. * Called when an attempted security breach is thwarted. Function is defined
  1293. * as follows: function(container, securityAlert)
  1294. * @param {Function} [params.Container.onConnect]
  1295. * Called when the client connects to the Managed Hub. Function is defined
  1296. * as follows: function(container)
  1297. * @param {Function} [params.Container.onDisconnect]
  1298. * Called when the client disconnects from the Managed Hub. Function is
  1299. * defined as follows: function(container)
  1300. * @param {Object} [params.Container.scope]
  1301. * Whenever one of the Container's callback functions is called, references
  1302. * to "this" in the callback will refer to the scope object. If no scope is
  1303. * provided, default is window.
  1304. * @param {Function} [params.Container.log]
  1305. * Optional logger function. Would be used to log to console.log or
  1306. * equivalent.
  1307. *
  1308. * @throws {OpenAjax.hub.Error.BadParameters} if required params are not
  1309. * present or null
  1310. * @throws {OpenAjax.hub.Error.Duplicate} if a Container with this clientID
  1311. * already exists in the given Managed Hub
  1312. * @throws {OpenAjax.hub.Error.Disconnected} if ManagedHub is not connected
  1313. */
  1314. OpenAjax.hub.InlineContainer = function( hub, clientID, params )
  1315. {
  1316. if ( ! hub || ! clientID || ! params ||
  1317. ! params.Container || ! params.Container.onSecurityAlert ) {
  1318. throw new Error(OpenAjax.hub.Error.BadParameters);
  1319. }
  1320. var cbScope = params.Container.scope || window;
  1321. var connected = false;
  1322. var subs = [];
  1323. var subIndex = 0;
  1324. var client = null;
  1325. if ( params.Container.log ) {
  1326. var log = function( msg ) {
  1327. try {
  1328. params.Container.log.call( cbScope, "InlineContainer::" + clientID + ": " + msg );
  1329. } catch( e ) {
  1330. OpenAjax.hub._debugger();
  1331. }
  1332. };
  1333. } else {
  1334. log = function() {};
  1335. }
  1336. this._init = function() {
  1337. hub.addContainer( this );
  1338. };
  1339. /*** OpenAjax.hub.Container interface implementation ***/
  1340. this.getHub = function() {
  1341. return hub;
  1342. };
  1343. this.sendToClient = function( topic, data, subscriptionID ) {
  1344. if ( connected ) {
  1345. var sub = subs[ subscriptionID ];
  1346. try {
  1347. sub.cb.call( sub.sc, topic, data, sub.d );
  1348. } catch( e ) {
  1349. OpenAjax.hub._debugger();
  1350. client._log( "caught error from onData callback to HubClient.subscribe(): " + e.message );
  1351. }
  1352. }
  1353. };
  1354. this.remove = function() {
  1355. if ( connected ) {
  1356. finishDisconnect();
  1357. }
  1358. };
  1359. this.isConnected = function() {
  1360. return connected;
  1361. };
  1362. this.getClientID = function() {
  1363. return clientID;
  1364. };
  1365. this.getPartnerOrigin = function() {
  1366. if ( connected ) {
  1367. return window.location.protocol + "//" + window.location.hostname;
  1368. }
  1369. return null;
  1370. };
  1371. this.getParameters = function() {
  1372. return params;
  1373. };
  1374. /*** OpenAjax.hub.HubClient interface implementation ***/
  1375. this.connect = function( hubClient, onComplete, scope ) {
  1376. if ( connected ) {
  1377. throw new Error( OpenAjax.hub.Error.Duplicate );
  1378. }
  1379. connected = true;
  1380. client = hubClient;
  1381. if ( params.Container.onConnect ) {
  1382. try {
  1383. params.Container.onConnect.call( cbScope, this );
  1384. } catch( e ) {
  1385. OpenAjax.hub._debugger();
  1386. log( "caught error from onConnect callback to constructor: " + e.message );
  1387. }
  1388. }
  1389. invokeOnComplete( onComplete, scope, hubClient, true );
  1390. };
  1391. this.disconnect = function( hubClient, onComplete, scope ) {
  1392. if ( ! connected ) {
  1393. throw new Error( OpenAjax.hub.Error.Disconnected );
  1394. }
  1395. finishDisconnect();
  1396. if ( params.Container.onDisconnect ) {
  1397. try {
  1398. params.Container.onDisconnect.call( cbScope, this );
  1399. } catch( e ) {
  1400. OpenAjax.hub._debugger();
  1401. log( "caught error from onDisconnect callback to constructor: " + e.message );
  1402. }
  1403. }
  1404. invokeOnComplete( onComplete, scope, hubClient, true );
  1405. };
  1406. /*** OpenAjax.hub.Hub interface implementation ***/
  1407. this.subscribe = function( topic, onData, scope, onComplete, subscriberData ) {
  1408. assertConn();
  1409. assertSubTopic( topic );
  1410. if ( ! onData ) {
  1411. throw new Error( OpenAjax.hub.Error.BadParameters );
  1412. }
  1413. var subID = "" + subIndex++;
  1414. var success = false;
  1415. var msg = null;
  1416. try {
  1417. var handle = hub.subscribeForClient( this, topic, subID );
  1418. success = true;
  1419. } catch( e ) {
  1420. // failure
  1421. subID = null;
  1422. msg = e.message;
  1423. }
  1424. scope = scope || window;
  1425. if ( success ) {
  1426. subs[ subID ] = { h: handle, cb: onData, sc: scope, d: subscriberData };
  1427. }
  1428. invokeOnComplete( onComplete, scope, subID, success, msg );
  1429. return subID;
  1430. };
  1431. this.publish = function( topic, data ) {
  1432. assertConn();
  1433. assertPubTopic( topic );
  1434. hub.publishForClient( this, topic, data );
  1435. };
  1436. this.unsubscribe = function( subscriptionID, onComplete, scope ) {
  1437. assertConn();
  1438. if ( typeof subscriptionID === "undefined" || subscriptionID === null ) {
  1439. throw new Error( OpenAjax.hub.Error.BadParameters );
  1440. }
  1441. var sub = subs[ subscriptionID ];
  1442. if ( ! sub ) {
  1443. throw new Error( OpenAjax.hub.Error.NoSubscription );
  1444. }
  1445. hub.unsubscribeForClient( this, sub.h );
  1446. delete subs[ subscriptionID ];
  1447. invokeOnComplete( onComplete, scope, subscriptionID, true );
  1448. };
  1449. this.getSubscriberData = function( subID ) {
  1450. assertConn();
  1451. return getSubscription( subID ).d;
  1452. };
  1453. this.getSubscriberScope = function( subID ) {
  1454. assertConn();
  1455. return getSubscription( subID ).sc;
  1456. };
  1457. /*** PRIVATE FUNCTIONS ***/
  1458. function invokeOnComplete( func, scope, item, success, errorCode ) {
  1459. if ( func ) { // onComplete is optional
  1460. try {
  1461. scope = scope || window;
  1462. func.call( scope, item, success, errorCode );
  1463. } catch( e ) {
  1464. OpenAjax.hub._debugger();
  1465. // invokeOnComplete is only called for client interfaces (Hub and HubClient)
  1466. client._log( "caught error from onComplete callback: " + e.message );
  1467. }
  1468. }
  1469. }
  1470. function finishDisconnect() {
  1471. for ( var subID in subs ) {
  1472. hub.unsubscribeForClient( this, subs[subID].h );
  1473. }
  1474. subs = [];
  1475. subIndex = 0;
  1476. connected = false;
  1477. }
  1478. function assertConn() {
  1479. if ( ! connected ) {
  1480. throw new Error( OpenAjax.hub.Error.Disconnected );
  1481. }
  1482. }
  1483. function assertPubTopic( topic ) {
  1484. if ((topic == null) || (topic === "") || (topic.indexOf("*") != -1) ||
  1485. (topic.indexOf("..") != -1) || (topic.charAt(0) == ".") ||
  1486. (topic.charAt(topic.length-1) == "."))
  1487. {
  1488. throw new Error(OpenAjax.hub.Error.BadParameters);
  1489. }
  1490. }
  1491. function assertSubTopic( topic ) {
  1492. if ( ! topic ) {
  1493. throw new Error(OpenAjax.hub.Error.BadParameters);
  1494. }
  1495. var path = topic.split(".");
  1496. var len = path.length;
  1497. for (var i = 0; i < len; i++) {
  1498. var p = path[i];
  1499. if ((p === "") ||
  1500. ((p.indexOf("*") != -1) && (p != "*") && (p != "**"))) {
  1501. throw new Error(OpenAjax.hub.Error.BadParameters);
  1502. }
  1503. if ((p == "**") && (i < len - 1)) {
  1504. throw new Error(OpenAjax.hub.Error.BadParameters);
  1505. }
  1506. }
  1507. }
  1508. function getSubscription( subID ) {
  1509. var sub = subs[ subID ];
  1510. if ( sub ) {
  1511. return sub;
  1512. }
  1513. throw new Error( OpenAjax.hub.Error.NoSubscription );
  1514. }
  1515. this._init();
  1516. };
  1517. ////////////////////////////////////////////////////////////////////////////////
  1518. /**
  1519. * Create a new InlineHubClient.
  1520. * @constructor
  1521. * @extends OpenAjax.hub.HubClient
  1522. *
  1523. * @param {Object} params
  1524. * Parameters used to instantiate the HubClient.
  1525. * Once the constructor is called, the params object belongs to the
  1526. * HubClient. The caller MUST not modify it.
  1527. * The following are the pre-defined properties on params:
  1528. * @param {Function} params.HubClient.onSecurityAlert
  1529. * Called when an attempted security breach is thwarted
  1530. * @param {Object} [params.HubClient.scope]
  1531. * Whenever one of the HubClient's callback functions is called,
  1532. * references to "this" in the callback will refer to the scope object.
  1533. * If not provided, the default is window.
  1534. * @param {Function} [params.HubClient.log]
  1535. * Optional logger function. Would be used to log to console.log or
  1536. * equivalent.
  1537. * @param {OpenAjax.hub.InlineContainer} params.InlineHubClient.container
  1538. * Specifies the InlineContainer to which this HubClient will connect
  1539. *
  1540. * @throws {OpenAjax.hub.Error.BadParameters} if any of the required
  1541. * parameters are missing
  1542. */
  1543. OpenAjax.hub.InlineHubClient = function( params )
  1544. {
  1545. if ( ! params || ! params.HubClient || ! params.HubClient.onSecurityAlert ||
  1546. ! params.InlineHubClient || ! params.InlineHubClient.container ) {
  1547. throw new Error(OpenAjax.hub.Error.BadParameters);
  1548. }
  1549. var container = params.InlineHubClient.container;
  1550. var scope = params.HubClient.scope || window;
  1551. if ( params.HubClient.log ) {
  1552. var log = function( msg ) {
  1553. try {
  1554. params.HubClient.log.call( scope, "InlineHubClient::" + container.getClientID() + ": " + msg );
  1555. } catch( e ) {
  1556. OpenAjax.hub._debugger();
  1557. }
  1558. };
  1559. } else {
  1560. log = function() {};
  1561. }
  1562. this._log = log;
  1563. /*** OpenAjax.hub.HubClient interface implementation ***/
  1564. /**
  1565. * Requests a connection to the ManagedHub, via the InlineContainer
  1566. * associated with this InlineHubClient.
  1567. *
  1568. * If the Container accepts the connection request, this HubClient's
  1569. * state is set to CONNECTED and the HubClient invokes the
  1570. * onComplete callback function.
  1571. *
  1572. * If the Container refuses the connection request, the HubClient
  1573. * invokes the onComplete callback function with an error code.
  1574. * The error code might, for example, indicate that the Container
  1575. * is being destroyed.
  1576. *
  1577. * If the HubClient is already connected, calling connect will cause
  1578. * the HubClient to immediately invoke the onComplete callback with
  1579. * the error code OpenAjax.hub.Error.Duplicate.
  1580. *
  1581. * @param {Function} [onComplete]
  1582. * Callback function to call when this operation completes.
  1583. * @param {Object} [scope]
  1584. * When the onComplete function is invoked, the JavaScript "this"
  1585. * keyword refers to this scope object.
  1586. * If no scope is provided, default is window.
  1587. *
  1588. * In this implementation of InlineHubClient, this function operates
  1589. * SYNCHRONOUSLY, so the onComplete callback function is invoked before
  1590. * this connect function returns. Developers are cautioned that in
  1591. * IframeHubClient implementations, this is not the case.
  1592. *
  1593. * A client application may call InlineHubClient.disconnect and then call
  1594. * InlineHubClient.connect to reconnect to the Managed Hub.
  1595. */
  1596. this.connect = function( onComplete, scope ) {
  1597. container.connect( this, onComplete, scope );
  1598. };
  1599. /**
  1600. * Disconnect from the ManagedHub
  1601. *
  1602. * Disconnect immediately:
  1603. *
  1604. * 1. Sets the HubClient's state to DISCONNECTED.
  1605. * 2. Causes the HubClient to send a Disconnect request to the
  1606. * associated Container.
  1607. * 3. Ensures that the client application will receive no more
  1608. * onData or onComplete callbacks associated with this
  1609. * connection, except for the disconnect function's own
  1610. * onComplete callback.
  1611. * 4. Automatically destroys all of the HubClient's subscriptions.
  1612. *
  1613. * @param {Function} [onComplete]
  1614. * Callback function to call when this operation completes.
  1615. * @param {Object} [scope]
  1616. * When the onComplete function is invoked, the JavaScript "this"
  1617. * keyword refers to the scope object.
  1618. * If no scope is provided, default is window.
  1619. *
  1620. * In this implementation of InlineHubClient, the disconnect function operates
  1621. * SYNCHRONOUSLY, so the onComplete callback function is invoked before
  1622. * this function returns. Developers are cautioned that in IframeHubClient
  1623. * implementations, this is not the case.
  1624. *
  1625. * A client application is allowed to call HubClient.disconnect and
  1626. * then call HubClient.connect in order to reconnect.
  1627. */
  1628. this.disconnect = function( onComplete, scope ) {
  1629. container.disconnect( this, onComplete, scope );
  1630. };
  1631. this.getPartnerOrigin = function() {
  1632. return container.getPartnerOrigin();
  1633. };
  1634. this.getClientID = function() {
  1635. return container.getClientID();
  1636. };
  1637. /*** OpenAjax.hub.Hub interface implementation ***/
  1638. /**
  1639. * Subscribe to a topic.
  1640. *
  1641. * @param {String} topic
  1642. * A valid topic string. MAY include wildcards.
  1643. * @param {Function} onData
  1644. * Callback function that is invoked whenever an event is
  1645. * published on the topic
  1646. * @param {Object} [scope]
  1647. * When onData callback or onComplete callback is invoked,
  1648. * the JavaScript "this" keyword refers to this scope object.
  1649. * If no scope is provided, default is window.
  1650. * @param {Function} [onComplete]
  1651. * Invoked to tell the client application whether the
  1652. * subscribe operation succeeded or failed.
  1653. * @param {*} [subscriberData]
  1654. * Client application provides this data, which is handed
  1655. * back to the client application in the subscriberData
  1656. * parameter of the onData and onComplete callback functions.
  1657. *
  1658. * @returns subscriptionID
  1659. * Identifier representing the subscription. This identifier is an
  1660. * arbitrary ID string that is unique within this Hub instance
  1661. * @type {String}
  1662. *
  1663. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  1664. * @throws {OpenAjax.hub.Error.BadParameters} if the topic is invalid (e.g. contains an empty token)
  1665. *
  1666. * In this implementation of InlineHubClient, the subscribe function operates
  1667. * Thus, onComplete is invoked before this function returns. Developers are
  1668. * cautioned that in most implementations of HubClient, onComplete is invoked
  1669. * after this function returns.
  1670. *
  1671. * If unsubscribe is called before subscribe completes, the subscription is
  1672. * immediately terminated, and onComplete is never invoked.
  1673. */
  1674. this.subscribe = function( topic, onData, scope, onComplete, subscriberData ) {
  1675. return container.subscribe( topic, onData, scope, onComplete, subscriberData );
  1676. };
  1677. /**
  1678. * Publish an event on 'topic' with the given data.
  1679. *
  1680. * @param {String} topic
  1681. * A valid topic string. MUST NOT include wildcards.
  1682. * @param {*} data
  1683. * Valid publishable data. To be portable across different
  1684. * Container implementations, this value SHOULD be serializable
  1685. * as JSON.
  1686. *
  1687. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance
  1688. * is not in CONNECTED state
  1689. *
  1690. * In this implementation, publish operates SYNCHRONOUSLY.
  1691. * Data will be delivered to subscribers after this function returns.
  1692. * In most implementations, publish operates synchronously,
  1693. * delivering its data to the clients before this function returns.
  1694. */
  1695. this.publish = function( topic, data ) {
  1696. container.publish( topic, data );
  1697. };
  1698. /**
  1699. * Unsubscribe from a subscription
  1700. *
  1701. * @param {String} subscriptionID
  1702. * A subscriptionID returned by InlineHubClient.prototype.subscribe()
  1703. * @param {Function} [onComplete]
  1704. * Callback function invoked when unsubscribe completes
  1705. * @param {Object} [scope]
  1706. * When onComplete callback function is invoked, the JavaScript "this"
  1707. * keyword refers to this scope object.
  1708. *
  1709. * @throws {OpenAjax.hub.Error.NoSubscription} if no such subscription is found
  1710. *
  1711. * To facilitate cleanup, it is possible to call unsubscribe even
  1712. * when the HubClient is in a DISCONNECTED state.
  1713. *
  1714. * In this implementation of HubClient, this function operates SYNCHRONOUSLY.
  1715. * Thus, onComplete is invoked before this function returns. Developers are
  1716. * cautioned that in most implementations of HubClient, onComplete is invoked
  1717. * after this function returns.
  1718. */
  1719. this.unsubscribe = function( subscriptionID, onComplete, scope ) {
  1720. container.unsubscribe( subscriptionID, onComplete, scope );
  1721. };
  1722. this.isConnected = function() {
  1723. return container.isConnected();
  1724. };
  1725. this.getScope = function() {
  1726. return scope;
  1727. };
  1728. this.getSubscriberData = function( subID ) {
  1729. return container.getSubscriberData( subID );
  1730. };
  1731. this.getSubscriberScope = function( subID ) {
  1732. return container.getSubscriberScope( subID );
  1733. };
  1734. /**
  1735. * Returns the params object associated with this Hub instance.
  1736. * Allows mix-in code to access parameters passed into constructor that created
  1737. * this Hub instance.
  1738. *
  1739. * @returns params the params object associated with this Hub instance
  1740. * @type {Object}
  1741. */
  1742. this.getParameters = function() {
  1743. return params;
  1744. };
  1745. };/*
  1746. Copyright 2006-2009 OpenAjax Alliance
  1747. Licensed under the Apache License, Version 2.0 (the "License");
  1748. you may not use this file except in compliance with the License.
  1749. You may obtain a copy of the License at
  1750. http://www.apache.org/licenses/LICENSE-2.0
  1751. Unless required by applicable law or agreed to in writing, software
  1752. distributed under the License is distributed on an "AS IS" BASIS,
  1753. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1754. See the License for the specific language governing permissions and
  1755. limitations under the License.
  1756. */
  1757. var OpenAjax = OpenAjax || {};
  1758. OpenAjax.hub = OpenAjax.hub || {};
  1759. OpenAjax.gadgets = typeof OpenAjax.gadgets === 'object' ? OpenAjax.gadgets :
  1760. typeof gadgets === 'object' ? gadgets :
  1761. {};
  1762. OpenAjax.gadgets.rpctx = OpenAjax.gadgets.rpctx || {};
  1763. (function() {
  1764. // For now, we only use "oaaConfig" for the global "gadgets" object. If the "gadgets" global
  1765. // already exists, then there is no reason to check for "oaaConfig". In the future, if we use
  1766. // "oaaConfig" for other purposes, we'll need to remove the check for "!window.gadgets".
  1767. if (typeof gadgets === 'undefined') {
  1768. // "oaaConfig" can be specified as a global object. If not found, then look for it as an
  1769. // attribute on the script line for the OpenAjax Hub JS file.
  1770. if (typeof oaaConfig === 'undefined') {
  1771. var scripts = document.getElementsByTagName("script");
  1772. // match "OpenAjax-mashup.js", "OpenAjaxManagedHub-all*.js", "OpenAjaxManagedHub-core*.js"
  1773. var reHub = /openajax(?:managedhub-(?:all|core).*|-mashup)\.js$/i;
  1774. for ( var i = scripts.length - 1; i >= 0; i-- ) {
  1775. var src = scripts[i].getAttribute( "src" );
  1776. if ( !src ) {
  1777. continue;
  1778. }
  1779. var m = src.match( reHub );
  1780. if ( m ) {
  1781. var config = scripts[i].getAttribute( "oaaConfig" );
  1782. if ( config ) {
  1783. try {
  1784. oaaConfig = eval( "({ " + config + " })" );
  1785. } catch (e) {}
  1786. }
  1787. break;
  1788. }
  1789. }
  1790. }
  1791. if (typeof oaaConfig !== 'undefined' && oaaConfig.gadgetsGlobal) {
  1792. gadgets = OpenAjax.gadgets;
  1793. }
  1794. }
  1795. })();
  1796. if (!OpenAjax.hub.IframeContainer) {
  1797. (function(){
  1798. /**
  1799. * Create a new Iframe Container.
  1800. * @constructor
  1801. * @extends OpenAjax.hub.Container
  1802. *
  1803. * IframeContainer implements the Container interface to provide a container
  1804. * that isolates client components into secure sandboxes by leveraging the
  1805. * isolation features provided by browser iframes.
  1806. *
  1807. * SECURITY
  1808. *
  1809. * In order for the connection between the IframeContainer and IframeHubClient
  1810. * to be fully secure, you must specify a valid 'tunnelURI'. Note that if you
  1811. * do specify a 'tunnelURI', then only the WPM and NIX transports are used,
  1812. * covering the following browsers:
  1813. * IE 6+, Firefox 3+, Safari 4+, Chrome 2+, Opera 9+.
  1814. *
  1815. * If no 'tunnelURI' is specified, then some security features are disabled:
  1816. * the IframeContainer will not report FramePhish errors, and on some browsers
  1817. * IframeContainer and IframeHubClient will not be able to validate the
  1818. * identity of their partner (i.e. getPartnerOrigin() will return 'null').
  1819. * However, not providing 'tunnelURI' allows the additional use of the RMR
  1820. * and FE transports -- in addition to the above browsers, the Hub code will
  1821. * also work on:
  1822. * Firefox 1 & 2, Safari 2 & 3, Chrome 1.
  1823. *
  1824. * @param {OpenAjax.hub.ManagedHub} hub
  1825. * Managed Hub instance to which this Container belongs
  1826. * @param {String} clientID
  1827. * A string ID that identifies a particular client of a Managed Hub. Unique
  1828. * within the context of the ManagedHub.
  1829. * @param {Object} params
  1830. * Parameters used to instantiate the IframeContainer.
  1831. * Once the constructor is called, the params object belongs exclusively to
  1832. * the IframeContainer. The caller MUST not modify it.
  1833. * The following are the pre-defined properties on params:
  1834. * @param {Function} params.Container.onSecurityAlert
  1835. * Called when an attempted security breach is thwarted. Function is defined
  1836. * as follows: function(container, securityAlert)
  1837. * @param {Function} [params.Container.onConnect]
  1838. * Called when the client connects to the Managed Hub. Function is defined
  1839. * as follows: function(container)
  1840. * @param {Function} [params.Container.onDisconnect]
  1841. * Called when the client disconnects from the Managed Hub. Function is
  1842. * defined as follows: function(container)
  1843. * @param {Object} [params.Container.scope]
  1844. * Whenever one of the Container's callback functions is called, references
  1845. * to "this" in the callback will refer to the scope object. If no scope is
  1846. * provided, default is window.
  1847. * @param {Function} [params.Container.log]
  1848. * Optional logger function. Would be used to log to console.log or
  1849. * equivalent.
  1850. * @param {Object} params.IframeContainer.parent
  1851. * DOM element that is to be parent of iframe
  1852. * @param {String} params.IframeContainer.uri
  1853. * Initial Iframe URI (Container will add parameters to this URI)
  1854. * @param {String} [params.IframeContainer.tunnelURI]
  1855. * URI of the tunnel iframe. Must be from the same origin as the page which
  1856. * instantiates the IframeContainer. If not specified, connection will not
  1857. * be fully secure (see SECURITY section).
  1858. * @param {Object} [params.IframeContainer.iframeAttrs]
  1859. * Attributes to add to IFRAME DOM entity. For example:
  1860. * { style: { width: "100%",
  1861. * height: "100%" },
  1862. * className: "some_class" }
  1863. * @param {Number} [params.IframeContainer.timeout]
  1864. * Load timeout in milliseconds. If not specified, defaults to 15000. If
  1865. * the client at params.IframeContainer.uri does not establish a connection
  1866. * with this container in the given time, the onSecurityAlert callback is
  1867. * called with a LoadTimeout error code.
  1868. * @param {Function} [params.IframeContainer.seed]
  1869. * A function that returns a string that will be used to seed the
  1870. * pseudo-random number generator, which is used to create the security
  1871. * tokens. An implementation of IframeContainer may choose to ignore this
  1872. * value.
  1873. * @param {Number} [params.IframeContainer.tokenLength]
  1874. * Length of the security tokens used when transmitting messages. If not
  1875. * specified, defaults to 6. An implementation of IframeContainer may choose
  1876. * to ignore this value.
  1877. *
  1878. * @throws {OpenAjax.hub.Error.BadParameters} if required params are not
  1879. * present or null
  1880. * @throws {OpenAjax.hub.Error.Duplicate} if a Container with this clientID
  1881. * already exists in the given Managed Hub
  1882. * @throws {OpenAjax.hub.Error.Disconnected} if hub is not connected
  1883. */
  1884. OpenAjax.hub.IframeContainer = function( hub, clientID, params )
  1885. {
  1886. assertValidParams( arguments );
  1887. var container = this;
  1888. var scope = params.Container.scope || window;
  1889. var connected = false;
  1890. var subs = {};
  1891. var securityToken;
  1892. var internalID;
  1893. var timeout = params.IframeContainer.timeout || 15000;
  1894. var loadTimer;
  1895. if ( params.Container.log ) {
  1896. var log = function( msg ) {
  1897. try {
  1898. params.Container.log.call( scope, "IframeContainer::" + clientID + ": " + msg );
  1899. } catch( e ) {
  1900. OpenAjax.hub._debugger();
  1901. }
  1902. };
  1903. } else {
  1904. log = function() {};
  1905. }
  1906. this._init = function() {
  1907. // add to ManagedHub first, to see if clientID is a duplicate
  1908. hub.addContainer( this );
  1909. // Create an "internal" ID, which is guaranteed to be unique within the
  1910. // window, not just within the hub.
  1911. internalID = OpenAjax.hub.IframeContainer._rpcRouter.add( clientID, this );
  1912. securityToken = generateSecurityToken( params, scope, log );
  1913. var relay = null;
  1914. var transportName = OpenAjax.gadgets.rpc.getRelayChannel();
  1915. if ( params.IframeContainer.tunnelURI ) {
  1916. if ( transportName !== "wpm" && transportName !== "nix" ) {
  1917. throw new Error( OpenAjax.hub.Error.IncompatBrowser );
  1918. }
  1919. } else {
  1920. log( "WARNING: Parameter 'IframeContaienr.tunnelURI' not specified. Connection will not be fully secure." );
  1921. if ( transportName === "rmr" ) {
  1922. relay = OpenAjax.gadgets.rpc.getOrigin( params.IframeContainer.uri ) + "/robots.txt";
  1923. }
  1924. }
  1925. // Create IFRAME to hold the client
  1926. createIframe();
  1927. OpenAjax.gadgets.rpc.setupReceiver( internalID, relay );
  1928. startLoadTimer();
  1929. };
  1930. /*** OpenAjax.hub.Container interface ***/
  1931. this.sendToClient = function( topic, data, subscriptionID ) {
  1932. OpenAjax.gadgets.rpc.call( internalID, "openajax.pubsub", null, "pub", topic, data,
  1933. subscriptionID );
  1934. };
  1935. this.remove = function() {
  1936. finishDisconnect();
  1937. clearTimeout( loadTimer );
  1938. OpenAjax.gadgets.rpc.removeReceiver( internalID );
  1939. var iframe = document.getElementById( internalID );
  1940. iframe.parentNode.removeChild( iframe );
  1941. OpenAjax.hub.IframeContainer._rpcRouter.remove( internalID );
  1942. };
  1943. this.isConnected = function() {
  1944. return connected;
  1945. };
  1946. this.getClientID = function() {
  1947. return clientID;
  1948. };
  1949. this.getPartnerOrigin = function() {
  1950. if ( connected ) {
  1951. var origin = OpenAjax.gadgets.rpc.getReceiverOrigin( internalID );
  1952. if ( origin ) {
  1953. // remove port if present
  1954. return ( /^([a-zA-Z]+:\/\/[^:]+).*/.exec( origin )[1] );
  1955. }
  1956. }
  1957. return null;
  1958. };
  1959. this.getParameters = function() {
  1960. return params;
  1961. };
  1962. this.getHub = function() {
  1963. return hub;
  1964. };
  1965. /*** OpenAjax.hub.IframeContainer interface ***/
  1966. /**
  1967. * Get the iframe associated with this iframe container
  1968. *
  1969. * This function returns the iframe associated with an IframeContainer,
  1970. * allowing the Manager Application to change its size, styles, scrollbars, etc.
  1971. *
  1972. * CAUTION: The iframe is owned exclusively by the IframeContainer. The Manager
  1973. * Application MUST NOT destroy the iframe directly. Also, if the iframe is
  1974. * hidden and disconnected, the Manager Application SHOULD NOT attempt to make
  1975. * it visible. The Container SHOULD automatically hide the iframe when it is
  1976. * disconnected; to make it visible would introduce security risks.
  1977. *
  1978. * @returns iframeElement
  1979. * @type {Object}
  1980. */
  1981. this.getIframe = function() {
  1982. return document.getElementById( internalID );
  1983. };
  1984. /*** private functions ***/
  1985. function assertValidParams( args ) {
  1986. var hub = args[0],
  1987. clientID = args[1],
  1988. params = args[2];
  1989. if ( ! hub || ! clientID || ! params || ! params.Container ||
  1990. ! params.Container.onSecurityAlert || ! params.IframeContainer ||
  1991. ! params.IframeContainer.parent || ! params.IframeContainer.uri ) {
  1992. throw new Error( OpenAjax.hub.Error.BadParameters );
  1993. }
  1994. }
  1995. this._handleIncomingRPC = function( command, topic, data ) {
  1996. switch ( command ) {
  1997. // publish
  1998. // 'data' is topic message
  1999. case "pub":
  2000. hub.publishForClient( container, topic, data );
  2001. break;
  2002. // subscribe
  2003. // 'data' is subscription ID
  2004. case "sub":
  2005. var errCode = ""; // empty string is success
  2006. try {
  2007. subs[ data ] = hub.subscribeForClient( container, topic, data );
  2008. } catch( e ) {
  2009. errCode = e.message;
  2010. }
  2011. return errCode;
  2012. // unsubscribe
  2013. // 'data' is subscription ID
  2014. case "uns":
  2015. var handle = subs[ data ];
  2016. hub.unsubscribeForClient( container, handle );
  2017. delete subs[ data ];
  2018. return data;
  2019. // connect
  2020. case "con":
  2021. finishConnect();
  2022. return true;
  2023. // disconnect
  2024. case "dis":
  2025. startLoadTimer();
  2026. finishDisconnect();
  2027. if ( params.Container.onDisconnect ) {
  2028. try {
  2029. params.Container.onDisconnect.call( scope, container );
  2030. } catch( e ) {
  2031. OpenAjax.hub._debugger();
  2032. log( "caught error from onDisconnect callback to constructor: " + e.message );
  2033. }
  2034. }
  2035. return true;
  2036. }
  2037. };
  2038. this._onSecurityAlert = function( error ) {
  2039. invokeSecurityAlert( rpcErrorsToOAA[ error ] );
  2040. };
  2041. // The RPC code requires that the 'name' attribute be properly set on the
  2042. // iframe. However, setting the 'name' property on the iframe object
  2043. // returned from 'createElement("iframe")' doesn't work on IE --
  2044. // 'window.name' returns null for the code within the iframe. The
  2045. // workaround is to set the 'innerHTML' of a span to the iframe's HTML code,
  2046. // with 'name' and other attributes properly set.
  2047. function createIframe() {
  2048. var span = document.createElement( "span" );
  2049. params.IframeContainer.parent.appendChild( span );
  2050. var iframeText = '<iframe id="' + internalID + '" name="' + internalID +
  2051. '" src="javascript:\'<html></html>\'"';
  2052. // Add iframe attributes
  2053. var styleText = '';
  2054. var attrs = params.IframeContainer.iframeAttrs;
  2055. if ( attrs ) {
  2056. for ( var attr in attrs ) {
  2057. switch ( attr ) {
  2058. case "style":
  2059. for ( var style in attrs.style ) {
  2060. styleText += style + ':' + attrs.style[ style ] + ';';
  2061. }
  2062. break;
  2063. case "className":
  2064. iframeText += ' class="' + attrs[ attr ] + '"';
  2065. break;
  2066. default:
  2067. iframeText += ' ' + attr + '="' + attrs[ attr ] + '"';
  2068. }
  2069. }
  2070. }
  2071. // initially hide IFRAME content, in order to lessen frame phishing impact
  2072. styleText += 'visibility:hidden;';
  2073. iframeText += ' style="' + styleText + '"></iframe>';
  2074. span.innerHTML = iframeText;
  2075. var tunnel = params.IframeContainer.tunnelURI;
  2076. document.getElementById( internalID ).src = params.IframeContainer.uri +
  2077. "#rpctoken=" + securityToken +
  2078. (tunnel ? "&parent=" + encodeURIComponent( tunnel ) + "&forcesecure=true" :
  2079. "&oaaParent=" + encodeURIComponent( OpenAjax.gadgets.rpc.getOrigin( window.location.href )));
  2080. }
  2081. // If the relay iframe used by RPC has not been loaded yet, then we won't have unload protection
  2082. // at this point. Since we can't detect when the relay iframe has loaded, we use a two stage
  2083. // connection process. First, the child sends a connection msg and the container sends an ack.
  2084. // Then the container sends a connection msg and the child replies with an ack. Since the
  2085. // container can only send a message if the relay iframe has loaded, then we know if we get an
  2086. // ack here that the relay iframe is ready. And we are fully connected.
  2087. function finishConnect() {
  2088. // connect acknowledgement
  2089. function callback( result ) {
  2090. if ( result ) {
  2091. connected = true;
  2092. clearTimeout( loadTimer );
  2093. document.getElementById( internalID ).style.visibility = "visible";
  2094. if ( params.Container.onConnect ) {
  2095. try {
  2096. params.Container.onConnect.call( scope, container );
  2097. } catch( e ) {
  2098. OpenAjax.hub._debugger();
  2099. log( "caught error from onConnect callback to constructor: " + e.message );
  2100. }
  2101. }
  2102. }
  2103. }
  2104. OpenAjax.gadgets.rpc.call( internalID, "openajax.pubsub", callback, "cmd", "con" );
  2105. }
  2106. function finishDisconnect() {
  2107. if ( connected ) {
  2108. connected = false;
  2109. document.getElementById( internalID ).style.visibility = "hidden";
  2110. // unsubscribe from all subs
  2111. for ( var s in subs ) {
  2112. hub.unsubscribeForClient( container, subs[s] );
  2113. }
  2114. subs = {};
  2115. }
  2116. }
  2117. function invokeSecurityAlert( errorMsg ) {
  2118. try {
  2119. params.Container.onSecurityAlert.call( scope, container, errorMsg );
  2120. } catch( e ) {
  2121. OpenAjax.hub._debugger();
  2122. log( "caught error from onSecurityAlert callback to constructor: " + e.message );
  2123. }
  2124. }
  2125. function startLoadTimer() {
  2126. loadTimer = setTimeout(
  2127. function() {
  2128. // alert the security alert callback
  2129. invokeSecurityAlert( OpenAjax.hub.SecurityAlert.LoadTimeout );
  2130. // don't receive any more messages from HubClient
  2131. container._handleIncomingRPC = function() {};
  2132. },
  2133. timeout
  2134. );
  2135. }
  2136. this._init();
  2137. };
  2138. ////////////////////////////////////////////////////////////////////////////////
  2139. /**
  2140. * Create a new IframeHubClient.
  2141. * @constructor
  2142. * @extends OpenAjax.hub.HubClient
  2143. *
  2144. * @param {Object} params
  2145. * Once the constructor is called, the params object belongs to the
  2146. * HubClient. The caller MUST not modify it.
  2147. * The following are the pre-defined properties on params:
  2148. * @param {Function} params.HubClient.onSecurityAlert
  2149. * Called when an attempted security breach is thwarted
  2150. * @param {Object} [params.HubClient.scope]
  2151. * Whenever one of the HubClient's callback functions is called,
  2152. * references to "this" in the callback will refer to the scope object.
  2153. * If not provided, the default is window.
  2154. * @param {Function} [params.HubClient.log]
  2155. * Optional logger function. Would be used to log to console.log or
  2156. * equivalent.
  2157. * @param {Boolean} [params.IframeHubClient.requireParentVerifiable]
  2158. * Set to true in order to require that this IframeHubClient use a
  2159. * transport that can verify the parent Container's identity.
  2160. * @param {Function} [params.IframeHubClient.seed]
  2161. * A function that returns a string that will be used to seed the
  2162. * pseudo-random number generator, which is used to create the security
  2163. * tokens. An implementation of IframeHubClient may choose to ignore
  2164. * this value.
  2165. * @param {Number} [params.IframeHubClient.tokenLength]
  2166. * Length of the security tokens used when transmitting messages. If
  2167. * not specified, defaults to 6. An implementation of IframeHubClient
  2168. * may choose to ignore this value.
  2169. *
  2170. * @throws {OpenAjax.hub.Error.BadParameters} if any of the required
  2171. * parameters is missing, or if a parameter value is invalid in
  2172. * some way.
  2173. */
  2174. OpenAjax.hub.IframeHubClient = function( params )
  2175. {
  2176. if ( ! params || ! params.HubClient || ! params.HubClient.onSecurityAlert ) {
  2177. throw new Error( OpenAjax.hub.Error.BadParameters );
  2178. }
  2179. var client = this;
  2180. var scope = params.HubClient.scope || window;
  2181. var connected = false;
  2182. var subs = {};
  2183. var subIndex = 0;
  2184. var clientID;
  2185. // var securityToken; // XXX still need "securityToken"?
  2186. if ( params.HubClient.log ) {
  2187. var log = function( msg ) {
  2188. try {
  2189. params.HubClient.log.call( scope, "IframeHubClient::" + clientID + ": " + msg );
  2190. } catch( e ) {
  2191. OpenAjax.hub._debugger();
  2192. }
  2193. };
  2194. } else {
  2195. log = function() {};
  2196. }
  2197. this._init = function() {
  2198. var urlParams = OpenAjax.gadgets.util.getUrlParameters();
  2199. if ( ! urlParams.parent ) {
  2200. // The RMR transport does not require a valid relay file, but does need a URL
  2201. // in the parent's domain. The URL does not need to point to valid file, so just
  2202. // point to 'robots.txt' file. See RMR transport code for more info.
  2203. var parent = urlParams.oaaParent + "/robots.txt";
  2204. OpenAjax.gadgets.rpc.setupReceiver( "..", parent );
  2205. }
  2206. if ( params.IframeHubClient && params.IframeHubClient.requireParentVerifiable &&
  2207. OpenAjax.gadgets.rpc.getReceiverOrigin( ".." ) === null ) {
  2208. // If user set 'requireParentVerifiable' to true but RPC transport does not
  2209. // support this, throw error.
  2210. OpenAjax.gadgets.rpc.removeReceiver( ".." );
  2211. throw new Error( OpenAjax.hub.Error.IncompatBrowser );
  2212. }
  2213. OpenAjax.hub.IframeContainer._rpcRouter.add( "..", this );
  2214. // XXX The RPC layer initializes immediately on load, in the child (IframeHubClient). So it is too
  2215. // late here to specify a security token for the RPC layer. At the moment, only the NIX
  2216. // transport requires a child token (IFPC [aka FIM] is not supported).
  2217. // securityToken = generateSecurityToken( params, scope, log );
  2218. var internalID = OpenAjax.gadgets.rpc.RPC_ID;
  2219. if ( ! internalID ) {
  2220. throw new Error( OpenAjax.hub.Error.WrongProtocol );
  2221. }
  2222. clientID = internalID.substr( internalID.indexOf("_") + 1 );
  2223. };
  2224. /*** HubClient interface ***/
  2225. this.connect = function( onComplete, scope ) {
  2226. if ( connected ) {
  2227. throw new Error( OpenAjax.hub.Error.Duplicate );
  2228. }
  2229. // connect acknowledgement
  2230. function callback( result ) {
  2231. if ( result ) {
  2232. connected = true;
  2233. if ( onComplete ) {
  2234. try {
  2235. onComplete.call( scope || window, client, true );
  2236. } catch( e ) {
  2237. OpenAjax.hub._debugger();
  2238. log( "caught error from onComplete callback to connect(): " + e.message );
  2239. }
  2240. }
  2241. }
  2242. }
  2243. OpenAjax.gadgets.rpc.call( "..", "openajax.pubsub", callback, "con" );
  2244. };
  2245. this.disconnect = function( onComplete, scope ) {
  2246. if ( !connected ) {
  2247. throw new Error( OpenAjax.hub.Error.Disconnected );
  2248. }
  2249. connected = false;
  2250. // disconnect acknowledgement
  2251. var callback = null;
  2252. if ( onComplete ) {
  2253. callback = function( result ) {
  2254. try {
  2255. onComplete.call( scope || window, client, true );
  2256. } catch( e ) {
  2257. OpenAjax.hub._debugger();
  2258. log( "caught error from onComplete callback to disconnect(): " + e.message );
  2259. }
  2260. };
  2261. }
  2262. OpenAjax.gadgets.rpc.call( "..", "openajax.pubsub", callback, "dis" );
  2263. };
  2264. this.getPartnerOrigin = function() {
  2265. if ( connected ) {
  2266. var origin = OpenAjax.gadgets.rpc.getReceiverOrigin( ".." );
  2267. if ( origin ) {
  2268. // remove port if present
  2269. return ( /^([a-zA-Z]+:\/\/[^:]+).*/.exec( origin )[1] );
  2270. }
  2271. }
  2272. return null;
  2273. };
  2274. this.getClientID = function() {
  2275. return clientID;
  2276. };
  2277. /*** Hub interface ***/
  2278. this.subscribe = function( topic, onData, scope, onComplete, subscriberData ) {
  2279. assertConn();
  2280. assertSubTopic( topic );
  2281. if ( ! onData ) {
  2282. throw new Error( OpenAjax.hub.Error.BadParameters );
  2283. }
  2284. scope = scope || window;
  2285. var subID = "" + subIndex++;
  2286. subs[ subID ] = { cb: onData, sc: scope, d: subscriberData };
  2287. // subscribe acknowledgement
  2288. function callback( result ) {
  2289. if ( result !== '' ) { // error
  2290. delete subs[ subID ];
  2291. }
  2292. if ( onComplete ) {
  2293. try {
  2294. onComplete.call( scope, subID, result === "", result );
  2295. } catch( e ) {
  2296. OpenAjax.hub._debugger();
  2297. log( "caught error from onComplete callback to subscribe(): " + e.message );
  2298. }
  2299. }
  2300. }
  2301. OpenAjax.gadgets.rpc.call( "..", "openajax.pubsub", callback, "sub", topic, subID );
  2302. return subID;
  2303. };
  2304. this.publish = function( topic, data ) {
  2305. assertConn();
  2306. assertPubTopic( topic );
  2307. OpenAjax.gadgets.rpc.call( "..", "openajax.pubsub", null, "pub", topic, data );
  2308. };
  2309. this.unsubscribe = function( subscriptionID, onComplete, scope ) {
  2310. assertConn();
  2311. if ( ! subscriptionID ) {
  2312. throw new Error( OpenAjax.hub.Error.BadParameters );
  2313. }
  2314. // if no such subscriptionID, or in process of unsubscribing given ID, throw error
  2315. if ( ! subs[ subscriptionID ] || subs[ subscriptionID ].uns ) {
  2316. throw new Error( OpenAjax.hub.Error.NoSubscription );
  2317. }
  2318. // unsubscribe in progress
  2319. subs[ subscriptionID ].uns = true;
  2320. // unsubscribe acknowledgement
  2321. function callback( result ) {
  2322. delete subs[ subscriptionID ];
  2323. if ( onComplete ) {
  2324. try {
  2325. onComplete.call( scope || window, subscriptionID, true );
  2326. } catch( e ) {
  2327. OpenAjax.hub._debugger();
  2328. log( "caught error from onComplete callback to unsubscribe(): " + e.message );
  2329. }
  2330. }
  2331. }
  2332. OpenAjax.gadgets.rpc.call( "..", "openajax.pubsub", callback, "uns", null, subscriptionID );
  2333. };
  2334. this.isConnected = function() {
  2335. return connected;
  2336. };
  2337. this.getScope = function() {
  2338. return scope;
  2339. };
  2340. this.getSubscriberData = function( subscriptionID ) {
  2341. assertConn();
  2342. if ( subs[ subscriptionID ] ) {
  2343. return subs[ subscriptionID ].d;
  2344. }
  2345. throw new Error( OpenAjax.hub.Error.NoSubscription );
  2346. };
  2347. this.getSubscriberScope = function( subscriptionID ) {
  2348. assertConn();
  2349. if ( subs[ subscriptionID ] ) {
  2350. return subs[ subscriptionID ].sc;
  2351. }
  2352. throw new Error( OpenAjax.hub.Error.NoSubscription );
  2353. };
  2354. this.getParameters = function() {
  2355. return params;
  2356. };
  2357. /*** private functions ***/
  2358. this._handleIncomingRPC = function( command, topic, data, subscriptionID ) {
  2359. if ( command === "pub" ) {
  2360. // if subscription exists and we are not in process of unsubscribing...
  2361. if ( subs[ subscriptionID ] && ! subs[ subscriptionID ].uns ) {
  2362. try {
  2363. subs[ subscriptionID ].cb.call( subs[ subscriptionID ].sc, topic,
  2364. data, subs[ subscriptionID ].d );
  2365. } catch( e ) {
  2366. OpenAjax.hub._debugger();
  2367. log( "caught error from onData callback to subscribe(): " + e.message );
  2368. }
  2369. }
  2370. }
  2371. // else if command === "cmd"...
  2372. // First time this function is called, topic should be "con". This is the 2nd stage of the
  2373. // connection process. Simply need to return "true" in order to send an acknowledgement
  2374. // back to container. See finishConnect() in the container object.
  2375. if ( topic === "con" ) {
  2376. return true;
  2377. }
  2378. return false;
  2379. };
  2380. function assertConn() {
  2381. if ( ! connected ) {
  2382. throw new Error( OpenAjax.hub.Error.Disconnected );
  2383. }
  2384. }
  2385. function assertSubTopic( topic )
  2386. {
  2387. if ( ! topic ) {
  2388. throw new Error( OpenAjax.hub.Error.BadParameters );
  2389. }
  2390. var path = topic.split(".");
  2391. var len = path.length;
  2392. for (var i = 0; i < len; i++) {
  2393. var p = path[i];
  2394. if ((p === "") ||
  2395. ((p.indexOf("*") != -1) && (p != "*") && (p != "**"))) {
  2396. throw new Error( OpenAjax.hub.Error.BadParameters );
  2397. }
  2398. if ((p == "**") && (i < len - 1)) {
  2399. throw new Error( OpenAjax.hub.Error.BadParameters );
  2400. }
  2401. }
  2402. }
  2403. function assertPubTopic( topic ) {
  2404. if ( !topic || topic === "" || (topic.indexOf("*") != -1) ||
  2405. (topic.indexOf("..") != -1) || (topic.charAt(0) == ".") ||
  2406. (topic.charAt(topic.length-1) == "."))
  2407. {
  2408. throw new Error( OpenAjax.hub.Error.BadParameters );
  2409. }
  2410. }
  2411. // function invokeSecurityAlert( errorMsg ) {
  2412. // try {
  2413. // params.HubClient.onSecurityAlert.call( scope, client, errorMsg );
  2414. // } catch( e ) {
  2415. // OpenAjax.hub._debugger();
  2416. // log( "caught error from onSecurityAlert callback to constructor: " + e.message );
  2417. // }
  2418. // }
  2419. this._init();
  2420. };
  2421. ////////////////////////////////////////////////////////////////////////////////
  2422. // RPC object contents:
  2423. // s: Service Name
  2424. // f: From
  2425. // c: The callback ID or 0 if none.
  2426. // a: The arguments for this RPC call.
  2427. // t: The authentication token.
  2428. OpenAjax.hub.IframeContainer._rpcRouter = function() {
  2429. var receivers = {};
  2430. function router() {
  2431. var r = receivers[ this.f ];
  2432. if ( r ) {
  2433. return r._handleIncomingRPC.apply( r, arguments );
  2434. }
  2435. }
  2436. function onSecurityAlert( receiverId, error ) {
  2437. var r = receivers[ receiverId ];
  2438. if ( r ) {
  2439. r._onSecurityAlert.call( r, error );
  2440. }
  2441. }
  2442. return {
  2443. add: function( id, receiver ) {
  2444. function _add( id, receiver ) {
  2445. if ( id === ".." ) {
  2446. if ( ! receivers[ ".." ] ) {
  2447. receivers[ ".." ] = receiver;
  2448. }
  2449. return;
  2450. }
  2451. do {
  2452. // a client with the specified ID already exists on this page;
  2453. // create a unique ID
  2454. newID = ((0x7fff * Math.random()) | 0).toString(16) + "_" + id;
  2455. } while ( receivers[ newID ] );
  2456. receivers[ newID ] = receiver;
  2457. return newID;
  2458. }
  2459. // when this function is first called, register the RPC service
  2460. OpenAjax.gadgets.rpc.register( "openajax.pubsub", router );
  2461. OpenAjax.gadgets.rpc.config({
  2462. securityCallback: onSecurityAlert
  2463. });
  2464. rpcErrorsToOAA[ OpenAjax.gadgets.rpc.SEC_ERROR_LOAD_TIMEOUT ] = OpenAjax.hub.SecurityAlert.LoadTimeout;
  2465. rpcErrorsToOAA[ OpenAjax.gadgets.rpc.SEC_ERROR_FRAME_PHISH ] = OpenAjax.hub.SecurityAlert.FramePhish;
  2466. rpcErrorsToOAA[ OpenAjax.gadgets.rpc.SEC_ERROR_FORGED_MSG ] = OpenAjax.hub.SecurityAlert.ForgedMsg;
  2467. this.add = _add;
  2468. return _add( id, receiver );
  2469. },
  2470. remove: function( id ) {
  2471. delete receivers[ id ];
  2472. }
  2473. };
  2474. }();
  2475. var rpcErrorsToOAA = {};
  2476. ////////////////////////////////////////////////////////////////////////////////
  2477. function generateSecurityToken( params, scope, log ) {
  2478. if ( ! OpenAjax.hub.IframeContainer._prng ) {
  2479. // create pseudo-random number generator with a default seed
  2480. var seed = new Date().getTime() + Math.random() + document.cookie;
  2481. OpenAjax.hub.IframeContainer._prng = OpenAjax._smash.crypto.newPRNG( seed );
  2482. }
  2483. var p = params.IframeContainer || params.IframeHubClient;
  2484. if ( p && p.seed ) {
  2485. try {
  2486. var extraSeed = p.seed.call( scope );
  2487. OpenAjax.hub.IframeContainer._prng.addSeed( extraSeed );
  2488. } catch( e ) {
  2489. OpenAjax.hub._debugger();
  2490. log( "caught error from 'seed' callback: " + e.message );
  2491. }
  2492. }
  2493. var tokenLength = (p && p.tokenLength) || 6;
  2494. return OpenAjax.hub.IframeContainer._prng.nextRandomB64Str( tokenLength );
  2495. }
  2496. })();
  2497. }/*
  2498. Copyright 2006-2009 OpenAjax Alliance
  2499. Licensed under the Apache License, Version 2.0 (the "License");
  2500. you may not use this file except in compliance with the License.
  2501. You may obtain a copy of the License at
  2502. http://www.apache.org/licenses/LICENSE-2.0
  2503. Unless required by applicable law or agreed to in writing, software
  2504. distributed under the License is distributed on an "AS IS" BASIS,
  2505. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2506. See the License for the specific language governing permissions and
  2507. limitations under the License.
  2508. */
  2509. // SMASH.CRYPTO
  2510. //
  2511. // Small library containing some minimal crypto functionality for a
  2512. // - a hash-function: SHA-1 (see FIPS PUB 180-2 for definition)
  2513. // BigEndianWord[5] <- smash.crypto.sha1( BigEndianWord[*] dataWA, int lenInBits)
  2514. //
  2515. // - a message authentication code (MAC): HMAC-SHA-1 (RFC2104/2202)
  2516. // BigEndianWord[5] <- smash.crypto.hmac_sha1(
  2517. // BigEndianWord[3-16] keyWA,
  2518. // Ascii or Unicode string dataS,
  2519. // int chrsz (8 for Asci/16 for Unicode)
  2520. //
  2521. // - pseudo-random number generator (PRNG): HMAC-SHA-1 in counter mode, following
  2522. // Barak & Halevi, An architecture for robust pseudo-random generation and applications to /dev/random, CCS 2005
  2523. // rngObj <- smash.crypto.newPRNG( String[>=12] seedS)
  2524. // where rngObj has methods
  2525. // addSeed(String seed)
  2526. // BigEndianWord[len] <- nextRandomOctets(int len)
  2527. // Base64-String[len] <- nextRandomB64Str(int len)
  2528. // Note: HMAC-SHA1 in counter-mode does not provide forward-security on corruption.
  2529. // However, the PRNG state is kept inside a closure. So if somebody can break the closure, he probably could
  2530. // break a whole lot more and forward-security of the prng is not the highest of concerns anymore :-)
  2531. if ( typeof OpenAjax._smash == 'undefined' ) { OpenAjax._smash = {}; }
  2532. OpenAjax._smash.crypto = {
  2533. // Some utilities
  2534. // convert a string to an array of big-endian words
  2535. 'strToWA': function (/* Ascii or Unicode string */ str, /* int 8 for Asci/16 for Unicode */ chrsz){
  2536. var bin = Array();
  2537. var mask = (1 << chrsz) - 1;
  2538. for(var i = 0; i < str.length * chrsz; i += chrsz)
  2539. bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);
  2540. return bin;
  2541. },
  2542. // MAC
  2543. 'hmac_sha1' : function(
  2544. /* BigEndianWord[3-16]*/ keyWA,
  2545. /* Ascii or Unicode string */ dataS,
  2546. /* int 8 for Asci/16 for Unicode */ chrsz)
  2547. {
  2548. // write our own hmac derived from paj's so we do not have to do constant key conversions and length checking ...
  2549. var ipad = Array(16), opad = Array(16);
  2550. for(var i = 0; i < 16; i++) {
  2551. ipad[i] = keyWA[i] ^ 0x36363636;
  2552. opad[i] = keyWA[i] ^ 0x5C5C5C5C;
  2553. }
  2554. var hash = this.sha1( ipad.concat(this.strToWA(dataS, chrsz)), 512 + dataS.length * chrsz);
  2555. return this.sha1( opad.concat(hash), 512 + 160);
  2556. },
  2557. // PRNG factory method
  2558. // see below 'addSeed', 'nextRandomOctets' & 'nextRandomB64Octets' for public methods of returnd prng object
  2559. 'newPRNG' : function (/* String[>=12] */ seedS) {
  2560. var that = this;
  2561. // parameter checking
  2562. // We cannot really verify entropy but obviously the string must have at least a minimal length to have enough entropy
  2563. // However, a 2^80 security seems ok, so we check only that at least 12 chars assuming somewhat random ASCII
  2564. if ( (typeof seedS != 'string') || (seedS.length < 12) ) {
  2565. alert("WARNING: Seed length too short ...");
  2566. }
  2567. // constants
  2568. var __refresh_keyWA = [ 0xA999, 0x3E36, 0x4706, 0x816A,
  2569. 0x2571, 0x7850, 0xC26C, 0x9CD0,
  2570. 0xBA3E, 0xD89D, 0x1233, 0x9525,
  2571. 0xff3C, 0x1A83, 0xD491, 0xFF15 ]; // some random key for refresh ...
  2572. // internal state
  2573. var _keyWA = []; // BigEndianWord[5]
  2574. var _cnt = 0; // int
  2575. function extract(seedS) {
  2576. return that.hmac_sha1(__refresh_keyWA, seedS, 8);
  2577. }
  2578. function refresh(seedS) {
  2579. // HMAC-SHA1 is not ideal, Rijndal 256bit block/key in CBC mode with fixed key might be better
  2580. // but to limit the primitives and given that we anyway have only limited entropy in practise
  2581. // this seems good enough
  2582. var uniformSeedWA = extract(seedS);
  2583. for(var i = 0; i < 5; i++) {
  2584. _keyWA[i] ^= uniformSeedWA[i];
  2585. }
  2586. }
  2587. // inital state seeding
  2588. refresh(seedS);
  2589. // public methods
  2590. return {
  2591. // Mix some additional seed into the PRNG state
  2592. 'addSeed' : function (/* String */ seed) {
  2593. // no parameter checking. Any added entropy should be fine ...
  2594. refresh(seed);
  2595. },
  2596. // Get an array of len random octets
  2597. 'nextRandomOctets' : /* BigEndianWord[len] <- */ function (/* int */ len) {
  2598. var randOctets = [];
  2599. while (len > 0) {
  2600. _cnt+=1;
  2601. var nextBlock = that.hmac_sha1(_keyWA, (_cnt).toString(16), 8);
  2602. for (var i=0; (i < 20) & (len > 0); i++, len--) {
  2603. randOctets.push( (nextBlock[i>>2] >> (i % 4) ) % 256);
  2604. }
  2605. // Note: if len was not a multiple 20, some random octets are ignored here but who cares ..
  2606. }
  2607. return randOctets;
  2608. },
  2609. // Get a random string of Base64-like (see below) chars of length len
  2610. // Note: there is a slightly non-standard Base64 with no padding and '-' and '_' for '+' and '/', respectively
  2611. 'nextRandomB64Str' : /* Base64-String <- */ function (/* int */ len) {
  2612. var b64StrMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
  2613. var randOctets = this.nextRandomOctets(len);
  2614. var randB64Str = '';
  2615. for (var i=0; i < len; i++) {
  2616. randB64Str += b64StrMap.charAt(randOctets[i] & 0x3F);
  2617. }
  2618. return randB64Str;
  2619. }
  2620. };
  2621. },
  2622. // Digest function:
  2623. // BigEndianWord[5] <- sha1( BigEndianWord[*] dataWA, int lenInBits)
  2624. 'sha1' : function(){
  2625. // Note: all Section references below refer to FIPS 180-2.
  2626. // private utility functions
  2627. // - 32bit addition with wrap-around
  2628. var add_wa = function (x, y){
  2629. var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  2630. var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  2631. return (msw << 16) | (lsw & 0xFFFF);
  2632. };
  2633. // - 32bit rotatate left
  2634. var rol = function(num, cnt) {
  2635. return (num << cnt) | (num >>> (32 - cnt));
  2636. };
  2637. // - round-dependent function f_t from Section 4.1.1
  2638. function sha1_ft(t, b, c, d) {
  2639. if(t < 20) return (b & c) | ((~b) & d);
  2640. if(t < 40) return b ^ c ^ d;
  2641. if(t < 60) return (b & c) | (b & d) | (c & d);
  2642. return b ^ c ^ d;
  2643. }
  2644. // - round-dependent SHA-1 constants from Section 4.2.1
  2645. function sha1_kt(t) {
  2646. return (t < 20) ? 1518500249 :
  2647. (t < 40) ? 1859775393 :
  2648. (t < 60) ? -1894007588 :
  2649. /* (t < 80) */ -899497514 ;
  2650. }
  2651. // main algorithm.
  2652. return function( /* BigEndianWord[*] */ dataWA, /* int */ lenInBits) {
  2653. // Section 6.1.1: Preprocessing
  2654. //-----------------------------
  2655. // 1. padding: (see also Section 5.1.1)
  2656. // - append one 1 followed by 0 bits filling up 448 bits of last (512bit) block
  2657. dataWA[lenInBits >> 5] |= 0x80 << (24 - lenInBits % 32);
  2658. // - encode length in bits in last 64 bits
  2659. // Note: we rely on javascript to zero file elements which are beyond last (partial) data-block
  2660. // but before this length encoding!
  2661. dataWA[((lenInBits + 64 >> 9) << 4) + 15] = lenInBits;
  2662. // 2. 512bit blocks (actual split done ondemand later)
  2663. var W = Array(80);
  2664. // 3. initial hash using SHA-1 constants on page 13
  2665. var H0 = 1732584193;
  2666. var H1 = -271733879;
  2667. var H2 = -1732584194;
  2668. var H3 = 271733878;
  2669. var H4 = -1009589776;
  2670. // 6.1.2 SHA-1 Hash Computation
  2671. for(var i = 0; i < dataWA.length; i += 16) {
  2672. // 1. Message schedule, done below
  2673. // 2. init working variables
  2674. var a = H0; var b = H1; var c = H2; var d = H3; var e = H4;
  2675. // 3. round-functions
  2676. for(var j = 0; j < 80; j++)
  2677. {
  2678. // postponed step 2
  2679. W[j] = ( (j < 16) ? dataWA[i+j] : rol(W[j-3] ^ W[j-8] ^ W[j-14] ^ W[j-16], 1));
  2680. var T = add_wa( add_wa( rol(a, 5), sha1_ft(j, b, c, d)),
  2681. add_wa( add_wa(e, W[j]), sha1_kt(j)) );
  2682. e = d;
  2683. d = c;
  2684. c = rol(b, 30);
  2685. b = a;
  2686. a = T;
  2687. }
  2688. // 4. intermediate hash
  2689. H0 = add_wa(a, H0);
  2690. H1 = add_wa(b, H1);
  2691. H2 = add_wa(c, H2);
  2692. H3 = add_wa(d, H3);
  2693. H4 = add_wa(e, H4);
  2694. }
  2695. return Array(H0, H1, H2, H3, H4);
  2696. };
  2697. }()
  2698. };
  2699. /*
  2700. http://www.JSON.org/json2.js
  2701. 2008-11-19
  2702. Public Domain.
  2703. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
  2704. See http://www.JSON.org/js.html
  2705. This file creates a global JSON object containing two methods: stringify
  2706. and parse.
  2707. JSON.stringify(value, replacer, space)
  2708. value any JavaScript value, usually an object or array.
  2709. replacer an optional parameter that determines how object
  2710. values are stringified for objects. It can be a
  2711. function or an array of strings.
  2712. space an optional parameter that specifies the indentation
  2713. of nested structures. If it is omitted, the text will
  2714. be packed without extra whitespace. If it is a number,
  2715. it will specify the number of spaces to indent at each
  2716. level. If it is a string (such as '\t' or '&nbsp;'),
  2717. it contains the characters used to indent at each level.
  2718. This method produces a JSON text from a JavaScript value.
  2719. When an object value is found, if the object contains a toJSON
  2720. method, its toJSON method will be called and the result will be
  2721. stringified. A toJSON method does not serialize: it returns the
  2722. value represented by the name/value pair that should be serialized,
  2723. or undefined if nothing should be serialized. The toJSON method
  2724. will be passed the key associated with the value, and this will be
  2725. bound to the object holding the key.
  2726. For example, this would serialize Dates as ISO strings.
  2727. Date.prototype.toJSON = function (key) {
  2728. function f(n) {
  2729. // Format integers to have at least two digits.
  2730. return n < 10 ? '0' + n : n;
  2731. }
  2732. return this.getUTCFullYear() + '-' +
  2733. f(this.getUTCMonth() + 1) + '-' +
  2734. f(this.getUTCDate()) + 'T' +
  2735. f(this.getUTCHours()) + ':' +
  2736. f(this.getUTCMinutes()) + ':' +
  2737. f(this.getUTCSeconds()) + 'Z';
  2738. };
  2739. You can provide an optional replacer method. It will be passed the
  2740. key and value of each member, with this bound to the containing
  2741. object. The value that is returned from your method will be
  2742. serialized. If your method returns undefined, then the member will
  2743. be excluded from the serialization.
  2744. If the replacer parameter is an array of strings, then it will be
  2745. used to select the members to be serialized. It filters the results
  2746. such that only members with keys listed in the replacer array are
  2747. stringified.
  2748. Values that do not have JSON representations, such as undefined or
  2749. functions, will not be serialized. Such values in objects will be
  2750. dropped; in arrays they will be replaced with null. You can use
  2751. a replacer function to replace those with JSON values.
  2752. JSON.stringify(undefined) returns undefined.
  2753. The optional space parameter produces a stringification of the
  2754. value that is filled with line breaks and indentation to make it
  2755. easier to read.
  2756. If the space parameter is a non-empty string, then that string will
  2757. be used for indentation. If the space parameter is a number, then
  2758. the indentation will be that many spaces.
  2759. Example:
  2760. text = JSON.stringify(['e', {pluribus: 'unum'}]);
  2761. // text is '["e",{"pluribus":"unum"}]'
  2762. text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
  2763. // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
  2764. text = JSON.stringify([new Date()], function (key, value) {
  2765. return this[key] instanceof Date ?
  2766. 'Date(' + this[key] + ')' : value;
  2767. });
  2768. // text is '["Date(---current time---)"]'
  2769. JSON.parse(text, reviver)
  2770. This method parses a JSON text to produce an object or array.
  2771. It can throw a SyntaxError exception.
  2772. The optional reviver parameter is a function that can filter and
  2773. transform the results. It receives each of the keys and values,
  2774. and its return value is used instead of the original value.
  2775. If it returns what it received, then the structure is not modified.
  2776. If it returns undefined then the member is deleted.
  2777. Example:
  2778. // Parse the text. Values that look like ISO date strings will
  2779. // be converted to Date objects.
  2780. myData = JSON.parse(text, function (key, value) {
  2781. var a;
  2782. if (typeof value === 'string') {
  2783. a =
  2784. /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
  2785. if (a) {
  2786. return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
  2787. +a[5], +a[6]));
  2788. }
  2789. }
  2790. return value;
  2791. });
  2792. myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
  2793. var d;
  2794. if (typeof value === 'string' &&
  2795. value.slice(0, 5) === 'Date(' &&
  2796. value.slice(-1) === ')') {
  2797. d = new Date(value.slice(5, -1));
  2798. if (d) {
  2799. return d;
  2800. }
  2801. }
  2802. return value;
  2803. });
  2804. This is a reference implementation. You are free to copy, modify, or
  2805. redistribute.
  2806. This code should be minified before deployment.
  2807. See http://javascript.crockford.com/jsmin.html
  2808. USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
  2809. NOT CONTROL.
  2810. */
  2811. /*jslint evil: true */
  2812. /*global JSON */
  2813. /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
  2814. call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
  2815. getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
  2816. lastIndex, length, parse, prototype, push, replace, slice, stringify,
  2817. test, toJSON, toString, valueOf
  2818. */
  2819. // Create a JSON object only if one does not already exist. We create the
  2820. // methods in a closure to avoid creating global variables.
  2821. if (!this.JSON) {
  2822. JSON = {};
  2823. }
  2824. (function () {
  2825. function f(n) {
  2826. // Format integers to have at least two digits.
  2827. return n < 10 ? '0' + n : n;
  2828. }
  2829. if (typeof Date.prototype.toJSON !== 'function') {
  2830. Date.prototype.toJSON = function (key) {
  2831. return this.getUTCFullYear() + '-' +
  2832. f(this.getUTCMonth() + 1) + '-' +
  2833. f(this.getUTCDate()) + 'T' +
  2834. f(this.getUTCHours()) + ':' +
  2835. f(this.getUTCMinutes()) + ':' +
  2836. f(this.getUTCSeconds()) + 'Z';
  2837. };
  2838. String.prototype.toJSON =
  2839. Number.prototype.toJSON =
  2840. Boolean.prototype.toJSON = function (key) {
  2841. return this.valueOf();
  2842. };
  2843. }
  2844. var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
  2845. escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
  2846. gap,
  2847. indent,
  2848. meta = { // table of character substitutions
  2849. '\b': '\\b',
  2850. '\t': '\\t',
  2851. '\n': '\\n',
  2852. '\f': '\\f',
  2853. '\r': '\\r',
  2854. '"' : '\\"',
  2855. '\\': '\\\\'
  2856. },
  2857. rep;
  2858. function quote(string) {
  2859. // If the string contains no control characters, no quote characters, and no
  2860. // backslash characters, then we can safely slap some quotes around it.
  2861. // Otherwise we must also replace the offending characters with safe escape
  2862. // sequences.
  2863. escapable.lastIndex = 0;
  2864. return escapable.test(string) ?
  2865. '"' + string.replace(escapable, function (a) {
  2866. var c = meta[a];
  2867. return typeof c === 'string' ? c :
  2868. '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
  2869. }) + '"' :
  2870. '"' + string + '"';
  2871. }
  2872. function str(key, holder) {
  2873. // Produce a string from holder[key].
  2874. var i, // The loop counter.
  2875. k, // The member key.
  2876. v, // The member value.
  2877. length,
  2878. mind = gap,
  2879. partial,
  2880. value = holder[key];
  2881. // If the value has a toJSON method, call it to obtain a replacement value.
  2882. if (value && typeof value === 'object' &&
  2883. typeof value.toJSON === 'function') {
  2884. value = value.toJSON(key);
  2885. }
  2886. // If we were called with a replacer function, then call the replacer to
  2887. // obtain a replacement value.
  2888. if (typeof rep === 'function') {
  2889. value = rep.call(holder, key, value);
  2890. }
  2891. // What happens next depends on the value's type.
  2892. switch (typeof value) {
  2893. case 'string':
  2894. return quote(value);
  2895. case 'number':
  2896. // JSON numbers must be finite. Encode non-finite numbers as null.
  2897. return isFinite(value) ? String(value) : 'null';
  2898. case 'boolean':
  2899. case 'null':
  2900. // If the value is a boolean or null, convert it to a string. Note:
  2901. // typeof null does not produce 'null'. The case is included here in
  2902. // the remote chance that this gets fixed someday.
  2903. return String(value);
  2904. // If the type is 'object', we might be dealing with an object or an array or
  2905. // null.
  2906. case 'object':
  2907. // Due to a specification blunder in ECMAScript, typeof null is 'object',
  2908. // so watch out for that case.
  2909. if (!value) {
  2910. return 'null';
  2911. }
  2912. // Make an array to hold the partial results of stringifying this object value.
  2913. gap += indent;
  2914. partial = [];
  2915. // Is the value an array?
  2916. if (Object.prototype.toString.apply(value) === '[object Array]') {
  2917. // The value is an array. Stringify every element. Use null as a placeholder
  2918. // for non-JSON values.
  2919. length = value.length;
  2920. for (i = 0; i < length; i += 1) {
  2921. partial[i] = str(i, value) || 'null';
  2922. }
  2923. // Join all of the elements together, separated with commas, and wrap them in
  2924. // brackets.
  2925. v = partial.length === 0 ? '[]' :
  2926. gap ? '[\n' + gap +
  2927. partial.join(',\n' + gap) + '\n' +
  2928. mind + ']' :
  2929. '[' + partial.join(',') + ']';
  2930. gap = mind;
  2931. return v;
  2932. }
  2933. // If the replacer is an array, use it to select the members to be stringified.
  2934. if (rep && typeof rep === 'object') {
  2935. length = rep.length;
  2936. for (i = 0; i < length; i += 1) {
  2937. k = rep[i];
  2938. if (typeof k === 'string') {
  2939. v = str(k, value);
  2940. if (v) {
  2941. partial.push(quote(k) + (gap ? ': ' : ':') + v);
  2942. }
  2943. }
  2944. }
  2945. } else {
  2946. // Otherwise, iterate through all of the keys in the object.
  2947. for (k in value) {
  2948. if (Object.hasOwnProperty.call(value, k)) {
  2949. v = str(k, value);
  2950. if (v) {
  2951. partial.push(quote(k) + (gap ? ': ' : ':') + v);
  2952. }
  2953. }
  2954. }
  2955. }
  2956. // Join all of the member texts together, separated with commas,
  2957. // and wrap them in braces.
  2958. v = partial.length === 0 ? '{}' :
  2959. gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
  2960. mind + '}' : '{' + partial.join(',') + '}';
  2961. gap = mind;
  2962. return v;
  2963. }
  2964. }
  2965. // If the JSON object does not yet have a stringify method, give it one.
  2966. if (typeof JSON.stringify !== 'function') {
  2967. JSON.stringify = function (value, replacer, space) {
  2968. // The stringify method takes a value and an optional replacer, and an optional
  2969. // space parameter, and returns a JSON text. The replacer can be a function
  2970. // that can replace values, or an array of strings that will select the keys.
  2971. // A default replacer method can be provided. Use of the space parameter can
  2972. // produce text that is more easily readable.
  2973. var i;
  2974. gap = '';
  2975. indent = '';
  2976. // If the space parameter is a number, make an indent string containing that
  2977. // many spaces.
  2978. if (typeof space === 'number') {
  2979. for (i = 0; i < space; i += 1) {
  2980. indent += ' ';
  2981. }
  2982. // If the space parameter is a string, it will be used as the indent string.
  2983. } else if (typeof space === 'string') {
  2984. indent = space;
  2985. }
  2986. // If there is a replacer, it must be a function or an array.
  2987. // Otherwise, throw an error.
  2988. rep = replacer;
  2989. if (replacer && typeof replacer !== 'function' &&
  2990. (typeof replacer !== 'object' ||
  2991. typeof replacer.length !== 'number')) {
  2992. throw new Error('JSON.stringify');
  2993. }
  2994. // Make a fake root object containing our value under the key of ''.
  2995. // Return the result of stringifying the value.
  2996. return str('', {'': value});
  2997. };
  2998. }
  2999. // If the JSON object does not yet have a parse method, give it one.
  3000. if (typeof JSON.parse !== 'function') {
  3001. JSON.parse = function (text, reviver) {
  3002. // The parse method takes a text and an optional reviver function, and returns
  3003. // a JavaScript value if the text is a valid JSON text.
  3004. var j;
  3005. function walk(holder, key) {
  3006. // The walk method is used to recursively walk the resulting structure so
  3007. // that modifications can be made.
  3008. var k, v, value = holder[key];
  3009. if (value && typeof value === 'object') {
  3010. for (k in value) {
  3011. if (Object.hasOwnProperty.call(value, k)) {
  3012. v = walk(value, k);
  3013. if (v !== undefined) {
  3014. value[k] = v;
  3015. } else {
  3016. delete value[k];
  3017. }
  3018. }
  3019. }
  3020. }
  3021. return reviver.call(holder, key, value);
  3022. }
  3023. // Parsing happens in four stages. In the first stage, we replace certain
  3024. // Unicode characters with escape sequences. JavaScript handles many characters
  3025. // incorrectly, either silently deleting them, or treating them as line endings.
  3026. cx.lastIndex = 0;
  3027. if (cx.test(text)) {
  3028. text = text.replace(cx, function (a) {
  3029. return '\\u' +
  3030. ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
  3031. });
  3032. }
  3033. // In the second stage, we run the text against regular expressions that look
  3034. // for non-JSON patterns. We are especially concerned with '()' and 'new'
  3035. // because they can cause invocation, and '=' because it can cause mutation.
  3036. // But just to be safe, we want to reject all unexpected forms.
  3037. // We split the second stage into 4 regexp operations in order to work around
  3038. // crippling inefficiencies in IE's and Safari's regexp engines. First we
  3039. // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
  3040. // replace all simple value tokens with ']' characters. Third, we delete all
  3041. // open brackets that follow a colon or comma or that begin the text. Finally,
  3042. // we look to see that the remaining characters are only whitespace or ']' or
  3043. // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
  3044. if (/^[\],:{}\s]*$/.
  3045. test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
  3046. replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
  3047. replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
  3048. // In the third stage we use the eval function to compile the text into a
  3049. // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
  3050. // in JavaScript: it can begin a block or an object literal. We wrap the text
  3051. // in parens to eliminate the ambiguity.
  3052. j = eval('(' + text + ')');
  3053. // In the optional fourth stage, we recursively walk the new structure, passing
  3054. // each name/value pair to a reviver function for possible transformation.
  3055. return typeof reviver === 'function' ?
  3056. walk({'': j}, '') : j;
  3057. }
  3058. // If the text is not JSON parseable, then a SyntaxError is thrown.
  3059. throw new SyntaxError('JSON.parse');
  3060. };
  3061. }
  3062. })();
  3063. /*
  3064. * Licensed to the Apache Software Foundation (ASF) under one
  3065. * or more contributor license agreements. See the NOTICE file
  3066. * distributed with this work for additional information
  3067. * regarding copyright ownership. The ASF licenses this file
  3068. * to you under the Apache License, Version 2.0 (the
  3069. * "License"); you may not use this file except in compliance
  3070. * with the License. You may obtain a copy of the License at
  3071. *
  3072. * http://www.apache.org/licenses/LICENSE-2.0
  3073. *
  3074. * Unless required by applicable law or agreed to in writing,
  3075. * software distributed under the License is distributed on an
  3076. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  3077. * KIND, either express or implied. See the License for the
  3078. * specific language governing permissions and limitations
  3079. * under the License.
  3080. */
  3081. /**
  3082. * @fileoverview External functions used by the OpenSocial RPC code. This file
  3083. * is for use by OpenAjax only.
  3084. */
  3085. //--- from core.util/util.js ---//
  3086. /**
  3087. * @static
  3088. * @class Provides general-purpose utility functions.
  3089. * @name gadgets.util
  3090. */
  3091. OpenAjax.gadgets.util = function() {
  3092. /**
  3093. * Parses URL parameters into an object.
  3094. * @param {string} url - the url parameters to parse
  3095. * @return {Array.<string>} The parameters as an array
  3096. */
  3097. function parseUrlParams(url) {
  3098. // Get settings from url, 'hash' takes precedence over 'search' component
  3099. // don't use document.location.hash due to browser differences.
  3100. var query;
  3101. var queryIdx = url.indexOf("?");
  3102. var hashIdx = url.indexOf("#");
  3103. if (hashIdx === -1) {
  3104. query = url.substr(queryIdx + 1);
  3105. } else {
  3106. // essentially replaces "#" with "&"
  3107. query = [url.substr(queryIdx + 1, hashIdx - queryIdx - 1), "&",
  3108. url.substr(hashIdx + 1)].join("");
  3109. }
  3110. return query.split("&");
  3111. }
  3112. var parameters = null;
  3113. var onLoadHandlers = [];
  3114. return /** @scope gadgets.util */ {
  3115. /**
  3116. * Gets the URL parameters.
  3117. *
  3118. * @param {string=} opt_url Optional URL whose parameters to parse.
  3119. * Defaults to window's current URL.
  3120. * @return {Object} Parameters passed into the query string
  3121. * @member gadgets.util
  3122. * @private Implementation detail.
  3123. */
  3124. getUrlParameters : function (opt_url) {
  3125. if (parameters !== null && typeof opt_url === "undefined") {
  3126. // "parameters" is a cache of current window params only.
  3127. return parameters;
  3128. }
  3129. var parsed = {};
  3130. var pairs = parseUrlParams(opt_url || document.location.href);
  3131. var unesc = window.decodeURIComponent ? decodeURIComponent : unescape;
  3132. for (var i = 0, j = pairs.length; i < j; ++i) {
  3133. var pos = pairs[i].indexOf('=');
  3134. if (pos === -1) {
  3135. continue;
  3136. }
  3137. var argName = pairs[i].substring(0, pos);
  3138. var value = pairs[i].substring(pos + 1);
  3139. // difference to IG_Prefs, is that args doesn't replace spaces in
  3140. // argname. Unclear on if it should do:
  3141. // argname = argname.replace(/\+/g, " ");
  3142. value = value.replace(/\+/g, " ");
  3143. parsed[argName] = unesc(value);
  3144. }
  3145. if (typeof opt_url === "undefined") {
  3146. // Cache current-window params in parameters var.
  3147. parameters = parsed;
  3148. }
  3149. return parsed;
  3150. },
  3151. /**
  3152. * Registers an onload handler.
  3153. * @param {function()} callback The handler to run
  3154. *
  3155. * @member gadgets.util
  3156. */
  3157. registerOnLoadHandler : function (callback) {
  3158. onLoadHandlers.push(callback);
  3159. },
  3160. /**
  3161. * Runs all functions registered via registerOnLoadHandler.
  3162. * @private Only to be used by the container, not gadgets.
  3163. */
  3164. runOnLoadHandlers : function () {
  3165. for (var i = 0, j = onLoadHandlers.length; i < j; ++i) {
  3166. onLoadHandlers[i]();
  3167. }
  3168. },
  3169. /**
  3170. * Attach an event listener to given DOM element
  3171. *
  3172. * @param {object} elem DOM element on which to attach event.
  3173. * @param {string} eventName Event type to listen for.
  3174. * @param {function} callback Invoked when specified event occurs.
  3175. * @param {boolean} useCapture If true, initiates capture.
  3176. */
  3177. 'attachBrowserEvent': function(elem, eventName, callback, useCapture) {
  3178. if (elem.addEventListener) {
  3179. elem.addEventListener(eventName, callback, useCapture);
  3180. } else if (elem.attachEvent) {
  3181. elem.attachEvent('on' + eventName, callback);
  3182. }
  3183. },
  3184. /**
  3185. * Remove event listener
  3186. *
  3187. * @param {object} elem DOM element from which to remove event.
  3188. * @param {string} eventName Event type to remove.
  3189. * @param {function} callback Listener to remove.
  3190. * @param {boolean} useCapture Specifies whether listener being removed was added with
  3191. * capture enabled.
  3192. */
  3193. 'removeBrowserEvent': function(elem, eventName, callback, useCapture) {
  3194. if (elem.removeEventListener) {
  3195. elem.removeEventListener(eventName, callback, useCapture);
  3196. } else if (elem.detachEvent){
  3197. elem.detachEvent('on' + eventName, callback);
  3198. }
  3199. }
  3200. };
  3201. }();
  3202. // Initialize url parameters so that hash data is pulled in before it can be
  3203. // altered by a click.
  3204. OpenAjax.gadgets.util.getUrlParameters();
  3205. //--- from core.json/json.js ---//
  3206. OpenAjax.gadgets.json = OpenAjax.gadgets.json || {};
  3207. if ( ! OpenAjax.gadgets.json.stringify ) {
  3208. OpenAjax.gadgets.json = {
  3209. parse: function(str) {
  3210. try {
  3211. if (str==="postmessage.test"){return false;}
  3212. return window.JSON.parse(str);
  3213. } catch (e) {
  3214. return false;
  3215. }
  3216. },
  3217. stringify: function(obj) {
  3218. try {
  3219. return window.JSON.stringify(obj);
  3220. } catch (e) {
  3221. return null;
  3222. }
  3223. }
  3224. };
  3225. }
  3226. //--- from core.log/log.js ---//
  3227. /**
  3228. * Log an informational message
  3229. */
  3230. OpenAjax.gadgets.log = function(message) {
  3231. OpenAjax.gadgets.log.logAtLevel(OpenAjax.gadgets.log.INFO, message);
  3232. };
  3233. /**
  3234. * Log a warning
  3235. */
  3236. OpenAjax.gadgets.warn = function(message) {
  3237. OpenAjax.gadgets.log.logAtLevel(OpenAjax.gadgets.log.WARNING, message);
  3238. };
  3239. /**
  3240. * Log an error
  3241. */
  3242. OpenAjax.gadgets.error = function(message) {
  3243. OpenAjax.gadgets.log.logAtLevel(OpenAjax.gadgets.log.ERROR, message);
  3244. };
  3245. /**
  3246. * Sets the log level threshold.
  3247. * @param {Number} logLevel - New log level threshold.
  3248. * @static
  3249. */
  3250. OpenAjax.gadgets.setLogLevel = function(logLevel) {
  3251. OpenAjax.gadgets.log.logLevelThreshold_ = logLevel;
  3252. };
  3253. /**
  3254. * Logs a log message if output console is available, and log threshold is met.
  3255. * @param {Number} level - the level to log with. Optional, defaults to
  3256. * @param {Object} message - The message to log
  3257. * gadgets.log.INFO.
  3258. * @static
  3259. */
  3260. OpenAjax.gadgets.log.logAtLevel = function(level, message) {
  3261. if (level < OpenAjax.gadgets.log.logLevelThreshold_ || !OpenAjax.gadgets.log._console) {
  3262. return;
  3263. }
  3264. var gadgetconsole = OpenAjax.gadgets.log._console;
  3265. if (level == OpenAjax.gadgets.log.WARNING && gadgetconsole.warn) {
  3266. gadgetconsole.warn(message);
  3267. } else if (level == OpenAjax.gadgets.log.ERROR && gadgetconsole.error) {
  3268. gadgetconsole.error(message);
  3269. } else if (gadgetconsole.log) {
  3270. gadgetconsole.log(message);
  3271. }
  3272. };
  3273. /**
  3274. * Log level for informational logging.
  3275. * @static
  3276. */
  3277. OpenAjax.gadgets.log.INFO = 1;
  3278. /**
  3279. * Log level for warning logging.
  3280. * @static
  3281. */
  3282. OpenAjax.gadgets.log.WARNING = 2;
  3283. /**
  3284. * Log level for error logging.
  3285. * @static
  3286. */
  3287. OpenAjax.gadgets.log.ERROR = 3;
  3288. /**
  3289. * Log level for no logging
  3290. * @static
  3291. */
  3292. OpenAjax.gadgets.log.NONE = 4;
  3293. /**
  3294. * Current log level threshold.
  3295. * @type Number
  3296. * @private
  3297. * @static
  3298. */
  3299. OpenAjax.gadgets.log.logLevelThreshold_ = OpenAjax.gadgets.log.INFO;
  3300. /**
  3301. * Console to log to
  3302. * @private
  3303. * @static
  3304. */
  3305. OpenAjax.gadgets.log._console = window.console ? window.console :
  3306. window.opera ? window.opera.postError : undefined;
  3307. ////////////////////////////////////////////////////////////////////////////////////////////////////
  3308. // onload handler compatibility code
  3309. ////////////////////////////////////////////////////////////////////////////////////////////////////
  3310. (function() {
  3311. // XXX What if this script file (iframe.js) is dynamically loaded after the page has loaded.
  3312. if ( ! window.__isgadget ) {
  3313. var loaded = false;
  3314. function onload() {
  3315. if ( ! loaded ) {
  3316. loaded = true;
  3317. // This is necessary for the RMR and FE transports.
  3318. OpenAjax.gadgets.util.runOnLoadHandlers();
  3319. // Since the page has now loaded, change registerOnLoadHandler() to immediately fire
  3320. // callback.
  3321. OpenAjax.gadgets.util.registerOnLoadHandler = function( callback ) {
  3322. setTimeout( callback, 0 );
  3323. };
  3324. // prevent IE memory leak
  3325. if ( window.detachEvent ) {
  3326. window.detachEvent( "onload", onload );
  3327. }
  3328. }
  3329. }
  3330. if ( window.addEventListener ) {
  3331. document.addEventListener( "DOMContentLoaded", onload, false );
  3332. window.addEventListener( "load", onload, false );
  3333. } else if ( window.attachEvent ) {
  3334. // XXX use doScroll trick?
  3335. window.attachEvent( "onload", onload );
  3336. }
  3337. }
  3338. })();
  3339. /*
  3340. * Licensed to the Apache Software Foundation (ASF) under one
  3341. * or more contributor license agreements. See the NOTICE file
  3342. * distributed with this work for additional information
  3343. * regarding copyright ownership. The ASF licenses this file
  3344. * to you under the Apache License, Version 2.0 (the
  3345. * "License"); you may not use this file except in compliance
  3346. * with the License. You may obtain a copy of the License at
  3347. *
  3348. * http://www.apache.org/licenses/LICENSE-2.0
  3349. *
  3350. * Unless required by applicable law or agreed to in writing,
  3351. * software distributed under the License is distributed on an
  3352. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  3353. * KIND, either express or implied. See the License for the
  3354. * specific language governing permissions and limitations under the License.
  3355. */
  3356. OpenAjax.gadgets.rpctx = OpenAjax.gadgets.rpctx || {};
  3357. /*
  3358. * For Gecko-based browsers, the security model allows a child to call a
  3359. * function on the frameElement of the iframe, even if the child is in
  3360. * a different domain. This method is dubbed "frameElement" (fe).
  3361. *
  3362. * The ability to add and call such functions on the frameElement allows
  3363. * a bidirectional channel to be setup via the adding of simple function
  3364. * references on the frameElement object itself. In this implementation,
  3365. * when the container sets up the authentication information for that gadget
  3366. * (by calling setAuth(...)) it as well adds a special function on the
  3367. * gadget's iframe. This function can then be used by the gadget to send
  3368. * messages to the container. In turn, when the gadget tries to send a
  3369. * message, it checks to see if this function has its own function stored
  3370. * that can be used by the container to call the gadget. If not, the
  3371. * function is created and subsequently used by the container.
  3372. * Note that as a result, FE can only be used by a container to call a
  3373. * particular gadget *after* that gadget has called the container at
  3374. * least once via FE.
  3375. *
  3376. * fe: Gecko-specific frameElement trick.
  3377. * - Firefox 1+
  3378. */
  3379. if (!OpenAjax.gadgets.rpctx.frameElement) { // make lib resilient to double-inclusion
  3380. OpenAjax.gadgets.rpctx.frameElement = function() {
  3381. // Consts for FrameElement.
  3382. var FE_G2C_CHANNEL = '__g2c_rpc';
  3383. var FE_C2G_CHANNEL = '__c2g_rpc';
  3384. var process;
  3385. var ready;
  3386. function callFrameElement(targetId, from, rpc) {
  3387. try {
  3388. if (from !== '..') {
  3389. // Call from gadget to the container.
  3390. var fe = window.frameElement;
  3391. if (typeof fe[FE_G2C_CHANNEL] === 'function') {
  3392. // Complete the setup of the FE channel if need be.
  3393. if (typeof fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] !== 'function') {
  3394. fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] = function(args) {
  3395. process(OpenAjax.gadgets.json.parse(args));
  3396. };
  3397. }
  3398. // Conduct the RPC call.
  3399. fe[FE_G2C_CHANNEL](OpenAjax.gadgets.json.stringify(rpc));
  3400. return;
  3401. }
  3402. } else {
  3403. // Call from container to gadget[targetId].
  3404. var frame = document.getElementById(targetId);
  3405. if (typeof frame[FE_G2C_CHANNEL] === 'function' &&
  3406. typeof frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL] === 'function') {
  3407. // Conduct the RPC call.
  3408. frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL](OpenAjax.gadgets.json.stringify(rpc));
  3409. return;
  3410. }
  3411. }
  3412. } catch (e) {
  3413. }
  3414. return true;
  3415. }
  3416. return {
  3417. getCode: function() {
  3418. return 'fe';
  3419. },
  3420. isParentVerifiable: function() {
  3421. return false;
  3422. },
  3423. init: function(processFn, readyFn) {
  3424. // No global setup.
  3425. process = processFn;
  3426. ready = readyFn;
  3427. return true;
  3428. },
  3429. setup: function(receiverId, token) {
  3430. // Indicate OK to call to container. This will be true
  3431. // by the end of this method.
  3432. if (receiverId !== '..') {
  3433. try {
  3434. var frame = document.getElementById(receiverId);
  3435. frame[FE_G2C_CHANNEL] = function(args) {
  3436. process(OpenAjax.gadgets.json.parse(args));
  3437. };
  3438. } catch (e) {
  3439. return false;
  3440. }
  3441. }
  3442. if (receiverId === '..') {
  3443. ready('..', true);
  3444. var ackFn = function() {
  3445. window.setTimeout(function() {
  3446. OpenAjax.gadgets.rpc.call(receiverId, OpenAjax.gadgets.rpc.ACK);
  3447. }, 500);
  3448. };
  3449. // Setup to container always happens before onload.
  3450. // If it didn't, the correct fix would be in gadgets.util.
  3451. OpenAjax.gadgets.util.registerOnLoadHandler(ackFn);
  3452. }
  3453. return true;
  3454. },
  3455. call: function(targetId, from, rpc) {
  3456. callFrameElement(targetId, from, rpc);
  3457. }
  3458. };
  3459. }();
  3460. } // !end of double-inclusion guard
  3461. /*
  3462. * Licensed to the Apache Software Foundation (ASF) under one
  3463. * or more contributor license agreements. See the NOTICE file
  3464. * distributed with this work for additional information
  3465. * regarding copyright ownership. The ASF licenses this file
  3466. * to you under the Apache License, Version 2.0 (the
  3467. * "License"); you may not use this file except in compliance
  3468. * with the License. You may obtain a copy of the License at
  3469. *
  3470. * http://www.apache.org/licenses/LICENSE-2.0
  3471. *
  3472. * Unless required by applicable law or agreed to in writing,
  3473. * software distributed under the License is distributed on an
  3474. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  3475. * KIND, either express or implied. See the License for the
  3476. * specific language governing permissions and limitations under the License.
  3477. */
  3478. OpenAjax.gadgets.rpctx = OpenAjax.gadgets.rpctx || {};
  3479. /*
  3480. * For all others, we have a fallback mechanism known as "ifpc". IFPC
  3481. * exploits the fact that while same-origin policy prohibits a frame from
  3482. * accessing members on a window not in the same domain, that frame can,
  3483. * however, navigate the window heirarchy (via parent). This is exploited by
  3484. * having a page on domain A that wants to talk to domain B create an iframe
  3485. * on domain B pointing to a special relay file and with a message encoded
  3486. * after the hash (#). This relay, in turn, finds the page on domain B, and
  3487. * can call a receipt function with the message given to it. The relay URL
  3488. * used by each caller is set via the gadgets.rpc.setRelayUrl(..) and
  3489. * *must* be called before the call method is used.
  3490. *
  3491. * ifpc: Iframe-based method, utilizing a relay page, to send a message.
  3492. * - No known major browsers still use this method, but it remains
  3493. * useful as a catch-all fallback for the time being.
  3494. */
  3495. if (!OpenAjax.gadgets.rpctx.ifpc) { // make lib resilient to double-inclusion
  3496. OpenAjax.gadgets.rpctx.ifpc = function() {
  3497. var iframePool = [];
  3498. var callId = 0;
  3499. var ready;
  3500. /**
  3501. * Encodes arguments for the legacy IFPC wire format.
  3502. *
  3503. * @param {Object} args
  3504. * @return {string} the encoded args
  3505. */
  3506. function encodeLegacyData(args) {
  3507. var argsEscaped = [];
  3508. for(var i = 0, j = args.length; i < j; ++i) {
  3509. argsEscaped.push(encodeURIComponent(OpenAjax.gadgets.json.stringify(args[i])));
  3510. }
  3511. return argsEscaped.join('&');
  3512. }
  3513. /**
  3514. * Helper function to emit an invisible IFrame.
  3515. * @param {string} src SRC attribute of the IFrame to emit.
  3516. * @private
  3517. */
  3518. function emitInvisibleIframe(src) {
  3519. var iframe;
  3520. // Recycle IFrames
  3521. for (var i = iframePool.length - 1; i >=0; --i) {
  3522. var ifr = iframePool[i];
  3523. try {
  3524. if (ifr && (ifr.recyclable || ifr.readyState === 'complete')) {
  3525. ifr.parentNode.removeChild(ifr);
  3526. if (window.ActiveXObject) {
  3527. // For MSIE, delete any iframes that are no longer being used. MSIE
  3528. // cannot reuse the IFRAME because a navigational click sound will
  3529. // be triggered when we set the SRC attribute.
  3530. // Other browsers scan the pool for a free iframe to reuse.
  3531. iframePool[i] = ifr = null;
  3532. iframePool.splice(i, 1);
  3533. } else {
  3534. ifr.recyclable = false;
  3535. iframe = ifr;
  3536. break;
  3537. }
  3538. }
  3539. } catch (e) {
  3540. // Ignore; IE7 throws an exception when trying to read readyState and
  3541. // readyState isn't set.
  3542. }
  3543. }
  3544. // Create IFrame if necessary
  3545. if (!iframe) {
  3546. iframe = document.createElement('iframe');
  3547. iframe.style.border = iframe.style.width = iframe.style.height = '0px';
  3548. iframe.style.visibility = 'hidden';
  3549. iframe.style.position = 'absolute';
  3550. iframe.onload = function() { this.recyclable = true; };
  3551. iframePool.push(iframe);
  3552. }
  3553. iframe.src = src;
  3554. window.setTimeout(function() { document.body.appendChild(iframe); }, 0);
  3555. }
  3556. return {
  3557. getCode: function() {
  3558. return 'ifpc';
  3559. },
  3560. isParentVerifiable: function() {
  3561. return true;
  3562. },
  3563. init: function(processFn, readyFn) {
  3564. // No global setup.
  3565. ready = readyFn;
  3566. ready('..', true); // Ready immediately.
  3567. return true;
  3568. },
  3569. setup: function(receiverId, token) {
  3570. // Indicate readiness to send to receiver.
  3571. ready(receiverId, true);
  3572. return true;
  3573. },
  3574. call: function(targetId, from, rpc) {
  3575. // Retrieve the relay file used by IFPC. Note that
  3576. // this must be set before the call, and so we conduct
  3577. // an extra check to ensure it is not blank.
  3578. var relay = OpenAjax.gadgets.rpc.getRelayUrl(targetId);
  3579. ++callId;
  3580. if (!relay) {
  3581. OpenAjax.gadgets.warn('No relay file assigned for IFPC');
  3582. return;
  3583. }
  3584. // The RPC mechanism supports two formats for IFPC (legacy and current).
  3585. var src = null;
  3586. if (rpc.l) {
  3587. // Use legacy protocol.
  3588. // Format: #iframe_id&callId&num_packets&packet_num&block_of_data
  3589. var callArgs = rpc.a;
  3590. src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
  3591. encodeLegacyData([from, rpc.s, '', '', from].concat(
  3592. callArgs))])].join('');
  3593. } else {
  3594. // Format: #targetId & sourceId@callId & packetNum & packetId & packetData
  3595. src = [relay, '#', targetId, '&', from, '@', callId,
  3596. '&1&0&', encodeURIComponent(OpenAjax.gadgets.json.stringify(rpc))].join('');
  3597. }
  3598. // Conduct the IFPC call by creating the Iframe with
  3599. // the relay URL and appended message.
  3600. emitInvisibleIframe(src);
  3601. return true;
  3602. }
  3603. };
  3604. }();
  3605. } // !end of double inclusion guard
  3606. /*
  3607. * Licensed to the Apache Software Foundation (ASF) under one
  3608. * or more contributor license agreements. See the NOTICE file
  3609. * distributed with this work for additional information
  3610. * regarding copyright ownership. The ASF licenses this file
  3611. * to you under the Apache License, Version 2.0 (the
  3612. * "License"); you may not use this file except in compliance
  3613. * with the License. You may obtain a copy of the License at
  3614. *
  3615. * http://www.apache.org/licenses/LICENSE-2.0
  3616. *
  3617. * Unless required by applicable law or agreed to in writing,
  3618. * software distributed under the License is distributed on an
  3619. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  3620. * KIND, either express or implied. See the License for the
  3621. * specific language governing permissions and limitations under the License.
  3622. */
  3623. OpenAjax.gadgets.rpctx = OpenAjax.gadgets.rpctx || {};
  3624. /**
  3625. * For Internet Explorer before version 8, the security model allows anyone
  3626. * parent to set the value of the "opener" property on another window,
  3627. * with only the receiving window able to read it.
  3628. * This method is dubbed "Native IE XDC" (NIX).
  3629. *
  3630. * This method works by placing a handler object in the "opener" property
  3631. * of a gadget when the container sets up the authentication information
  3632. * for that gadget (by calling setAuthToken(...)). At that point, a NIX
  3633. * wrapper is created and placed into the gadget by calling
  3634. * theframe.contentWindow.opener = wrapper. Note that as a result, NIX can
  3635. * only be used by a container to call a particular gadget *after* that
  3636. * gadget has called the container at least once via NIX.
  3637. *
  3638. * The NIX wrappers in this RPC implementation are instances of a VBScript
  3639. * class that is created when this implementation loads. The reason for
  3640. * using a VBScript class stems from the fact that any object can be passed
  3641. * into the opener property.
  3642. * While this is a good thing, as it lets us pass functions and setup a true
  3643. * bidirectional channel via callbacks, it opens a potential security hole
  3644. * by which the other page can get ahold of the "window" or "document"
  3645. * objects in the parent page and in turn wreak havok. This is due to the
  3646. * fact that any JS object useful for establishing such a bidirectional
  3647. * channel (such as a function) can be used to access a function
  3648. * (eg. obj.toString, or a function itself) created in a specific context,
  3649. * in particular the global context of the sender. Suppose container
  3650. * domain C passes object obj to gadget on domain G. Then the gadget can
  3651. * access C's global context using:
  3652. * var parentWindow = (new obj.toString.constructor("return window;"))();
  3653. * Nulling out all of obj's properties doesn't fix this, since IE helpfully
  3654. * restores them to their original values if you do something like:
  3655. * delete obj.toString; delete obj.toString;
  3656. * Thus, we wrap the necessary functions and information inside a VBScript
  3657. * object. VBScript objects in IE, like DOM objects, are in fact COM
  3658. * wrappers when used in JavaScript, so we can safely pass them around
  3659. * without worrying about a breach of context while at the same time
  3660. * allowing them to act as a pass-through mechanism for information
  3661. * and function calls. The implementation details of this VBScript wrapper
  3662. * can be found in the setupChannel() method below.
  3663. *
  3664. * nix: Internet Explorer-specific window.opener trick.
  3665. * - Internet Explorer 6
  3666. * - Internet Explorer 7
  3667. */
  3668. if (!OpenAjax.gadgets.rpctx.nix) { // make lib resilient to double-inclusion
  3669. OpenAjax.gadgets.rpctx.nix = function() {
  3670. // Consts for NIX. VBScript doesn't
  3671. // allow items to start with _ for some reason,
  3672. // so we need to make these names quite unique, as
  3673. // they will go into the global namespace.
  3674. var NIX_WRAPPER = 'GRPC____NIXVBS_wrapper';
  3675. var NIX_GET_WRAPPER = 'GRPC____NIXVBS_get_wrapper';
  3676. var NIX_HANDLE_MESSAGE = 'GRPC____NIXVBS_handle_message';
  3677. var NIX_CREATE_CHANNEL = 'GRPC____NIXVBS_create_channel';
  3678. var MAX_NIX_SEARCHES = 10;
  3679. var NIX_SEARCH_PERIOD = 500;
  3680. // JavaScript reference to the NIX VBScript wrappers.
  3681. // Gadgets will have but a single channel under
  3682. // nix_channels['..'] while containers will have a channel
  3683. // per gadget stored under the gadget's ID.
  3684. var nix_channels = {};
  3685. var isForceSecure = {};
  3686. // Store the ready signal method for use on handshake complete.
  3687. var ready;
  3688. var numHandlerSearches = 0;
  3689. // Search for NIX handler to parent. Tries MAX_NIX_SEARCHES times every
  3690. // NIX_SEARCH_PERIOD milliseconds.
  3691. function conductHandlerSearch() {
  3692. // Call from gadget to the container.
  3693. var handler = nix_channels['..'];
  3694. if (handler) {
  3695. return;
  3696. }
  3697. if (++numHandlerSearches > MAX_NIX_SEARCHES) {
  3698. // Handshake failed. Will fall back.
  3699. OpenAjax.gadgets.warn('Nix transport setup failed, falling back...');
  3700. ready('..', false);
  3701. return;
  3702. }
  3703. // If the gadget has yet to retrieve a reference to
  3704. // the NIX handler, try to do so now. We don't do a
  3705. // typeof(window.opener.GetAuthToken) check here
  3706. // because it means accessing that field on the COM object, which,
  3707. // being an internal function reference, is not allowed.
  3708. // "in" works because it merely checks for the prescence of
  3709. // the key, rather than actually accessing the object's property.
  3710. // This is just a sanity check, not a validity check.
  3711. if (!handler && window.opener && "GetAuthToken" in window.opener) {
  3712. handler = window.opener;
  3713. // Create the channel to the parent/container.
  3714. // First verify that it knows our auth token to ensure it's not
  3715. // an impostor.
  3716. if (handler.GetAuthToken() == OpenAjax.gadgets.rpc.getAuthToken('..')) {
  3717. // Auth match - pass it back along with our wrapper to finish.
  3718. // own wrapper and our authentication token for co-verification.
  3719. var token = OpenAjax.gadgets.rpc.getAuthToken('..');
  3720. handler.CreateChannel(window[NIX_GET_WRAPPER]('..', token),
  3721. token);
  3722. // Set channel handler
  3723. nix_channels['..'] = handler;
  3724. window.opener = null;
  3725. // Signal success and readiness to send to parent.
  3726. // Container-to-gadget bit flipped in CreateChannel.
  3727. ready('..', true);
  3728. return;
  3729. }
  3730. }
  3731. // Try again.
  3732. window.setTimeout(function() { conductHandlerSearch(); },
  3733. NIX_SEARCH_PERIOD);
  3734. }
  3735. // Returns current window location, without hash values
  3736. function getLocationNoHash() {
  3737. var loc = window.location.href;
  3738. var idx = loc.indexOf('#');
  3739. if (idx == -1) {
  3740. return loc;
  3741. }
  3742. return loc.substring(0, idx);
  3743. }
  3744. // When "forcesecure" is set to true, use the relay file and a simple variant of IFPC to first
  3745. // authenticate the container and gadget with each other. Once that is done, then initialize
  3746. // the NIX protocol.
  3747. function setupSecureRelayToParent(rpctoken) {
  3748. // To the parent, transmit the child's URL, the passed in auth
  3749. // token, and another token generated by the child.
  3750. var childToken = (0x7FFFFFFF * Math.random()) | 0; // TODO expose way to have child set this value
  3751. var data = [
  3752. getLocationNoHash(),
  3753. childToken
  3754. ];
  3755. OpenAjax.gadgets.rpc._createRelayIframe(rpctoken, data);
  3756. // listen for response from parent
  3757. var hash = window.location.href.split('#')[1] || '';
  3758. function relayTimer() {
  3759. var newHash = window.location.href.split('#')[1] || '';
  3760. if (newHash !== hash) {
  3761. clearInterval(relayTimerId);
  3762. var params = OpenAjax.gadgets.util.getUrlParameters(window.location.href);
  3763. if (params.childtoken == childToken) {
  3764. // parent has been authenticated; now init NIX
  3765. conductHandlerSearch();
  3766. return;
  3767. }
  3768. // security error -- token didn't match
  3769. ready('..', false);
  3770. }
  3771. }
  3772. var relayTimerId = setInterval( relayTimer, 100 );
  3773. }
  3774. return {
  3775. getCode: function() {
  3776. return 'nix';
  3777. },
  3778. isParentVerifiable: function(opt_receiverId) {
  3779. // NIX is only parent verifiable if a receiver was setup with "forcesecure" set to TRUE.
  3780. if (opt_receiverId) {
  3781. return isForceSecure[opt_receiverId];
  3782. }
  3783. return false;
  3784. },
  3785. init: function(processFn, readyFn) {
  3786. ready = readyFn;
  3787. // Ensure VBScript wrapper code is in the page and that the
  3788. // global Javascript handlers have been set.
  3789. // VBScript methods return a type of 'unknown' when
  3790. // checked via the typeof operator in IE. Fortunately
  3791. // for us, this only applies to COM objects, so we
  3792. // won't see this for a real Javascript object.
  3793. if (typeof window[NIX_GET_WRAPPER] !== 'unknown') {
  3794. window[NIX_HANDLE_MESSAGE] = function(data) {
  3795. window.setTimeout(
  3796. function() { processFn(OpenAjax.gadgets.json.parse(data)); }, 0);
  3797. };
  3798. window[NIX_CREATE_CHANNEL] = function(name, channel, token) {
  3799. // Verify the authentication token of the gadget trying
  3800. // to create a channel for us.
  3801. if (OpenAjax.gadgets.rpc.getAuthToken(name) === token) {
  3802. nix_channels[name] = channel;
  3803. ready(name, true);
  3804. }
  3805. };
  3806. // Inject the VBScript code needed.
  3807. var vbscript =
  3808. // We create a class to act as a wrapper for
  3809. // a Javascript call, to prevent a break in of
  3810. // the context.
  3811. 'Class ' + NIX_WRAPPER + '\n '
  3812. // An internal member for keeping track of the
  3813. // name of the document (container or gadget)
  3814. // for which this wrapper is intended. For
  3815. // those wrappers created by gadgets, this is not
  3816. // used (although it is set to "..")
  3817. + 'Private m_Intended\n'
  3818. // Stores the auth token used to communicate with
  3819. // the gadget. The GetChannelCreator method returns
  3820. // an object that returns this auth token. Upon matching
  3821. // that with its own, the gadget uses the object
  3822. // to actually establish the communication channel.
  3823. + 'Private m_Auth\n'
  3824. // Method for internally setting the value
  3825. // of the m_Intended property.
  3826. + 'Public Sub SetIntendedName(name)\n '
  3827. + 'If isEmpty(m_Intended) Then\n'
  3828. + 'm_Intended = name\n'
  3829. + 'End If\n'
  3830. + 'End Sub\n'
  3831. // Method for internally setting the value of the m_Auth property.
  3832. + 'Public Sub SetAuth(auth)\n '
  3833. + 'If isEmpty(m_Auth) Then\n'
  3834. + 'm_Auth = auth\n'
  3835. + 'End If\n'
  3836. + 'End Sub\n'
  3837. // A wrapper method which actually causes a
  3838. // message to be sent to the other context.
  3839. + 'Public Sub SendMessage(data)\n '
  3840. + NIX_HANDLE_MESSAGE + '(data)\n'
  3841. + 'End Sub\n'
  3842. // Returns the auth token to the gadget, so it can
  3843. // confirm a match before initiating the connection
  3844. + 'Public Function GetAuthToken()\n '
  3845. + 'GetAuthToken = m_Auth\n'
  3846. + 'End Function\n'
  3847. // Method for setting up the container->gadget
  3848. // channel. Not strictly needed in the gadget's
  3849. // wrapper, but no reason to get rid of it. Note here
  3850. // that we pass the intended name to the NIX_CREATE_CHANNEL
  3851. // method so that it can save the channel in the proper place
  3852. // *and* verify the channel via the authentication token passed
  3853. // here.
  3854. + 'Public Sub CreateChannel(channel, auth)\n '
  3855. + 'Call ' + NIX_CREATE_CHANNEL + '(m_Intended, channel, auth)\n'
  3856. + 'End Sub\n'
  3857. + 'End Class\n'
  3858. // Function to get a reference to the wrapper.
  3859. + 'Function ' + NIX_GET_WRAPPER + '(name, auth)\n'
  3860. + 'Dim wrap\n'
  3861. + 'Set wrap = New ' + NIX_WRAPPER + '\n'
  3862. + 'wrap.SetIntendedName name\n'
  3863. + 'wrap.SetAuth auth\n'
  3864. + 'Set ' + NIX_GET_WRAPPER + ' = wrap\n'
  3865. + 'End Function';
  3866. try {
  3867. window.execScript(vbscript, 'vbscript');
  3868. } catch (e) {
  3869. return false;
  3870. }
  3871. }
  3872. return true;
  3873. },
  3874. setup: function(receiverId, token, forcesecure) {
  3875. isForceSecure[receiverId] = !!forcesecure;
  3876. if (receiverId === '..') {
  3877. if (forcesecure) {
  3878. setupSecureRelayToParent(token);
  3879. } else {
  3880. conductHandlerSearch();
  3881. }
  3882. return true;
  3883. }
  3884. try {
  3885. var frame = document.getElementById(receiverId);
  3886. var wrapper = window[NIX_GET_WRAPPER](receiverId, token);
  3887. frame.contentWindow.opener = wrapper;
  3888. } catch (e) {
  3889. return false;
  3890. }
  3891. return true;
  3892. },
  3893. call: function(targetId, from, rpc) {
  3894. try {
  3895. // If we have a handler, call it.
  3896. if (nix_channels[targetId]) {
  3897. nix_channels[targetId].SendMessage(OpenAjax.gadgets.json.stringify(rpc));
  3898. }
  3899. } catch (e) {
  3900. return false;
  3901. }
  3902. return true;
  3903. },
  3904. // data = [child URL, child auth token]
  3905. relayOnload: function(receiverId, data) {
  3906. // transmit childtoken back to child to complete authentication
  3907. var src = data[0] + '#childtoken=' + data[1];
  3908. var childIframe = document.getElementById(receiverId);
  3909. childIframe.src = src;
  3910. }
  3911. };
  3912. }();
  3913. } // !end of double-inclusion guard
  3914. /*
  3915. * Licensed to the Apache Software Foundation (ASF) under one
  3916. * or more contributor license agreements. See the NOTICE file
  3917. * distributed with this work for additional information
  3918. * regarding copyright ownership. The ASF licenses this file
  3919. * to you under the Apache License, Version 2.0 (the
  3920. * "License"); you may not use this file except in compliance
  3921. * with the License. You may obtain a copy of the License at
  3922. *
  3923. * http://www.apache.org/licenses/LICENSE-2.0
  3924. *
  3925. * Unless required by applicable law or agreed to in writing,
  3926. * software distributed under the License is distributed on an
  3927. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  3928. * KIND, either express or implied. See the License for the
  3929. * specific language governing permissions and limitations under the License.
  3930. */
  3931. OpenAjax.gadgets.rpctx = OpenAjax.gadgets.rpctx || {};
  3932. /*
  3933. * For older WebKit-based browsers, the security model does not allow for any
  3934. * known "native" hacks for conducting cross browser communication. However,
  3935. * a variation of the IFPC (see below) can be used, entitled "RMR". RMR is
  3936. * a technique that uses the resize event of the iframe to indicate that a
  3937. * message was sent (instead of the much slower/performance heavy polling
  3938. * technique used when a defined relay page is not avaliable). Simply put,
  3939. * RMR uses the same "pass the message by the URL hash" trick that IFPC
  3940. * uses to send a message, but instead of having an active relay page that
  3941. * runs a piece of code when it is loaded, RMR merely changes the URL
  3942. * of the relay page (which does not even have to exist on the domain)
  3943. * and then notifies the other party by resizing the relay iframe. RMR
  3944. * exploits the fact that iframes in the dom of page A can be resized
  3945. * by page A while the onresize event will be fired in the DOM of page B,
  3946. * thus providing a single bit channel indicating "message sent to you".
  3947. * This method has the added benefit that the relay need not be active,
  3948. * nor even exist: a 404 suffices just as well.
  3949. *
  3950. * rmr: WebKit-specific resizing trick.
  3951. * - Safari 2+
  3952. * - Chrome 1
  3953. */
  3954. if (!OpenAjax.gadgets.rpctx.rmr) { // make lib resilient to double-inclusion
  3955. OpenAjax.gadgets.rpctx.rmr = function() {
  3956. // Consts for RMR, including time in ms RMR uses to poll for
  3957. // its relay frame to be created, and the max # of polls it does.
  3958. var RMR_SEARCH_TIMEOUT = 500;
  3959. var RMR_MAX_POLLS = 10;
  3960. // JavaScript references to the channel objects used by RMR.
  3961. // Gadgets will have but a single channel under
  3962. // rmr_channels['..'] while containers will have a channel
  3963. // per gadget stored under the gadget's ID.
  3964. var rmr_channels = {};
  3965. var process;
  3966. var ready;
  3967. /**
  3968. * Append an RMR relay frame to the document. This allows the receiver
  3969. * to start receiving messages.
  3970. *
  3971. * @param {Node} channelFrame Relay frame to add to the DOM body.
  3972. * @param {string} relayUri Base URI for the frame.
  3973. * @param {string} data to pass along to the frame.
  3974. * @param {string=} opt_frameId ID of frame for which relay is being appended (optional).
  3975. */
  3976. function appendRmrFrame(channelFrame, relayUri, data, opt_frameId) {
  3977. var appendFn = function() {
  3978. // Append the iframe.
  3979. document.body.appendChild(channelFrame);
  3980. // Set the src of the iframe to 'about:blank' first and then set it
  3981. // to the relay URI. This prevents the iframe from maintaining a src
  3982. // to the 'old' relay URI if the page is returned to from another.
  3983. // In other words, this fixes the bfcache issue that causes the iframe's
  3984. // src property to not be updated despite us assigning it a new value here.
  3985. channelFrame.src = 'about:blank';
  3986. if (opt_frameId) {
  3987. // Process the initial sent payload (typically sent by container to
  3988. // child/gadget) only when the relay frame has finished loading. We
  3989. // do this to ensure that, in processRmrData(...), the ACK sent due
  3990. // to processing can actually be sent. Before this time, the frame's
  3991. // contentWindow is null, making it impossible to do so.
  3992. channelFrame.onload = function() {
  3993. processRmrData(opt_frameId);
  3994. };
  3995. }
  3996. channelFrame.src = relayUri + '#' + data;
  3997. };
  3998. if (document.body) {
  3999. appendFn();
  4000. } else {
  4001. // Common gadget case: attaching header during in-gadget handshake,
  4002. // when we may still be in script in head. Attach onload.
  4003. OpenAjax.gadgets.util.registerOnLoadHandler(function() { appendFn(); });
  4004. }
  4005. }
  4006. /**
  4007. * Sets up the RMR transport frame for the given frameId. For gadgets
  4008. * calling containers, the frameId should be '..'.
  4009. *
  4010. * @param {string} frameId The ID of the frame.
  4011. */
  4012. function setupRmr(frameId) {
  4013. if (typeof rmr_channels[frameId] === "object") {
  4014. // Sanity check. Already done.
  4015. return;
  4016. }
  4017. var channelFrame = document.createElement('iframe');
  4018. var frameStyle = channelFrame.style;
  4019. frameStyle.position = 'absolute';
  4020. frameStyle.top = '0px';
  4021. frameStyle.border = '0';
  4022. frameStyle.opacity = '0';
  4023. // The width here is important as RMR
  4024. // makes use of the resize handler for the frame.
  4025. // Do not modify unless you test thoroughly!
  4026. frameStyle.width = '10px';
  4027. frameStyle.height = '1px';
  4028. channelFrame.id = 'rmrtransport-' + frameId;
  4029. channelFrame.name = channelFrame.id;
  4030. // Use the explicitly set relay, if one exists. Otherwise,
  4031. // Construct one using the parent parameter plus robots.txt
  4032. // as a synthetic relay. This works since browsers using RMR
  4033. // treat 404s as legitimate for the purposes of cross domain
  4034. // communication.
  4035. var relayUri = OpenAjax.gadgets.rpc.getRelayUrl(frameId);
  4036. if (!relayUri) {
  4037. relayUri =
  4038. OpenAjax.gadgets.rpc.getOrigin(OpenAjax.gadgets.util.getUrlParameters()["parent"]) +
  4039. '/robots.txt';
  4040. }
  4041. rmr_channels[frameId] = {
  4042. frame: channelFrame,
  4043. receiveWindow: null,
  4044. relayUri: relayUri,
  4045. searchCounter : 0,
  4046. width: 10,
  4047. // Waiting means "waiting for acknowledgement to be received."
  4048. // Acknowledgement always comes as a special ACK
  4049. // message having been received. This message is received
  4050. // during handshake in different ways by the container and
  4051. // gadget, and by normal RMR message passing once the handshake
  4052. // is complete.
  4053. waiting: true,
  4054. queue: [],
  4055. // Number of non-ACK messages that have been sent to the recipient
  4056. // and have been acknowledged.
  4057. sendId: 0,
  4058. // Number of messages received and processed from the sender.
  4059. // This is the number that accompanies every ACK to tell the
  4060. // sender to clear its queue.
  4061. recvId: 0
  4062. };
  4063. if (frameId !== '..') {
  4064. // Container always appends a relay to the gadget, before
  4065. // the gadget appends its own relay back to container. The
  4066. // gadget, in the meantime, refuses to attach the container
  4067. // relay until it finds this one. Thus, the container knows
  4068. // for certain that gadget to container communication is set
  4069. // up by the time it finds its own relay. In addition to
  4070. // establishing a reliable handshake protocol, this also
  4071. // makes it possible for the gadget to send an initial batch
  4072. // of messages to the container ASAP.
  4073. appendRmrFrame(channelFrame, relayUri, getRmrData(frameId));
  4074. }
  4075. // Start searching for our own frame on the other page.
  4076. conductRmrSearch(frameId);
  4077. }
  4078. /**
  4079. * Searches for a relay frame, created by the sender referenced by
  4080. * frameId, with which this context receives messages. Once
  4081. * found with proper permissions, attaches a resize handler which
  4082. * signals messages to be sent.
  4083. *
  4084. * @param {string} frameId Frame ID of the prospective sender.
  4085. */
  4086. function conductRmrSearch(frameId) {
  4087. var channelWindow = null;
  4088. // Increment the search counter.
  4089. rmr_channels[frameId].searchCounter++;
  4090. try {
  4091. var targetWin = OpenAjax.gadgets.rpc._getTargetWin(frameId);
  4092. if (frameId === '..') {
  4093. // We are a gadget.
  4094. channelWindow = targetWin.frames['rmrtransport-' + OpenAjax.gadgets.rpc.RPC_ID];
  4095. } else {
  4096. // We are a container.
  4097. channelWindow = targetWin.frames['rmrtransport-..'];
  4098. }
  4099. } catch (e) {
  4100. // Just in case; may happen when relay is set to about:blank or unset.
  4101. // Catching exceptions here ensures that the timeout to continue the
  4102. // search below continues to work.
  4103. }
  4104. var status = false;
  4105. if (channelWindow) {
  4106. // We have a valid reference to "our" RMR transport frame.
  4107. // Register the proper event handlers.
  4108. status = registerRmrChannel(frameId, channelWindow);
  4109. }
  4110. if (!status) {
  4111. // Not found yet. Continue searching, but only if the counter
  4112. // has not reached the threshold.
  4113. if (rmr_channels[frameId].searchCounter > RMR_MAX_POLLS) {
  4114. // If we reach this point, then RMR has failed and we
  4115. // fall back to IFPC.
  4116. return;
  4117. }
  4118. window.setTimeout(function() {
  4119. conductRmrSearch(frameId);
  4120. }, RMR_SEARCH_TIMEOUT);
  4121. }
  4122. }
  4123. /**
  4124. * Attempts to conduct an RPC call to the specified
  4125. * target with the specified data via the RMR
  4126. * method. If this method fails, the system attempts again
  4127. * using the known default of IFPC.
  4128. *
  4129. * @param {string} targetId Module Id of the RPC service provider.
  4130. * @param {string} serviceName Name of the service to call.
  4131. * @param {string} from Module Id of the calling provider.
  4132. * @param {Object} rpc The RPC data for this call.
  4133. */
  4134. function callRmr(targetId, serviceName, from, rpc) {
  4135. var handler = null;
  4136. if (from !== '..') {
  4137. // Call from gadget to the container.
  4138. handler = rmr_channels['..'];
  4139. } else {
  4140. // Call from container to the gadget.
  4141. handler = rmr_channels[targetId];
  4142. }
  4143. if (handler) {
  4144. // Queue the current message if not ACK.
  4145. // ACK is always sent through getRmrData(...).
  4146. if (serviceName !== OpenAjax.gadgets.rpc.ACK) {
  4147. handler.queue.push(rpc);
  4148. }
  4149. if (handler.waiting ||
  4150. (handler.queue.length === 0 &&
  4151. !(serviceName === OpenAjax.gadgets.rpc.ACK && rpc && rpc.ackAlone === true))) {
  4152. // If we are awaiting a response from any previously-sent messages,
  4153. // or if we don't have anything new to send, just return.
  4154. // Note that we don't short-return if we're ACKing just-received
  4155. // messages.
  4156. return true;
  4157. }
  4158. if (handler.queue.length > 0) {
  4159. handler.waiting = true;
  4160. }
  4161. var url = handler.relayUri + "#" + getRmrData(targetId);
  4162. try {
  4163. // Update the URL with the message.
  4164. handler.frame.contentWindow.location = url;
  4165. // Resize the frame.
  4166. var newWidth = handler.width == 10 ? 20 : 10;
  4167. handler.frame.style.width = newWidth + 'px';
  4168. handler.width = newWidth;
  4169. // Done!
  4170. } catch (e) {
  4171. // Something about location-setting or resizing failed.
  4172. // This should never happen, but if it does, fall back to
  4173. // the default transport.
  4174. return false;
  4175. }
  4176. }
  4177. return true;
  4178. }
  4179. /**
  4180. * Returns as a string the data to be appended to an RMR relay frame,
  4181. * constructed from the current request queue plus an ACK message indicating
  4182. * the currently latest-processed message ID.
  4183. *
  4184. * @param {string} toFrameId Frame whose sendable queued data to retrieve.
  4185. */
  4186. function getRmrData(toFrameId) {
  4187. var channel = rmr_channels[toFrameId];
  4188. var rmrData = {id: channel.sendId};
  4189. if (channel) {
  4190. rmrData.d = Array.prototype.slice.call(channel.queue, 0);
  4191. rmrData.d.push({s:OpenAjax.gadgets.rpc.ACK, id:channel.recvId});
  4192. }
  4193. return OpenAjax.gadgets.json.stringify(rmrData);
  4194. }
  4195. /**
  4196. * Retrieve data from the channel keyed by the given frameId,
  4197. * processing it as a batch. All processed data is assumed to have been
  4198. * generated by getRmrData(...), pairing that method with this.
  4199. *
  4200. * @param {string} fromFrameId Frame from which data is being retrieved.
  4201. */
  4202. function processRmrData(fromFrameId) {
  4203. var channel = rmr_channels[fromFrameId];
  4204. var data = channel.receiveWindow.location.hash.substring(1);
  4205. // Decode the RPC object array.
  4206. var rpcObj = OpenAjax.gadgets.json.parse(decodeURIComponent(data)) || {};
  4207. var rpcArray = rpcObj.d || [];
  4208. var nonAckReceived = false;
  4209. var noLongerWaiting = false;
  4210. var numBypassed = 0;
  4211. var numToBypass = (channel.recvId - rpcObj.id);
  4212. for (var i = 0; i < rpcArray.length; ++i) {
  4213. var rpc = rpcArray[i];
  4214. // If we receive an ACK message, then mark the current
  4215. // handler as no longer waiting and send out the next
  4216. // queued message.
  4217. if (rpc.s === OpenAjax.gadgets.rpc.ACK) {
  4218. // ACK received - whether this came from a handshake or
  4219. // an active call, in either case it indicates readiness to
  4220. // send messages to the from frame.
  4221. ready(fromFrameId, true);
  4222. if (channel.waiting) {
  4223. noLongerWaiting = true;
  4224. }
  4225. channel.waiting = false;
  4226. var newlyAcked = Math.max(0, rpc.id - channel.sendId);
  4227. channel.queue.splice(0, newlyAcked);
  4228. channel.sendId = Math.max(channel.sendId, rpc.id || 0);
  4229. continue;
  4230. }
  4231. // If we get here, we've received > 0 non-ACK messages to
  4232. // process. Indicate this bit for later.
  4233. nonAckReceived = true;
  4234. // Bypass any messages already received.
  4235. if (++numBypassed <= numToBypass) {
  4236. continue;
  4237. }
  4238. ++channel.recvId;
  4239. process(rpc); // actually dispatch the message
  4240. }
  4241. // Send an ACK indicating that we got/processed the message(s).
  4242. // Do so if we've received a message to process or if we were waiting
  4243. // before but a received ACK has cleared our waiting bit, and we have
  4244. // more messages to send. Performing this operation causes additional
  4245. // messages to be sent.
  4246. if (nonAckReceived ||
  4247. (noLongerWaiting && channel.queue.length > 0)) {
  4248. var from = (fromFrameId === '..') ? OpenAjax.gadgets.rpc.RPC_ID : '..';
  4249. callRmr(fromFrameId, OpenAjax.gadgets.rpc.ACK, from, {ackAlone: nonAckReceived});
  4250. }
  4251. }
  4252. /**
  4253. * Registers the RMR channel handler for the given frameId and associated
  4254. * channel window.
  4255. *
  4256. * @param {string} frameId The ID of the frame for which this channel is being
  4257. * registered.
  4258. * @param {Object} channelWindow The window of the receive frame for this
  4259. * channel, if any.
  4260. *
  4261. * @return {boolean} True if the frame was setup successfully, false
  4262. * otherwise.
  4263. */
  4264. function registerRmrChannel(frameId, channelWindow) {
  4265. var channel = rmr_channels[frameId];
  4266. // Verify that the channel is ready for receiving.
  4267. try {
  4268. var canAccess = false;
  4269. // Check to see if the document is in the window. For Chrome, this
  4270. // will return 'false' if the channelWindow is inaccessible by this
  4271. // piece of JavaScript code, meaning that the URL of the channelWindow's
  4272. // parent iframe has not yet changed from 'about:blank'. We do this
  4273. // check this way because any true *access* on the channelWindow object
  4274. // will raise a security exception, which, despite the try-catch, still
  4275. // gets reported to the debugger (it does not break execution, the try
  4276. // handles that problem, but it is still reported, which is bad form).
  4277. // This check always succeeds in Safari 3.1 regardless of the state of
  4278. // the window.
  4279. canAccess = 'document' in channelWindow;
  4280. if (!canAccess) {
  4281. return false;
  4282. }
  4283. // Check to see if the document is an object. For Safari 3.1, this will
  4284. // return undefined if the page is still inaccessible. Unfortunately, this
  4285. // *will* raise a security issue in the debugger.
  4286. // TODO Find a way around this problem.
  4287. canAccess = typeof channelWindow['document'] == 'object';
  4288. if (!canAccess) {
  4289. return false;
  4290. }
  4291. // Once we get here, we know we can access the document (and anything else)
  4292. // on the window object. Therefore, we check to see if the location is
  4293. // still about:blank (this takes care of the Safari 3.2 case).
  4294. var loc = channelWindow.location.href;
  4295. // Check if this is about:blank for Safari.
  4296. if (loc === 'about:blank') {
  4297. return false;
  4298. }
  4299. } catch (ex) {
  4300. // For some reason, the iframe still points to about:blank. We try
  4301. // again in a bit.
  4302. return false;
  4303. }
  4304. // Save a reference to the receive window.
  4305. channel.receiveWindow = channelWindow;
  4306. // Register the onresize handler.
  4307. function onresize() {
  4308. processRmrData(frameId);
  4309. }
  4310. if (typeof channelWindow.attachEvent === "undefined") {
  4311. channelWindow.onresize = onresize;
  4312. } else {
  4313. channelWindow.attachEvent("onresize", onresize);
  4314. }
  4315. if (frameId === '..') {
  4316. // Gadget to container. Signal to the container that the gadget
  4317. // is ready to receive messages by attaching the g -> c relay.
  4318. // As a nice optimization, pass along any gadget to container
  4319. // queued messages that have backed up since then. ACK is enqueued in
  4320. // getRmrData to ensure that the container's waiting flag is set to false
  4321. // (this happens in the below code run on the container side).
  4322. appendRmrFrame(channel.frame, channel.relayUri, getRmrData(frameId), frameId);
  4323. } else {
  4324. // Process messages that the gadget sent in its initial relay payload.
  4325. // We can do this immediately because the container has already appended
  4326. // and loaded a relay frame that can be used to ACK the messages the gadget
  4327. // sent. In the preceding if-block, however, the processRmrData(...) call
  4328. // must wait. That's because appendRmrFrame may not actually append the
  4329. // frame - in the context of a gadget, this code may be running in the
  4330. // head element, so it cannot be appended to body. As a result, the
  4331. // gadget cannot ACK the container for messages it received.
  4332. processRmrData(frameId);
  4333. }
  4334. return true;
  4335. }
  4336. return {
  4337. getCode: function() {
  4338. return 'rmr';
  4339. },
  4340. isParentVerifiable: function() {
  4341. return true;
  4342. },
  4343. init: function(processFn, readyFn) {
  4344. // No global setup.
  4345. process = processFn;
  4346. ready = readyFn;
  4347. return true;
  4348. },
  4349. setup: function(receiverId, token) {
  4350. try {
  4351. setupRmr(receiverId);
  4352. } catch (e) {
  4353. OpenAjax.gadgets.warn('Caught exception setting up RMR: ' + e);
  4354. return false;
  4355. }
  4356. return true;
  4357. },
  4358. call: function(targetId, from, rpc) {
  4359. return callRmr(targetId, rpc.s, from, rpc);
  4360. }
  4361. };
  4362. }();
  4363. } // !end of double-inclusion guard
  4364. /*
  4365. * Licensed to the Apache Software Foundation (ASF) under one
  4366. * or more contributor license agreements. See the NOTICE file
  4367. * distributed with this work for additional information
  4368. * regarding copyright ownership. The ASF licenses this file
  4369. * to you under the Apache License, Version 2.0 (the
  4370. * "License"); you may not use this file except in compliance
  4371. * with the License. You may obtain a copy of the License at
  4372. *
  4373. * http://www.apache.org/licenses/LICENSE-2.0
  4374. *
  4375. * Unless required by applicable law or agreed to in writing,
  4376. * software distributed under the License is distributed on an
  4377. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  4378. * KIND, either express or implied. See the License for the
  4379. * specific language governing permissions and limitations under the License.
  4380. */
  4381. OpenAjax.gadgets.rpctx = OpenAjax.gadgets.rpctx || {};
  4382. /**
  4383. * Transport for browsers that support native messaging (various implementations
  4384. * of the HTML5 postMessage method). Officially defined at
  4385. * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html.
  4386. *
  4387. * postMessage is a native implementation of XDC. A page registers that
  4388. * it would like to receive messages by listening the the "message" event
  4389. * on the window (document in DPM) object. In turn, another page can
  4390. * raise that event by calling window.postMessage (document.postMessage
  4391. * in DPM) with a string representing the message and a string
  4392. * indicating on which domain the receiving page must be to receive
  4393. * the message. The target page will then have its "message" event raised
  4394. * if the domain matches and can, in turn, check the origin of the message
  4395. * and process the data contained within.
  4396. *
  4397. * wpm: postMessage on the window object.
  4398. * - Internet Explorer 8+
  4399. * - Safari 4+
  4400. * - Chrome 2+
  4401. * - Webkit nightlies
  4402. * - Firefox 3+
  4403. * - Opera 9+
  4404. */
  4405. if (!OpenAjax.gadgets.rpctx.wpm) { // make lib resilient to double-inclusion
  4406. OpenAjax.gadgets.rpctx.wpm = function() {
  4407. var process, ready;
  4408. var postMessage;
  4409. var pmSync = false;
  4410. var pmEventDomain = false;
  4411. // Some browsers (IE, Opera) have an implementation of postMessage that is
  4412. // synchronous, although HTML5 specifies that it should be asynchronous. In
  4413. // order to make all browsers behave consistently, we run a small test to detect
  4414. // if postMessage is asynchronous or not. If not, we wrap calls to postMessage
  4415. // in a setTimeout with a timeout of 0.
  4416. // Also, Opera's "message" event does not have an "origin" property (at least,
  4417. // it doesn't in version 9.64; presumably, it will in version 10). If
  4418. // event.origin does not exist, use event.domain. The other difference is that
  4419. // while event.origin looks like <scheme>://<hostname>:<port>, event.domain
  4420. // consists only of <hostname>.
  4421. //
  4422. function testPostMessage() {
  4423. var hit = false;
  4424. function receiveMsg(event) {
  4425. if (event.data == "postmessage.test") {
  4426. hit = true;
  4427. if (typeof event.origin === "undefined") {
  4428. pmEventDomain = true;
  4429. }
  4430. }
  4431. }
  4432. OpenAjax.gadgets.util.attachBrowserEvent(window, "message", receiveMsg, false);
  4433. window.postMessage("postmessage.test", "*");
  4434. // if 'hit' is true here, then postMessage is synchronous
  4435. if (hit) {
  4436. pmSync = true;
  4437. }
  4438. OpenAjax.gadgets.util.removeBrowserEvent(window, "message", receiveMsg, false);
  4439. }
  4440. function onmessage(packet) {
  4441. var rpc = OpenAjax.gadgets.json.parse(packet.data);
  4442. if (!rpc || !rpc.f) {
  4443. return;
  4444. }
  4445. // for security, check origin against expected value
  4446. var origRelay = OpenAjax.gadgets.rpc.getRelayUrl(rpc.f) ||
  4447. OpenAjax.gadgets.util.getUrlParameters()["parent"];
  4448. var origin = OpenAjax.gadgets.rpc.getOrigin(origRelay);
  4449. if (!pmEventDomain ? packet.origin !== origin :
  4450. packet.domain !== /^.+:\/\/([^:]+).*/.exec( origin )[1]) {
  4451. return;
  4452. }
  4453. process(rpc);
  4454. }
  4455. return {
  4456. getCode: function() {
  4457. return 'wpm';
  4458. },
  4459. isParentVerifiable: function() {
  4460. return true;
  4461. },
  4462. init: function(processFn, readyFn) {
  4463. process = processFn;
  4464. ready = readyFn;
  4465. testPostMessage();
  4466. if (!pmSync) {
  4467. postMessage = function(win, msg, origin) {
  4468. win.postMessage(msg, origin);
  4469. };
  4470. } else {
  4471. postMessage = function(win, msg, origin) {
  4472. window.setTimeout( function() {
  4473. win.postMessage(msg, origin);
  4474. }, 0);
  4475. };
  4476. }
  4477. // Set up native postMessage handler.
  4478. OpenAjax.gadgets.util.attachBrowserEvent(window, 'message', onmessage, false);
  4479. ready('..', true); // Immediately ready to send to parent.
  4480. return true;
  4481. },
  4482. setup: function(receiverId, token, forcesecure) {
  4483. // If we're a gadget, send an ACK message to indicate to container
  4484. // that we're ready to receive messages.
  4485. if (receiverId === '..') {
  4486. if (forcesecure) {
  4487. OpenAjax.gadgets.rpc._createRelayIframe(token);
  4488. } else {
  4489. OpenAjax.gadgets.rpc.call(receiverId, OpenAjax.gadgets.rpc.ACK);
  4490. }
  4491. }
  4492. return true;
  4493. },
  4494. call: function(targetId, from, rpc) {
  4495. var targetWin = OpenAjax.gadgets.rpc._getTargetWin(targetId);
  4496. // targetOrigin = canonicalized relay URL
  4497. var origRelay = OpenAjax.gadgets.rpc.getRelayUrl(targetId) ||
  4498. OpenAjax.gadgets.util.getUrlParameters()["parent"];
  4499. var origin = OpenAjax.gadgets.rpc.getOrigin(origRelay);
  4500. if (origin) {
  4501. postMessage(targetWin, OpenAjax.gadgets.json.stringify(rpc), origin);
  4502. } else {
  4503. OpenAjax.gadgets.error("No relay set (used as window.postMessage targetOrigin)" +
  4504. ", cannot send cross-domain message");
  4505. }
  4506. return true;
  4507. },
  4508. relayOnload: function(receiverId, data) {
  4509. ready(receiverId, true);
  4510. }
  4511. };
  4512. }();
  4513. } // !end of double-inclusion guard
  4514. /*
  4515. * Licensed to the Apache Software Foundation (ASF) under one
  4516. * or more contributor license agreements. See the NOTICE file
  4517. * distributed with this work for additional information
  4518. * regarding copyright ownership. The ASF licenses this file
  4519. * to you under the Apache License, Version 2.0 (the
  4520. * "License"); you may not use this file except in compliance
  4521. * with the License. You may obtain a copy of the License at
  4522. *
  4523. * http://www.apache.org/licenses/LICENSE-2.0
  4524. *
  4525. * Unless required by applicable law or agreed to in writing,
  4526. * software distributed under the License is distributed on an
  4527. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  4528. * KIND, either express or implied. See the License for the
  4529. * specific language governing permissions and limitations under the License.
  4530. */
  4531. /**
  4532. * @fileoverview Remote procedure call library for gadget-to-container,
  4533. * container-to-gadget, and gadget-to-gadget (thru container) communication.
  4534. */
  4535. /**
  4536. * gadgets.rpc Transports
  4537. *
  4538. * All transports are stored in object gadgets.rpctx, and are provided
  4539. * to the core gadgets.rpc library by various build rules.
  4540. *
  4541. * Transports used by core gadgets.rpc code to actually pass messages.
  4542. * each transport implements the same interface exposing hooks that
  4543. * the core library calls at strategic points to set up and use
  4544. * the transport.
  4545. *
  4546. * The methods each transport must implement are:
  4547. * + getCode(): returns a string identifying the transport. For debugging.
  4548. * + isParentVerifiable(): indicates (via boolean) whether the method
  4549. * has the property that its relay URL verifies for certain the
  4550. * receiver's protocol://host:port.
  4551. * + init(processFn, readyFn): Performs any global initialization needed. Called
  4552. * before any other gadgets.rpc methods are invoked. processFn is
  4553. * the function in gadgets.rpc used to process an rpc packet. readyFn is
  4554. * a function that must be called when the transport is ready to send
  4555. * and receive messages bidirectionally. Returns
  4556. * true if successful, false otherwise.
  4557. * + setup(receiverId, token): Performs per-receiver initialization, if any.
  4558. * receiverId will be '..' for gadget-to-container. Returns true if
  4559. * successful, false otherwise.
  4560. * + call(targetId, from, rpc): Invoked to send an actual
  4561. * message to the given targetId, with the given serviceName, from
  4562. * the sender identified by 'from'. Payload is an rpc packet. Returns
  4563. * true if successful, false otherwise.
  4564. */
  4565. if (!OpenAjax.gadgets.rpc) { // make lib resilient to double-inclusion
  4566. /**
  4567. * @static
  4568. * @namespace Provides operations for making rpc calls.
  4569. * @name gadgets.rpc
  4570. */
  4571. OpenAjax.gadgets.rpc = function() {
  4572. /**
  4573. * @const
  4574. * @private
  4575. */
  4576. var CALLBACK_NAME = '__cb';
  4577. /**
  4578. * @const
  4579. * @private
  4580. */
  4581. var DEFAULT_NAME = '';
  4582. /** Exported constant, for use by transports only.
  4583. * @const
  4584. * @type {string}
  4585. * @member gadgets.rpc
  4586. */
  4587. var ACK = '__ack';
  4588. /**
  4589. * Timeout and number of attempts made to setup a transport receiver.
  4590. * @const
  4591. * @private
  4592. */
  4593. var SETUP_FRAME_TIMEOUT = 500;
  4594. /**
  4595. * @const
  4596. * @private
  4597. */
  4598. var SETUP_FRAME_MAX_TRIES = 10;
  4599. var services = {};
  4600. var relayUrl = {};
  4601. var useLegacyProtocol = {};
  4602. var authToken = {};
  4603. var callId = 0;
  4604. var callbacks = {};
  4605. var setup = {};
  4606. var sameDomain = {};
  4607. var params = {};
  4608. var receiverTx = {};
  4609. var earlyRpcQueue = {};
  4610. // isGadget =~ isChild for the purposes of rpc (used only in setup).
  4611. var isChild = (window.top !== window.self);
  4612. // Set the current rpc ID from window.name immediately, to prevent
  4613. // shadowing of window.name by a "var name" declaration, or similar.
  4614. var rpcId = window.name;
  4615. var securityCallback = function() {};
  4616. var LOAD_TIMEOUT = 0;
  4617. var FRAME_PHISH = 1;
  4618. var FORGED_MSG = 2;
  4619. // Fallback transport is simply a dummy impl that emits no errors
  4620. // and logs info on calls it receives, to avoid undesired side-effects
  4621. // from falling back to IFPC or some other transport.
  4622. var fallbackTransport = (function() {
  4623. function logFn(name) {
  4624. return function() {
  4625. OpenAjax.gadgets.log("gadgets.rpc." + name + "(" +
  4626. OpenAjax.gadgets.json.stringify(Array.prototype.slice.call(arguments)) +
  4627. "): call ignored. [caller: " + document.location +
  4628. ", isChild: " + isChild + "]");
  4629. };
  4630. }
  4631. return {
  4632. getCode: function() {
  4633. return "noop";
  4634. },
  4635. isParentVerifiable: function() {
  4636. return true; // Not really, but prevents transport assignment to IFPC.
  4637. },
  4638. init: logFn("init"),
  4639. setup: logFn("setup"),
  4640. call: logFn("call")
  4641. };
  4642. })();
  4643. // Load the authentication token for speaking to the container
  4644. // from the gadget's parameters, or default to '0' if not found.
  4645. if (OpenAjax.gadgets.util) {
  4646. params = OpenAjax.gadgets.util.getUrlParameters();
  4647. }
  4648. /**
  4649. * Return a transport representing the best available cross-domain
  4650. * message-passing mechanism available to the browser.
  4651. *
  4652. * <p>Transports are selected on a cascading basis determined by browser
  4653. * capability and other checks. The order of preference is:
  4654. * <ol>
  4655. * <li> wpm: Uses window.postMessage standard.
  4656. * <li> dpm: Uses document.postMessage, similar to wpm but pre-standard.
  4657. * <li> nix: Uses IE-specific browser hacks.
  4658. * <li> rmr: Signals message passing using relay file's onresize handler.
  4659. * <li> fe: Uses FF2-specific window.frameElement hack.
  4660. * <li> ifpc: Sends messages via active load of a relay file.
  4661. * </ol>
  4662. * <p>See each transport's commentary/documentation for details.
  4663. * @return {Object}
  4664. * @member gadgets.rpc
  4665. */
  4666. function getTransport() {
  4667. return typeof window.postMessage === 'function' ? OpenAjax.gadgets.rpctx.wpm :
  4668. typeof window.postMessage === 'object' ? OpenAjax.gadgets.rpctx.wpm :
  4669. window.ActiveXObject ? OpenAjax.gadgets.rpctx.nix :
  4670. navigator.userAgent.indexOf('WebKit') > 0 ? OpenAjax.gadgets.rpctx.rmr :
  4671. navigator.product === 'Gecko' ? OpenAjax.gadgets.rpctx.frameElement :
  4672. OpenAjax.gadgets.rpctx.ifpc;
  4673. }
  4674. /**
  4675. * Function passed to, and called by, a transport indicating it's ready to
  4676. * send and receive messages.
  4677. */
  4678. function transportReady(receiverId, readySuccess) {
  4679. var tx = transport;
  4680. if (!readySuccess) {
  4681. tx = fallbackTransport;
  4682. }
  4683. receiverTx[receiverId] = tx;
  4684. // If there are any early-queued messages, send them now directly through
  4685. // the needed transport.
  4686. var earlyQueue = earlyRpcQueue[receiverId] || [];
  4687. for (var i = 0; i < earlyQueue.length; ++i) {
  4688. var rpc = earlyQueue[i];
  4689. // There was no auth/rpc token set before, so set it now.
  4690. rpc.t = getAuthToken(receiverId);
  4691. tx.call(receiverId, rpc.f, rpc);
  4692. }
  4693. // Clear the queue so it won't be sent again.
  4694. earlyRpcQueue[receiverId] = [];
  4695. }
  4696. // Track when this main page is closed or navigated to a different location
  4697. // ("unload" event).
  4698. // NOTE: The use of the "unload" handler here and for the relay iframe
  4699. // prevents the use of the in-memory page cache in modern browsers.
  4700. // See: https://developer.mozilla.org/en/using_firefox_1.5_caching
  4701. // See: http://webkit.org/blog/516/webkit-page-cache-ii-the-unload-event/
  4702. var mainPageUnloading = false,
  4703. hookedUnload = false;
  4704. function hookMainPageUnload() {
  4705. if ( hookedUnload ) {
  4706. return;
  4707. }
  4708. function onunload() {
  4709. mainPageUnloading = true;
  4710. }
  4711. OpenAjax.gadgets.util.attachBrowserEvent(window, 'unload', onunload, false);
  4712. hookedUnload = true;
  4713. }
  4714. function relayOnload(targetId, sourceId, token, data, relayWindow) {
  4715. // Validate auth token.
  4716. if (!authToken[sourceId] || authToken[sourceId] !== token) {
  4717. OpenAjax.gadgets.error("Invalid auth token. " + authToken[sourceId] + " vs " + token);
  4718. securityCallback(sourceId, FORGED_MSG);
  4719. }
  4720. relayWindow.onunload = function() {
  4721. if (setup[sourceId] && !mainPageUnloading) {
  4722. securityCallback(sourceId, FRAME_PHISH);
  4723. OpenAjax.gadgets.rpc.removeReceiver(sourceId);
  4724. }
  4725. };
  4726. hookMainPageUnload();
  4727. data = OpenAjax.gadgets.json.parse(decodeURIComponent(data));
  4728. transport.relayOnload(sourceId, data);
  4729. }
  4730. /**
  4731. * Helper function to process an RPC request
  4732. * @param {Object} rpc RPC request object
  4733. * @private
  4734. */
  4735. function process(rpc) {
  4736. //
  4737. // RPC object contents:
  4738. // s: Service Name
  4739. // f: From
  4740. // c: The callback ID or 0 if none.
  4741. // a: The arguments for this RPC call.
  4742. // t: The authentication token.
  4743. //
  4744. if (rpc && typeof rpc.s === 'string' && typeof rpc.f === 'string' &&
  4745. rpc.a instanceof Array) {
  4746. // Validate auth token.
  4747. if (authToken[rpc.f]) {
  4748. // We don't do type coercion here because all entries in the authToken
  4749. // object are strings, as are all url params. See setupReceiver(...).
  4750. if (authToken[rpc.f] !== rpc.t) {
  4751. OpenAjax.gadgets.error("Invalid auth token. " + authToken[rpc.f] + " vs " + rpc.t);
  4752. securityCallback(rpc.f, FORGED_MSG);
  4753. }
  4754. }
  4755. if (rpc.s === ACK) {
  4756. // Acknowledgement API, used to indicate a receiver is ready.
  4757. window.setTimeout(function() { transportReady(rpc.f, true); }, 0);
  4758. return;
  4759. }
  4760. // If there is a callback for this service, attach a callback function
  4761. // to the rpc context object for asynchronous rpc services.
  4762. //
  4763. // Synchronous rpc request handlers should simply ignore it and return a
  4764. // value as usual.
  4765. // Asynchronous rpc request handlers, on the other hand, should pass its
  4766. // result to this callback function and not return a value on exit.
  4767. //
  4768. // For example, the following rpc handler passes the first parameter back
  4769. // to its rpc client with a one-second delay.
  4770. //
  4771. // function asyncRpcHandler(param) {
  4772. // var me = this;
  4773. // setTimeout(function() {
  4774. // me.callback(param);
  4775. // }, 1000);
  4776. // }
  4777. if (rpc.c) {
  4778. rpc.callback = function(result) {
  4779. OpenAjax.gadgets.rpc.call(rpc.f, CALLBACK_NAME, null, rpc.c, result);
  4780. };
  4781. }
  4782. // Call the requested RPC service.
  4783. var result = (services[rpc.s] ||
  4784. services[DEFAULT_NAME]).apply(rpc, rpc.a);
  4785. // If the rpc request handler returns a value, immediately pass it back
  4786. // to the callback. Otherwise, do nothing, assuming that the rpc handler
  4787. // will make an asynchronous call later.
  4788. if (rpc.c && typeof result !== 'undefined') {
  4789. OpenAjax.gadgets.rpc.call(rpc.f, CALLBACK_NAME, null, rpc.c, result);
  4790. }
  4791. }
  4792. }
  4793. /**
  4794. * Helper method returning a canonicalized protocol://host[:port] for
  4795. * a given input URL, provided as a string. Used to compute convenient
  4796. * relay URLs and to determine whether a call is coming from the same
  4797. * domain as its receiver (bypassing the try/catch capability detection
  4798. * flow, thereby obviating Firebug and other tools reporting an exception).
  4799. *
  4800. * @param {string} url Base URL to canonicalize.
  4801. * @memberOf gadgets.rpc
  4802. */
  4803. function getOrigin(url) {
  4804. if (!url) {
  4805. return "";
  4806. }
  4807. url = url.toLowerCase();
  4808. if (url.indexOf("//") == 0) {
  4809. url = window.location.protocol + url;
  4810. }
  4811. if (url.indexOf("://") == -1) {
  4812. // Assumed to be schemaless. Default to current protocol.
  4813. url = window.location.protocol + "//" + url;
  4814. }
  4815. // At this point we guarantee that "://" is in the URL and defines
  4816. // current protocol. Skip past this to search for host:port.
  4817. var host = url.substring(url.indexOf("://") + 3);
  4818. // Find the first slash char, delimiting the host:port.
  4819. var slashPos = host.indexOf("/");
  4820. if (slashPos != -1) {
  4821. host = host.substring(0, slashPos);
  4822. }
  4823. var protocol = url.substring(0, url.indexOf("://"));
  4824. // Use port only if it's not default for the protocol.
  4825. var portStr = "";
  4826. var portPos = host.indexOf(":");
  4827. if (portPos != -1) {
  4828. var port = host.substring(portPos + 1);
  4829. host = host.substring(0, portPos);
  4830. if ((protocol === "http" && port !== "80") ||
  4831. (protocol === "https" && port !== "443")) {
  4832. portStr = ":" + port;
  4833. }
  4834. }
  4835. // Return <protocol>://<host>[<port>]
  4836. return protocol + "://" + host + portStr;
  4837. }
  4838. function getTargetWin(id) {
  4839. if (typeof id === "undefined" ||
  4840. id === "..") {
  4841. return window.parent;
  4842. }
  4843. // Cast to a String to avoid an index lookup.
  4844. id = String(id);
  4845. // Try window.frames first
  4846. var target = window.frames[id];
  4847. if (target) {
  4848. return target;
  4849. }
  4850. // Fall back to getElementById()
  4851. target = document.getElementById(id);
  4852. if (target && target.contentWindow) {
  4853. return target.contentWindow;
  4854. }
  4855. return null;
  4856. }
  4857. // Pick the most efficient RPC relay mechanism.
  4858. var transport = getTransport();
  4859. // Create the Default RPC handler.
  4860. services[DEFAULT_NAME] = function() {
  4861. OpenAjax.gadgets.warn('Unknown RPC service: ' + this.s);
  4862. };
  4863. // Create a Special RPC handler for callbacks.
  4864. services[CALLBACK_NAME] = function(callbackId, result) {
  4865. var callback = callbacks[callbackId];
  4866. if (callback) {
  4867. delete callbacks[callbackId];
  4868. callback(result);
  4869. }
  4870. };
  4871. /**
  4872. * Conducts any frame-specific work necessary to setup
  4873. * the channel type chosen. This method is called when
  4874. * the container page first registers the gadget in the
  4875. * RPC mechanism. Gadgets, in turn, will complete the setup
  4876. * of the channel once they send their first messages.
  4877. */
  4878. function setupFrame(frameId, token, forcesecure) {
  4879. if (setup[frameId] === true) {
  4880. return;
  4881. }
  4882. if (typeof setup[frameId] === 'undefined') {
  4883. setup[frameId] = 0;
  4884. }
  4885. var tgtFrame = document.getElementById(frameId);
  4886. if (frameId === '..' || tgtFrame != null) {
  4887. if (transport.setup(frameId, token, forcesecure) === true) {
  4888. setup[frameId] = true;
  4889. return;
  4890. }
  4891. }
  4892. if (setup[frameId] !== true && setup[frameId]++ < SETUP_FRAME_MAX_TRIES) {
  4893. // Try again in a bit, assuming that frame will soon exist.
  4894. window.setTimeout(function() { setupFrame(frameId, token, forcesecure); },
  4895. SETUP_FRAME_TIMEOUT);
  4896. } else {
  4897. // Fail: fall back for this gadget.
  4898. receiverTx[frameId] = fallbackTransport;
  4899. setup[frameId] = true;
  4900. }
  4901. }
  4902. /**
  4903. * Attempts to make an rpc by calling the target's receive method directly.
  4904. * This works when gadgets are rendered on the same domain as their container,
  4905. * a potentially useful optimization for trusted content which keeps
  4906. * RPC behind a consistent interface.
  4907. *
  4908. * @param {string} target Module id of the rpc service provider
  4909. * @param {Object} rpc RPC data
  4910. * @return {boolean}
  4911. */
  4912. function callSameDomain(target, rpc) {
  4913. if (typeof sameDomain[target] === 'undefined') {
  4914. // Seed with a negative, typed value to avoid
  4915. // hitting this code path repeatedly.
  4916. sameDomain[target] = false;
  4917. var targetRelay = OpenAjax.gadgets.rpc.getRelayUrl(target);
  4918. if (getOrigin(targetRelay) !== getOrigin(window.location.href)) {
  4919. // Not worth trying -- avoid the error and just return.
  4920. return false;
  4921. }
  4922. var targetEl = getTargetWin(target);
  4923. try {
  4924. // If this succeeds, then same-domain policy applied
  4925. sameDomain[target] = targetEl.OpenAjax.gadgets.rpc.receiveSameDomain;
  4926. } catch (e) {
  4927. // Shouldn't happen due to origin check. Caught to emit
  4928. // more meaningful error to the caller.
  4929. OpenAjax.gadgets.error("Same domain call failed: parent= incorrectly set.");
  4930. }
  4931. }
  4932. if (typeof sameDomain[target] === 'function') {
  4933. // Call target's receive method
  4934. sameDomain[target](rpc);
  4935. return true;
  4936. }
  4937. return false;
  4938. }
  4939. /**
  4940. * Sets the relay URL of a target frame.
  4941. * @param {string} targetId Name of the target frame.
  4942. * @param {string} url Full relay URL of the target frame.
  4943. * @param {boolean=} opt_useLegacy True if this relay needs the legacy IFPC
  4944. * wire format.
  4945. *
  4946. * @member gadgets.rpc
  4947. * @deprecated
  4948. */
  4949. function setRelayUrl(targetId, url, opt_useLegacy) {
  4950. // make URL absolute if necessary
  4951. if (!/http(s)?:\/\/.+/.test(url)) {
  4952. if (url.indexOf("//") == 0) {
  4953. url = window.location.protocol + url;
  4954. } else if (url.charAt(0) == '/') {
  4955. url = window.location.protocol + "//" + window.location.host + url;
  4956. } else if (url.indexOf("://") == -1) {
  4957. // Assumed to be schemaless. Default to current protocol.
  4958. url = window.location.protocol + "//" + url;
  4959. }
  4960. }
  4961. relayUrl[targetId] = url;
  4962. useLegacyProtocol[targetId] = !!opt_useLegacy;
  4963. }
  4964. /**
  4965. * Helper method to retrieve the authToken for a given gadget.
  4966. * Not to be used directly.
  4967. * @member gadgets.rpc
  4968. * @return {string}
  4969. */
  4970. function getAuthToken(targetId) {
  4971. return authToken[targetId];
  4972. }
  4973. /**
  4974. * Sets the auth token of a target frame.
  4975. * @param {string} targetId Name of the target frame.
  4976. * @param {string} token The authentication token to use for all
  4977. * calls to or from this target id.
  4978. *
  4979. * @member gadgets.rpc
  4980. * @deprecated
  4981. */
  4982. function setAuthToken(targetId, token, forcesecure) {
  4983. token = token || "";
  4984. // Coerce token to a String, ensuring that all authToken values
  4985. // are strings. This ensures correct comparison with URL params
  4986. // in the process(rpc) method.
  4987. authToken[targetId] = String(token);
  4988. setupFrame(targetId, token, forcesecure);
  4989. }
  4990. function setupContainerGadgetContext(rpctoken, opt_forcesecure) {
  4991. /**
  4992. * Initializes gadget to container RPC params from the provided configuration.
  4993. */
  4994. function init(config) {
  4995. var configRpc = config ? config.rpc : {};
  4996. var parentRelayUrl = configRpc.parentRelayUrl;
  4997. // Allow for wild card parent relay files as long as it's from a
  4998. // white listed domain. This is enforced by the rendering servlet.
  4999. if (parentRelayUrl.substring(0, 7) !== 'http://' &&
  5000. parentRelayUrl.substring(0, 8) !== 'https://' &&
  5001. parentRelayUrl.substring(0, 2) !== '//') {
  5002. // Relative path: we append to the parent.
  5003. // We're relying on the server validating the parent parameter in this
  5004. // case. Because of this, parent may only be passed in the query, not fragment.
  5005. if (typeof params.parent === "string" && params.parent !== "") {
  5006. // Otherwise, relayUrl['..'] will be null, signaling transport
  5007. // code to ignore rpc calls since they cannot work without a
  5008. // relay URL with host qualification.
  5009. if (parentRelayUrl.substring(0, 1) !== '/') {
  5010. // Path-relative. Trust that parent is passed in appropriately.
  5011. var lastSlash = params.parent.lastIndexOf('/');
  5012. parentRelayUrl = params.parent.substring(0, lastSlash + 1) + parentRelayUrl;
  5013. } else {
  5014. // Host-relative.
  5015. parentRelayUrl = getOrigin(params.parent) + parentRelayUrl;
  5016. }
  5017. }
  5018. }
  5019. var useLegacy = !!configRpc.useLegacyProtocol;
  5020. setRelayUrl('..', parentRelayUrl, useLegacy);
  5021. if (useLegacy) {
  5022. transport = OpenAjax.gadgets.rpctx.ifpc;
  5023. transport.init(process, transportReady);
  5024. }
  5025. // Sets the auth token and signals transport to setup connection to container.
  5026. var forceSecure = opt_forcesecure || params.forcesecure || false;
  5027. setAuthToken('..', rpctoken, forceSecure);
  5028. }
  5029. var requiredConfig = {
  5030. parentRelayUrl : OpenAjax.gadgets.config.NonEmptyStringValidator
  5031. };
  5032. OpenAjax.gadgets.config.register("rpc", requiredConfig, init);
  5033. }
  5034. function setupContainerGenericIframe(rpctoken, opt_parent, opt_forcesecure) {
  5035. // Generic child IFRAME setting up connection w/ its container.
  5036. // Use the opt_parent param if provided, or the "parent" query param
  5037. // if found -- otherwise, do nothing since this call might be initiated
  5038. // automatically at first, then actively later in IFRAME code.
  5039. var forcesecure = opt_forcesecure || params.forcesecure || false;
  5040. var parent = opt_parent || params.parent;
  5041. if (parent) {
  5042. setRelayUrl('..', parent);
  5043. setAuthToken('..', rpctoken, forcesecure);
  5044. }
  5045. }
  5046. function setupChildIframe(gadgetId, opt_frameurl, opt_authtoken, opt_forcesecure) {
  5047. if (!OpenAjax.gadgets.util) {
  5048. return;
  5049. }
  5050. var childIframe = document.getElementById(gadgetId);
  5051. if (!childIframe) {
  5052. throw new Error("Cannot set up gadgets.rpc receiver with ID: " + gadgetId +
  5053. ", element not found.");
  5054. }
  5055. // The "relay URL" can either be explicitly specified or is set as
  5056. // the child IFRAME URL verbatim.
  5057. var relayUrl = opt_frameurl || childIframe.src;
  5058. setRelayUrl(gadgetId, relayUrl);
  5059. // The auth token is parsed from child params (rpctoken) or overridden.
  5060. var childParams = OpenAjax.gadgets.util.getUrlParameters(childIframe.src);
  5061. var rpctoken = opt_authtoken || childParams.rpctoken;
  5062. var forcesecure = opt_forcesecure || childParams.forcesecure;
  5063. setAuthToken(gadgetId, rpctoken, forcesecure);
  5064. }
  5065. /**
  5066. * Sets up the gadgets.rpc library to communicate with the receiver.
  5067. * <p>This method replaces setRelayUrl(...) and setAuthToken(...)
  5068. *
  5069. * <p>Simplified instructions - highly recommended:
  5070. * <ol>
  5071. * <li> Generate &lt;iframe id="&lt;ID&gt;" src="...#parent=&lt;PARENTURL&gt;&rpctoken=&lt;RANDOM&gt;"/&gt;
  5072. * and add to DOM.
  5073. * <li> Call gadgets.rpc.setupReceiver("&lt;ID>");
  5074. * <p>All parent/child communication initializes automatically from here.
  5075. * Naturally, both sides need to include the library.
  5076. * </ol>
  5077. *
  5078. * <p>Detailed container/parent instructions:
  5079. * <ol>
  5080. * <li> Create the target IFRAME (eg. gadget) with a given &lt;ID> and params
  5081. * rpctoken=<token> (eg. #rpctoken=1234), which is a random/unguessbable
  5082. * string, and parent=&lt;url>, where &lt;url> is the URL of the container.
  5083. * <li> Append IFRAME to the document.
  5084. * <li> Call gadgets.rpc.setupReceiver(&lt;ID>)
  5085. * <p>[Optional]. Strictly speaking, you may omit rpctoken and parent. This
  5086. * practice earns little but is occasionally useful for testing.
  5087. * If you omit parent, you MUST pass your container URL as the 2nd
  5088. * parameter to this method.
  5089. * </ol>
  5090. *
  5091. * <p>Detailed gadget/child IFRAME instructions:
  5092. * <ol>
  5093. * <li> If your container/parent passed parent and rpctoken params (query string
  5094. * or fragment are both OK), you needn't do anything. The library will self-
  5095. * initialize.
  5096. * <li> If "parent" is omitted, you MUST call this method with targetId '..'
  5097. * and the second param set to the parent URL.
  5098. * <li> If "rpctoken" is omitted, but the container set an authToken manually
  5099. * for this frame, you MUST pass that ID (however acquired) as the 2nd param
  5100. * to this method.
  5101. * </ol>
  5102. *
  5103. * @member gadgets.rpc
  5104. * @param {string} targetId
  5105. * @param {string=} opt_receiverurl
  5106. * @param {string=} opt_authtoken
  5107. * @param {boolean=} opt_forcesecure
  5108. */
  5109. function setupReceiver(targetId, opt_receiverurl, opt_authtoken, opt_forcesecure) {
  5110. if (targetId === '..') {
  5111. // Gadget/IFRAME to container.
  5112. var rpctoken = opt_authtoken || params.rpctoken || params.ifpctok || "";
  5113. if (window['__isgadget'] === true) {
  5114. setupContainerGadgetContext(rpctoken, opt_forcesecure);
  5115. } else {
  5116. setupContainerGenericIframe(rpctoken, opt_receiverurl, opt_forcesecure);
  5117. }
  5118. } else {
  5119. // Container to child.
  5120. setupChildIframe(targetId, opt_receiverurl, opt_authtoken, opt_forcesecure);
  5121. }
  5122. }
  5123. return /** @scope gadgets.rpc */ {
  5124. config: function(config) {
  5125. if (typeof config.securityCallback === 'function') {
  5126. securityCallback = config.securityCallback;
  5127. }
  5128. },
  5129. /**
  5130. * Registers an RPC service.
  5131. * @param {string} serviceName Service name to register.
  5132. * @param {function(Object,Object)} handler Service handler.
  5133. *
  5134. * @member gadgets.rpc
  5135. */
  5136. register: function(serviceName, handler) {
  5137. if (serviceName === CALLBACK_NAME || serviceName === ACK) {
  5138. throw new Error("Cannot overwrite callback/ack service");
  5139. }
  5140. if (serviceName === DEFAULT_NAME) {
  5141. throw new Error("Cannot overwrite default service:"
  5142. + " use registerDefault");
  5143. }
  5144. services[serviceName] = handler;
  5145. },
  5146. /**
  5147. * Unregisters an RPC service.
  5148. * @param {string} serviceName Service name to unregister.
  5149. *
  5150. * @member gadgets.rpc
  5151. */
  5152. unregister: function(serviceName) {
  5153. if (serviceName === CALLBACK_NAME || serviceName === ACK) {
  5154. throw new Error("Cannot delete callback/ack service");
  5155. }
  5156. if (serviceName === DEFAULT_NAME) {
  5157. throw new Error("Cannot delete default service:"
  5158. + " use unregisterDefault");
  5159. }
  5160. delete services[serviceName];
  5161. },
  5162. /**
  5163. * Registers a default service handler to processes all unknown
  5164. * RPC calls which raise an exception by default.
  5165. * @param {function(Object,Object)} handler Service handler.
  5166. *
  5167. * @member gadgets.rpc
  5168. */
  5169. registerDefault: function(handler) {
  5170. services[DEFAULT_NAME] = handler;
  5171. },
  5172. /**
  5173. * Unregisters the default service handler. Future unknown RPC
  5174. * calls will fail silently.
  5175. *
  5176. * @member gadgets.rpc
  5177. */
  5178. unregisterDefault: function() {
  5179. delete services[DEFAULT_NAME];
  5180. },
  5181. /**
  5182. * Forces all subsequent calls to be made by a transport
  5183. * method that allows the caller to verify the message receiver
  5184. * (by way of the parent parameter, through getRelayUrl(...)).
  5185. * At present this means IFPC or WPM.
  5186. * @member gadgets.rpc
  5187. */
  5188. forceParentVerifiable: function() {
  5189. if (!transport.isParentVerifiable()) {
  5190. transport = OpenAjax.gadgets.rpctx.ifpc;
  5191. }
  5192. },
  5193. /**
  5194. * Calls an RPC service.
  5195. * @param {string} targetId Module Id of the RPC service provider.
  5196. * Empty if calling the parent container.
  5197. * @param {string} serviceName Service name to call.
  5198. * @param {function()|null} callback Callback function (if any) to process
  5199. * the return value of the RPC request.
  5200. * @param {*} var_args Parameters for the RPC request.
  5201. *
  5202. * @member gadgets.rpc
  5203. */
  5204. call: function(targetId, serviceName, callback, var_args) {
  5205. targetId = targetId || '..';
  5206. // Default to the container calling.
  5207. var from = '..';
  5208. if (targetId === '..') {
  5209. from = rpcId;
  5210. }
  5211. ++callId;
  5212. if (callback) {
  5213. callbacks[callId] = callback;
  5214. }
  5215. var rpc = {
  5216. s: serviceName,
  5217. f: from,
  5218. c: callback ? callId : 0,
  5219. a: Array.prototype.slice.call(arguments, 3),
  5220. t: authToken[targetId],
  5221. l: useLegacyProtocol[targetId]
  5222. };
  5223. if (targetId !== '..' && !document.getElementById(targetId)) {
  5224. // The target has been removed from the DOM. Don't even try.
  5225. OpenAjax.gadgets.log("WARNING: attempted send to nonexistent frame: " + targetId);
  5226. return;
  5227. }
  5228. // If target is on the same domain, call method directly
  5229. if (callSameDomain(targetId, rpc)) {
  5230. return;
  5231. }
  5232. // Attempt to make call via a cross-domain transport.
  5233. // Retrieve the transport for the given target - if one
  5234. // target is misconfigured, it won't affect the others.
  5235. var channel = receiverTx[targetId];
  5236. if (!channel) {
  5237. // Not set up yet. Enqueue the rpc for such time as it is.
  5238. if (!earlyRpcQueue[targetId]) {
  5239. earlyRpcQueue[targetId] = [ rpc ];
  5240. } else {
  5241. earlyRpcQueue[targetId].push(rpc);
  5242. }
  5243. return;
  5244. }
  5245. // If we are told to use the legacy format, then we must
  5246. // default to IFPC.
  5247. if (useLegacyProtocol[targetId]) {
  5248. channel = OpenAjax.gadgets.rpctx.ifpc;
  5249. }
  5250. if (channel.call(targetId, from, rpc) === false) {
  5251. // Fall back to IFPC. This behavior may be removed as IFPC is as well.
  5252. receiverTx[targetId] = fallbackTransport;
  5253. transport.call(targetId, from, rpc);
  5254. }
  5255. },
  5256. /**
  5257. * Gets the relay URL of a target frame.
  5258. * @param {string} targetId Name of the target frame.
  5259. * @return {string|undefined} Relay URL of the target frame.
  5260. *
  5261. * @member gadgets.rpc
  5262. */
  5263. getRelayUrl: function(targetId) {
  5264. var url = relayUrl[targetId];
  5265. // Some RPC methods (wpm, for one) are unhappy with schemeless URLs.
  5266. if (url && url.substring(0,1) === '/') {
  5267. if (url.substring(1,2) === '/') { // starts with '//'
  5268. url = document.location.protocol + url;
  5269. } else { // relative URL, starts with '/'
  5270. url = document.location.protocol + '//' + document.location.host + url;
  5271. }
  5272. }
  5273. return url;
  5274. },
  5275. setRelayUrl: setRelayUrl,
  5276. setAuthToken: setAuthToken,
  5277. setupReceiver: setupReceiver,
  5278. getAuthToken: getAuthToken,
  5279. // Note: Does not delete iframe
  5280. removeReceiver: function(receiverId) {
  5281. delete relayUrl[receiverId];
  5282. delete useLegacyProtocol[receiverId];
  5283. delete authToken[receiverId];
  5284. delete setup[receiverId];
  5285. delete sameDomain[receiverId];
  5286. delete receiverTx[receiverId];
  5287. },
  5288. /**
  5289. * Gets the RPC relay mechanism.
  5290. * @return {string} RPC relay mechanism. See above for
  5291. * a list of supported types.
  5292. *
  5293. * @member gadgets.rpc
  5294. */
  5295. getRelayChannel: function() {
  5296. return transport.getCode();
  5297. },
  5298. /**
  5299. * Receives and processes an RPC request. (Not to be used directly.)
  5300. * Only used by IFPC.
  5301. * @param {Array.<string>} fragment An RPC request fragment encoded as
  5302. * an array. The first 4 elements are target id, source id & call id,
  5303. * total packet number, packet id. The last element stores the actual
  5304. * JSON-encoded and URI escaped packet data.
  5305. *
  5306. * @member gadgets.rpc
  5307. * @deprecated
  5308. */
  5309. receive: function(fragment, otherWindow) {
  5310. if (fragment.length > 4) {
  5311. process(OpenAjax.gadgets.json.parse(
  5312. decodeURIComponent(fragment[fragment.length - 1])));
  5313. } else {
  5314. relayOnload.apply(null, fragment.concat(otherWindow));
  5315. }
  5316. },
  5317. /**
  5318. * Receives and processes an RPC request sent via the same domain.
  5319. * (Not to be used directly). Converts the inbound rpc object's
  5320. * Array into a local Array to pass the process() Array test.
  5321. * @param {Object} rpc RPC object containing all request params
  5322. * @member gadgets.rpc
  5323. */
  5324. receiveSameDomain: function(rpc) {
  5325. // Pass through to local process method but converting to a local Array
  5326. rpc.a = Array.prototype.slice.call(rpc.a);
  5327. window.setTimeout(function() { process(rpc); }, 0);
  5328. },
  5329. // Helper method to get the protocol://host:port of an input URL.
  5330. // see docs above
  5331. getOrigin: getOrigin,
  5332. getReceiverOrigin: function(receiverId) {
  5333. var channel = receiverTx[receiverId];
  5334. if (!channel) {
  5335. // not set up yet
  5336. return null;
  5337. }
  5338. if (!channel.isParentVerifiable(receiverId)) {
  5339. // given transport cannot verify receiver origin
  5340. return null;
  5341. }
  5342. var origRelay = OpenAjax.gadgets.rpc.getRelayUrl(receiverId) ||
  5343. OpenAjax.gadgets.util.getUrlParameters().parent;
  5344. return OpenAjax.gadgets.rpc.getOrigin(origRelay);
  5345. },
  5346. /**
  5347. * Internal-only method used to initialize gadgets.rpc.
  5348. * @member gadgets.rpc
  5349. */
  5350. init: function() {
  5351. // Conduct any global setup necessary for the chosen transport.
  5352. // Do so after gadgets.rpc definition to allow transport to access
  5353. // gadgets.rpc methods.
  5354. if (transport.init(process, transportReady) === false) {
  5355. transport = fallbackTransport;
  5356. }
  5357. if (isChild) {
  5358. setupReceiver('..');
  5359. }
  5360. },
  5361. /** Returns the window keyed by the ID. null/".." for parent, else child */
  5362. _getTargetWin: getTargetWin,
  5363. /** Create an iframe for loading the relay URL. Used by child only. */
  5364. _createRelayIframe: function(token, data) {
  5365. var relay = OpenAjax.gadgets.rpc.getRelayUrl('..');
  5366. if (!relay) {
  5367. return;
  5368. }
  5369. // Format: #targetId & sourceId & authToken & data
  5370. var src = relay + '#..&' + rpcId + '&' + token + '&' +
  5371. encodeURIComponent(OpenAjax.gadgets.json.stringify(data));
  5372. var iframe = document.createElement('iframe');
  5373. iframe.style.border = iframe.style.width = iframe.style.height = '0px';
  5374. iframe.style.visibility = 'hidden';
  5375. iframe.style.position = 'absolute';
  5376. function appendFn() {
  5377. // Append the iframe.
  5378. document.body.appendChild(iframe);
  5379. // Set the src of the iframe to 'about:blank' first and then set it
  5380. // to the relay URI. This prevents the iframe from maintaining a src
  5381. // to the 'old' relay URI if the page is returned to from another.
  5382. // In other words, this fixes the bfcache issue that causes the iframe's
  5383. // src property to not be updated despite us assigning it a new value here.
  5384. iframe.src = 'javascript:"<html></html>"';
  5385. iframe.src = src;
  5386. }
  5387. if (document.body) {
  5388. appendFn();
  5389. } else {
  5390. OpenAjax.gadgets.util.registerOnLoadHandler(function() { appendFn(); });
  5391. }
  5392. return iframe;
  5393. },
  5394. ACK: ACK,
  5395. RPC_ID: rpcId,
  5396. SEC_ERROR_LOAD_TIMEOUT: LOAD_TIMEOUT,
  5397. SEC_ERROR_FRAME_PHISH: FRAME_PHISH,
  5398. SEC_ERROR_FORGED_MSG : FORGED_MSG
  5399. };
  5400. }();
  5401. // Initialize library/transport.
  5402. OpenAjax.gadgets.rpc.init();
  5403. } // !end of double-inclusion guard