right.js 151 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309
  1. /**
  2. * RightJS v2.3.1 - http://rightjs.org
  3. * Released under the terms of MIT license
  4. *
  5. * Copyright (C) 2008-2012 Nikolay Nemshilov
  6. */
  7. /**
  8. * The basic layout for RightJS builds
  9. *
  10. * Copyright (C) 2008-2011 Nikolay Nemshilov
  11. */
  12. var RightJS = (function(window, document, Object, Array, String, Function, Number, Math, undefined) {
  13. /**
  14. * The framework description object
  15. *
  16. * Copyright (C) 2008-2011 Nikolay Nemshilov
  17. */
  18. var RightJS = function(value) {
  19. return value; // <- a dummy method to emulate the safe-mode
  20. };
  21. RightJS.version = "2.3.1";
  22. RightJS.modules =["core", "dom", "form", "events", "xhr", "fx", "cookie"];
  23. /**
  24. * There are some util methods
  25. *
  26. * Credits:
  27. * Some of the functionality and names are inspired or copied from
  28. * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
  29. * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
  30. *
  31. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  32. */
  33. /**
  34. * Some top-level variables to shortify the things
  35. */
  36. var A_proto = Array.prototype,
  37. to_s = Object.prototype.toString, slice = A_proto.slice,
  38. HTML = document.documentElement, UID = 1, // !#server
  39. Wrappers_Cache = [], UID_KEY = 'uniqueNumber', // DON'T change the UID_KEY!
  40. /**
  41. * extends the first object with the keys and values of the second one
  42. *
  43. * NOTE: the third optional argument tells if the existing values
  44. * of the first object should _NOT_ get updated by the values of the second object
  45. *
  46. * @param oritinal Object destintation object
  47. * @param source Object source object
  48. * @param Boolean flag if the function should not overwrite intersecting values
  49. * @return Object extended destination object
  50. */
  51. $ext = RightJS.$ext = function(dest, source, dont_overwrite) {
  52. var src = source || {}, key;
  53. for (key in src) {
  54. if (!dont_overwrite || !(key in dest)) {
  55. dest[key] = src[key];
  56. }
  57. }
  58. return dest;
  59. },
  60. /** !#server
  61. * evals the given javascript text in the context of the current window
  62. *
  63. * @param String javascript
  64. * @return void
  65. */
  66. $eval = RightJS.$eval = function(text) {
  67. if (text) {
  68. if ('execScript' in window) {
  69. current_Document.win()._.execScript(text);
  70. } else {
  71. $E('script', {text: text}).insertTo(HTML);
  72. }
  73. }
  74. },
  75. /**
  76. * throws an exception to break iterations throw a callback
  77. *
  78. * @return void
  79. * @throws Break
  80. */
  81. $break = RightJS.$break = function() {
  82. throw new Break();
  83. },
  84. /**
  85. * generates aliases for the object properties
  86. *
  87. * @param object Object object
  88. * @param names Object aliases hash
  89. * @return Object the extended objects
  90. */
  91. $alias = RightJS.$alias = function(object, names) {
  92. for (var new_name in names) {
  93. object[new_name] = object[names[new_name]];
  94. }
  95. return object;
  96. },
  97. /**
  98. * checks if the given value or a reference points
  99. * to a really defined value
  100. *
  101. * NOTE: will return true for variables equal to null, false, 0, and so one.
  102. *
  103. * EXAMPLE:
  104. *
  105. * var smth = null;
  106. * defined(smth); <- will return true
  107. *
  108. * var obj = {};
  109. * defined(obj['smth']); <- will return false
  110. *
  111. * @param mixed value
  112. * @return boolean check result
  113. */
  114. defined = RightJS.defined = function(value) {
  115. return typeof(value) !== 'undefined';
  116. },
  117. /**
  118. * checks if the given value is a function
  119. *
  120. * @param mixed value
  121. * @return boolean check result
  122. */
  123. isFunction = RightJS.isFunction = function(value) {
  124. return typeof(value) === 'function';
  125. },
  126. /**
  127. * checks if the given value is a string
  128. *
  129. * @param mixed value
  130. * @return boolean check result
  131. */
  132. isString = RightJS.isString = function(value) {
  133. return typeof(value) === 'string';
  134. },
  135. /**
  136. * checks if the given value is a number
  137. *
  138. * @param mixed value to check
  139. * @return boolean check result
  140. */
  141. isNumber = RightJS.isNumber = function(value) {
  142. return typeof(value) === 'number' && !isNaN(value);
  143. },
  144. /**
  145. * checks if the given value is a hash-like object
  146. *
  147. * @param mixed value
  148. * @return boolean check result
  149. */
  150. isHash = RightJS.isHash = function(value) {
  151. return to_s.call(value) === '[object Object]';
  152. },
  153. /**
  154. * checks if the given value is an array
  155. *
  156. * @param mixed value to check
  157. * @return boolean check result
  158. */
  159. isArray = RightJS.isArray = function(value) {
  160. return to_s.call(value) === '[object Array]';
  161. },
  162. /** !#server
  163. * checks if the given value is an element
  164. *
  165. * @param mixed value to check
  166. * @return boolean check result
  167. */
  168. isElement = RightJS.isElement = function(value) {
  169. return value != null && value.nodeType === 1;
  170. },
  171. /** !#server
  172. * checks if the given value is a DOM-node
  173. *
  174. * @param mixed value to check
  175. * @return boolean check result
  176. */
  177. isNode = RightJS.isNode = function(value) {
  178. return value != null && value.nodeType != null;
  179. },
  180. /** !#server
  181. * searches an element by id and/or extends it with the framework extentions
  182. *
  183. * @param String element id or Element to extend
  184. * @return Element or null
  185. */
  186. $ = RightJS.$ = function(object) {
  187. if (object instanceof Wrapper) {
  188. return object;
  189. } else if (typeof object === 'string') {
  190. object = document.getElementById(object);
  191. }
  192. return wrap(object);
  193. },
  194. /** !#server
  195. * Finds all the elements in the document by the given css_rule
  196. *
  197. * @param String element
  198. * @param Boolean raw search marker
  199. * @return Array search result
  200. */
  201. $$ = RightJS.$$ = function(css_rule, raw) {
  202. return current_Document.find(css_rule, raw);
  203. },
  204. /** !#server
  205. * shortcut to instance new elements
  206. *
  207. * @param String tag name
  208. * @param object options
  209. * @return Element instance
  210. */
  211. $E = RightJS.$E = function(tag_name, options) {
  212. return new Element(tag_name, options);
  213. },
  214. /**
  215. * shortcut, generates an array of words from a given string
  216. *
  217. * @param String string
  218. * @return Array of words
  219. */
  220. $w = RightJS.$w = function(string) {
  221. return string.trim().split(/\s+/);
  222. },
  223. /**
  224. * generates an unique id for an object
  225. *
  226. * @param Object object
  227. * @return Integer uniq id
  228. */
  229. $uid = RightJS.$uid = function(item) {
  230. return UID_KEY in item ? item[UID_KEY] : (item[UID_KEY] = UID++);
  231. },
  232. /**
  233. * converts any iterables into an array
  234. *
  235. * @param Object iterable
  236. * @return Array list
  237. */
  238. $A = RightJS.$A = function(it) {
  239. return slice.call(it, 0);
  240. };
  241. /** !#server
  242. * IE needs a patch for the $A function
  243. * because it doesn't handle all the cases
  244. */
  245. if (!A_proto.map) {
  246. $A = RightJS.$A = function(it) {
  247. try {
  248. return slice.call(it, 0);
  249. } catch(e) {
  250. for (var a=[], i=0, length = it.length; i < length; i++) {
  251. a[i] = it[i];
  252. }
  253. return a;
  254. }
  255. };
  256. }
  257. /** !#server
  258. * Internet Explorer needs some additional mumbo-jumbo in here
  259. */
  260. if (isHash(HTML)) {
  261. isHash = RightJS.isHash = function(value) {
  262. return to_s.call(value) === '[object Object]' &&
  263. value != null && value.hasOwnProperty != null;
  264. };
  265. }
  266. /**
  267. * Generating methods for native units extending
  268. */
  269. // adds a standard '.include' method to the native unit
  270. function extend_native(klass) {
  271. return $ext(klass, {
  272. Methods: {},
  273. include: function() {
  274. for (var i=0, l = arguments.length; i < l; i++) {
  275. if (isHash(arguments[i])) {
  276. $ext(klass.prototype, arguments[i]);
  277. $ext(klass.Methods, arguments[i]);
  278. }
  279. }
  280. }
  281. });
  282. }
  283. for (var i=0, natives = 'Array Function Number String Date RegExp'.split(' '); i < natives.length; i++) {
  284. RightJS[natives[i]] = extend_native(new Function('return '+ natives[i])());
  285. }
  286. // referring those two as well
  287. RightJS.Object = Object;
  288. RightJS.Math = Math;
  289. /**
  290. * Checks if the data is an array and if not,
  291. * then makes an array out of it
  292. *
  293. * @param mixed in data
  294. * @return Array data
  295. */
  296. function ensure_array(data) {
  297. return isArray(data) ? data : [data];
  298. }
  299. /**
  300. * The Object class extentions
  301. *
  302. * Credits:
  303. * Some functionality is inspired by
  304. * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
  305. *
  306. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  307. */
  308. $ext(Object, {
  309. /**
  310. * extracts the list of the attribute names of the given object
  311. *
  312. * @param Object object
  313. * @return Array keys list
  314. */
  315. keys: function(object) {
  316. var keys = [], key;
  317. for (key in object) {
  318. keys.push(key);
  319. }
  320. return keys;
  321. },
  322. /**
  323. * extracts the list of the attribute values of the given object
  324. *
  325. * @param Object object
  326. * @return Array values list
  327. */
  328. values: function(object) {
  329. var values = [], key;
  330. for (key in object) {
  331. values.push(object[key]);
  332. }
  333. return values;
  334. },
  335. /**
  336. * Calls the function with every key/value pair on the hash
  337. *
  338. * @param in Object the data hash
  339. * @param Function the callback
  340. * @param scope Object an optional scope
  341. * @return Object the original hash
  342. */
  343. each: function(object, callback, scope) {
  344. for (var key in object) {
  345. callback.call(scope, key, object[key]);
  346. }
  347. return object;
  348. },
  349. /**
  350. * checks if the object-hash has no keys
  351. *
  352. * @param Object object
  353. * @return check result
  354. */
  355. empty: function(object) {
  356. for (var key in object) { return false; }
  357. return true;
  358. },
  359. /**
  360. * A simple cloning method
  361. * NOTE: does not clone the things recoursively!
  362. *
  363. * @param Object object
  364. * @return Object clone
  365. */
  366. clone: function(object) {
  367. return Object.merge(object);
  368. },
  369. /**
  370. * returns a copy of the object which contains
  371. * all the same keys/values except the key-names
  372. * passed the the method arguments
  373. *
  374. * @param Object object
  375. * @param String key-name to exclude
  376. * .....
  377. * @return Object filtered copy
  378. */
  379. without: function() {
  380. var filter = $A(arguments), object = filter.shift(), copy = {}, key;
  381. for (key in object) {
  382. if (!filter.include(key)) {
  383. copy[key] = object[key];
  384. }
  385. }
  386. return copy;
  387. },
  388. /**
  389. * returns a copy of the object which contains all the
  390. * key/value pairs from the specified key-names list
  391. *
  392. * NOTE: if some key does not exists in the original object, it will be just skipped
  393. *
  394. * @param Object object
  395. * @param String key name to exclude
  396. * .....
  397. * @return Object filtered copy
  398. */
  399. only: function() {
  400. var filter = $A(arguments), object = filter.shift(), copy = {},
  401. i=0, length = filter.length;
  402. for (; i < length; i++) {
  403. if (filter[i] in object) {
  404. copy[filter[i]] = object[filter[i]];
  405. }
  406. }
  407. return copy;
  408. },
  409. /**
  410. * merges the given objects and returns the result
  411. *
  412. * NOTE this method _DO_NOT_ change the objects, it creates a new object
  413. * which conatins all the given ones.
  414. * if there is some keys introspections, the last object wins.
  415. * all non-object arguments will be omitted
  416. *
  417. * @param first Object object
  418. * @param second Object mixing
  419. * ......
  420. * @return Object merged object
  421. */
  422. merge: function() {
  423. var object = {}, i=0, args=arguments, l=args.length, key;
  424. for (; i < l; i++) {
  425. if (isHash(args[i])) {
  426. for (key in args[i]) {
  427. object[key] = isHash(args[i][key]) && !(args[i][key] instanceof Class) ?
  428. Object.merge(key in object ? object[key] : {}, args[i][key]) : args[i][key];
  429. }
  430. }
  431. }
  432. return object;
  433. },
  434. /**
  435. * converts a hash-object into an equivalent url query string
  436. *
  437. * @param Object object
  438. * @return String query
  439. */
  440. toQueryString: function(object) {
  441. var entries = to_query_string_map(object), i=0, result = [];
  442. for (; i < entries.length; i++) {
  443. result.push(encodeURIComponent(entries[i][0]) + "=" + encodeURIComponent(''+entries[i][1]));
  444. }
  445. return result.join('&');
  446. }
  447. }, true);
  448. // private
  449. /**
  450. * pre-converts nested objects into a flat key-value structure
  451. *
  452. * @param {Object} data-hash
  453. * @param {String} key-prefix
  454. * @return {Array} key-value pairs
  455. */
  456. function to_query_string_map(hash, prefix) {
  457. var result = [], key, value, i;
  458. for (key in hash) {
  459. value = hash[key];
  460. if (prefix) {
  461. key = prefix + "["+ key + "]";
  462. }
  463. if (typeof(value) === 'object') {
  464. if (isArray(value)) {
  465. if (!key.endsWith('[]')) {
  466. key += "[]";
  467. }
  468. for (i=0; i < value.length; i++) {
  469. result.push([key, value[i]]);
  470. }
  471. } else if (value) { // assuming it's an object
  472. value = to_query_string_map(value, key);
  473. for (i=0; i < value.length; i++) {
  474. result.push(value[i]);
  475. }
  476. }
  477. } else {
  478. result.push([key, value]);
  479. }
  480. }
  481. return result;
  482. }
  483. /**
  484. * here are the starndard Math object extends
  485. *
  486. * Credits:
  487. * The idea of random mehtod is taken from
  488. * - Ruby (http://www.ruby-lang.org) Copyright (C) Yukihiro Matsumoto
  489. *
  490. * Copyright (C) 2008-2010 Nikolay Nemshilov
  491. */
  492. var Math_old_random = Math.random;
  493. /**
  494. * the standard random method replacement, to make it more useful
  495. *
  496. * USE:
  497. * Math.random(); // original functionality, returns a float between 0 and 1
  498. * Math.random(10); // returns an integer between 0 and 10
  499. * Math.random(1,4); // returns an integer between 1 and 4
  500. *
  501. * @param min Integer minimum value if there's two arguments and maximum value if there's only one
  502. * @param max Integer maximum value
  503. * @return Float random between 0 and 1 if there's no arguments or an integer in the given range
  504. */
  505. Math.random = function(min, max) {
  506. if (arguments.length === 0) {
  507. return Math_old_random();
  508. } else if (arguments.length === 1) {
  509. max = min;
  510. min = 0;
  511. }
  512. return ~~(Math_old_random() * (max-min+1) + ~~min);
  513. };
  514. /**
  515. * The Array class extentions
  516. *
  517. * Credits:
  518. * Some of the functionality is inspired by
  519. * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
  520. * - Ruby (http://www.ruby-lang.org) Copyright (C) Yukihiro Matsumoto
  521. *
  522. * Copyright (C) 2008-2010 Nikolay Nemshilov
  523. */
  524. var original_sort = A_proto.sort,
  525. // JavaScript 1.6 methods recatching up or faking
  526. for_each = A_proto.forEach || function(callback, scope) {
  527. for (var i=0, l=this.length; i < l; i++) {
  528. callback.call(scope, this[i], i, this);
  529. }
  530. },
  531. filter = A_proto.filter || function(callback, scope) {
  532. for (var result=[], j=0, i=0, l=this.length; i < l; i++) {
  533. if (callback.call(scope, this[i], i, this)) {
  534. result[j++] = this[i];
  535. }
  536. }
  537. return result;
  538. },
  539. reject = function(callback, scope) {
  540. for (var result=[], j=0, i=0, l=this.length; i < l; i++) {
  541. if (!callback.call(scope, this[i], i, this)) {
  542. result[j++] = this[i];
  543. }
  544. }
  545. return result;
  546. },
  547. map = A_proto.map || function(callback, scope) {
  548. for (var result=[], i=0, l=this.length; i < l; i++) {
  549. result[i] = callback.call(scope, this[i], i, this);
  550. }
  551. return result;
  552. },
  553. some = A_proto.some || function(callback, scope) {
  554. for (var i=0, l=this.length; i < l; i++) {
  555. if (callback.call(scope, this[i], i, this)) {
  556. return true;
  557. }
  558. }
  559. return false;
  560. },
  561. every = A_proto.every || function(callback, scope) {
  562. for (var i=0, l=this.length; i < l; i++) {
  563. if (!callback.call(scope, this[i], i, this)) {
  564. return false;
  565. }
  566. }
  567. return true;
  568. },
  569. first = function(callback, scope) {
  570. for (var i=0, l=this.length; i < l; i++) {
  571. if (callback.call(scope, this[i], i, this)) {
  572. return this[i];
  573. }
  574. }
  575. return undefined;
  576. },
  577. last = function(callback, scope) {
  578. for (var i=this.length-1; i > -1; i--) {
  579. if (callback.call(scope, this[i], i, this)) {
  580. return this[i];
  581. }
  582. }
  583. return undefined;
  584. };
  585. //
  586. // RightJS callbacks magick preprocessing
  587. //
  588. // prepares a correct callback function
  589. function guess_callback(argsi, array) {
  590. var callback = argsi[0], args = slice.call(argsi, 1), scope = array, attr;
  591. if (typeof(callback) === 'string') {
  592. attr = callback;
  593. if (array.length !== 0 && typeof(array[0][attr]) === 'function') {
  594. callback = function(object) { return object[attr].apply(object, args); };
  595. } else {
  596. callback = function(object) { return object[attr]; };
  597. }
  598. } else {
  599. scope = args[0];
  600. }
  601. return [callback, scope];
  602. }
  603. // defining the manual break errors class
  604. function Break() {}
  605. // calls the given method with preprocessing the arguments
  606. function call_method(func, scope, args) {
  607. try {
  608. return func.apply(scope, guess_callback(args, scope));
  609. } catch(e) { if (!(e instanceof Break)) { throw(e); } }
  610. return undefined;
  611. }
  612. // checks the value as a boolean
  613. function boolean_check(i) {
  614. return !!i;
  615. }
  616. // default sorting callback
  617. function default_sort(a, b) {
  618. return a > b ? 1 : a < b ? -1 : 0;
  619. }
  620. Array.include({
  621. /**
  622. * IE fix
  623. * returns the index of the value in the array
  624. *
  625. * @param mixed value
  626. * @param Integer optional offset
  627. * @return Integer index or -1 if not found
  628. */
  629. indexOf: A_proto.indexOf || function(value, from) {
  630. for (var i=(from<0) ? Math.max(0, this.length+from) : from || 0, l=this.length; i < l; i++) {
  631. if (this[i] === value) {
  632. return i;
  633. }
  634. }
  635. return -1;
  636. },
  637. /**
  638. * IE fix
  639. * returns the last index of the value in the array
  640. *
  641. * @param mixed value
  642. * @return Integer index or -1 if not found
  643. */
  644. lastIndexOf: A_proto.lastIndexOf || function(value) {
  645. for (var i=this.length-1; i > -1; i--) {
  646. if (this[i] === value) {
  647. return i;
  648. }
  649. }
  650. return -1;
  651. },
  652. /**
  653. * returns the first element of the array
  654. *
  655. * @return mixed first element of the array
  656. */
  657. first: function() {
  658. return arguments.length ? call_method(first, this, arguments) : this[0];
  659. },
  660. /**
  661. * returns the last element of the array
  662. *
  663. * @return mixed last element of the array
  664. */
  665. last: function() {
  666. return arguments.length ? call_method(last, this, arguments) : this[this.length-1];
  667. },
  668. /**
  669. * returns a random item of the array
  670. *
  671. * @return mixed a random item
  672. */
  673. random: function() {
  674. return this.length === 0 ? undefined : this[Math.random(this.length-1)];
  675. },
  676. /**
  677. * returns the array size
  678. *
  679. * @return Integer the array size
  680. */
  681. size: function() {
  682. return this.length;
  683. },
  684. /**
  685. * cleans the array
  686. * @return Array this
  687. */
  688. clean: function() {
  689. this.length = 0;
  690. return this;
  691. },
  692. /**
  693. * checks if the array has no elements in it
  694. *
  695. * @return boolean check result
  696. */
  697. empty: function() {
  698. return this.length === 0;
  699. },
  700. /**
  701. * creates a copy of the given array
  702. *
  703. * @return Array copy of the array
  704. */
  705. clone: function() {
  706. return this.slice(0);
  707. },
  708. /**
  709. * calls the given callback function in the given scope for each element of the array
  710. *
  711. * @param Function callback
  712. * @param Object scope
  713. * @return Array this
  714. */
  715. each: function() {
  716. call_method(for_each, this, arguments);
  717. return this;
  718. },
  719. forEach: for_each,
  720. /**
  721. * creates a list of the array items converted in the given callback function
  722. *
  723. * @param Function callback
  724. * @param Object optional scope
  725. * @return Array collected
  726. */
  727. map: function() {
  728. return call_method(map, this, arguments);
  729. },
  730. /**
  731. * creates a list of the array items which are matched in the given callback function
  732. *
  733. * @param Function callback
  734. * @param Object optional scope
  735. * @return Array filtered copy
  736. */
  737. filter: function() {
  738. return call_method(filter, this, arguments);
  739. },
  740. /**
  741. * creates a list of the array items that are not matching the give callback function
  742. *
  743. * @param Function callback
  744. * @param Object optionl scope
  745. * @return Array filtered copy
  746. */
  747. reject: function() {
  748. return call_method(reject, this, arguments);
  749. },
  750. /**
  751. * checks if any of the array elements is logically true
  752. *
  753. * @param Function optional callback for checks
  754. * @param Object optional scope for the callback
  755. * @return boolean check result
  756. */
  757. some: function(value) {
  758. return call_method(some, this, value ? arguments : [boolean_check]);
  759. },
  760. /**
  761. * checks if all the array elements are logically true
  762. *
  763. * @param Function optional callback for checks
  764. * @param Object optional scope for the callback
  765. * @return Boolean check result
  766. */
  767. every: function(value) {
  768. return call_method(every, this, value ? arguments : [boolean_check]);
  769. },
  770. /**
  771. * applies the given lambda to each element in the array
  772. *
  773. * NOTE: changes the array by itself
  774. *
  775. * @param Function callback
  776. * @param Object optional scope
  777. * @return Array this
  778. */
  779. walk: function() {
  780. this.map.apply(this, arguments).forEach(function(value, i) { this[i] = value; }, this);
  781. return this;
  782. },
  783. /**
  784. * similar to the concat function but it adds only the values which are not on the list yet
  785. *
  786. * @param Array to merge
  787. * ....................
  788. * @return Array new merged
  789. */
  790. merge: function() {
  791. for (var copy = this.clone(), arg, i=0; i < arguments.length; i++) {
  792. arg = ensure_array(arguments[i]);
  793. for (var j=0; j < arg.length; j++) {
  794. if (copy.indexOf(arg[j]) == -1) {
  795. copy.push(arg[j]);
  796. }
  797. }
  798. }
  799. return copy;
  800. },
  801. /**
  802. * flats out complex array into a single dimension array
  803. *
  804. * @return Array flatten copy
  805. */
  806. flatten: function() {
  807. var copy = [];
  808. this.forEach(function(value) {
  809. if (isArray(value)) {
  810. copy = copy.concat(value.flatten());
  811. } else {
  812. copy.push(value);
  813. }
  814. });
  815. return copy;
  816. },
  817. /**
  818. * returns a copy of the array whithout any null or undefined values
  819. *
  820. * @return Array filtered version
  821. */
  822. compact: function() {
  823. return this.without(null, undefined);
  824. },
  825. /**
  826. * returns a copy of the array which contains only the unique values
  827. *
  828. * @return Array filtered copy
  829. */
  830. uniq: function() {
  831. return [].merge(this);
  832. },
  833. /**
  834. * checks if all of the given values
  835. * exists in the given array
  836. *
  837. * @param mixed value
  838. * ....
  839. * @return boolean check result
  840. */
  841. includes: function() {
  842. for (var i=0; i < arguments.length; i++) {
  843. if (this.indexOf(arguments[i]) === -1) {
  844. return false;
  845. }
  846. }
  847. return true;
  848. },
  849. /**
  850. * returns a copy of the array without the items passed as the arguments
  851. *
  852. * @param mixed value
  853. * ......
  854. * @return Array filtered copy
  855. */
  856. without: function() {
  857. var filter = slice.call(arguments);
  858. return this.filter(function(value) {
  859. return filter.indexOf(value) === -1;
  860. });
  861. },
  862. /**
  863. * Shuffles the array items in a random order
  864. *
  865. * @return Array shuffled version
  866. */
  867. shuffle: function() {
  868. var shuff = this.clone(), j, x, i = shuff.length;
  869. for (; i > 0; j = Math.random(i-1), x = shuff[--i], shuff[i] = shuff[j], shuff[j] = x) {}
  870. return shuff;
  871. },
  872. /**
  873. * Default sort fix for numeric values
  874. *
  875. * @param Function callback
  876. * @return Array self
  877. */
  878. sort: function(callback) {
  879. return original_sort.apply(this, (callback || !isNumber(this[0])) ? arguments : [default_sort]);
  880. },
  881. /**
  882. * sorts the array by running its items though a lambda or calling their attributes
  883. *
  884. * @param Function callback or attribute name
  885. * @param Object scope or attribute argument
  886. * @return Array sorted copy
  887. */
  888. sortBy: function() {
  889. var pair = guess_callback(arguments, this);
  890. return this.sort(function(a, b) {
  891. return default_sort(
  892. pair[0].call(pair[1], a),
  893. pair[0].call(pair[1], b)
  894. );
  895. });
  896. },
  897. /**
  898. * Returns the minimal value on the list
  899. *
  900. * @return Number minimal value
  901. */
  902. min: function() {
  903. return Math.min.apply(Math, this);
  904. },
  905. /**
  906. * Returns the maximal value
  907. *
  908. * @return Number maximal value
  909. */
  910. max: function() {
  911. return Math.max.apply(Math, this);
  912. },
  913. /**
  914. * Returns a summ of all the items on the list
  915. *
  916. * @return Number a summ of values on the list
  917. */
  918. sum: function() {
  919. for(var sum=0, i=0, l=this.length; i < l; sum += this[i++]) {}
  920. return sum;
  921. }
  922. });
  923. A_proto.include = A_proto.includes;
  924. /**
  925. * The String class extentions
  926. *
  927. * Credits:
  928. * Some of the functionality inspired by
  929. * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
  930. * The trim function taken from work of Steven Levithan
  931. * - http://blog.stevenlevithan.com/archives/faster-trim-javascript
  932. *
  933. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  934. */
  935. String.include({
  936. /**
  937. * checks if the string is an empty string
  938. *
  939. * @return boolean check result
  940. */
  941. empty: function() {
  942. return this == '';
  943. },
  944. /**
  945. * checks if the string contains only white-spaces
  946. *
  947. * @return boolean check result
  948. */
  949. blank: function() {
  950. return this == false;
  951. },
  952. /**
  953. * removes trailing whitespaces
  954. *
  955. * @return String trimmed version
  956. */
  957. trim: String.prototype.trim || function() {
  958. var str = this.replace(/^\s\s*/, ''), i = str.length;
  959. while ((/\s/).test(str.charAt(--i))) {}
  960. return str.slice(0, i + 1);
  961. },
  962. /**
  963. * returns a copy of the string with all the tags removed
  964. * @return String without tags
  965. */
  966. stripTags: function() {
  967. return this.replace(/<\/?[^>]+>/ig, '');
  968. },
  969. /**
  970. * removes all the scripts declarations out of the string
  971. * @param mixed option. If it equals true the scrips will be executed,
  972. * if a function the scripts will be passed in it
  973. * @return String without scripts
  974. */
  975. stripScripts: function(option) {
  976. var scripts = '', text = this.replace(
  977. /<script[^>]*>([\s\S]*?)<\/script>/img,
  978. function(match, source) {
  979. scripts += source + "\n";
  980. return '';
  981. }
  982. );
  983. if (option === true) {
  984. $eval(scripts);
  985. } else if (isFunction(option)) {
  986. option(scripts, text);
  987. }
  988. return text;
  989. },
  990. /**
  991. * extracts all the scripts out of the string
  992. *
  993. * @return String the extracted stcripts
  994. */
  995. extractScripts: function() {
  996. var scripts = '';
  997. this.stripScripts(function(s) { scripts = s; });
  998. return scripts;
  999. },
  1000. /**
  1001. * evals all the scripts in the string
  1002. *
  1003. * @return String self (unchanged version with scripts still in their place)
  1004. */
  1005. evalScripts: function() {
  1006. this.stripScripts(true);
  1007. return this;
  1008. },
  1009. /**
  1010. * converts underscored or dasherized string to a camelized one
  1011. * @returns String camelized version
  1012. */
  1013. camelize: function() {
  1014. return this.replace(/(\-|_)+(.)?/g, function(match, dash, chr) {
  1015. return chr ? chr.toUpperCase() : '';
  1016. });
  1017. },
  1018. /**
  1019. * converts a camelized or dasherized string into an underscored one
  1020. * @return String underscored version
  1021. */
  1022. underscored: function() {
  1023. return this.replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/\-/g, '_').toLowerCase();
  1024. },
  1025. /**
  1026. * returns a capitalised version of the string
  1027. *
  1028. * @return String captialised version
  1029. */
  1030. capitalize: function() {
  1031. return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  1032. },
  1033. /**
  1034. * Makes a dashed version of the string
  1035. *
  1036. * @return String dashed version
  1037. */
  1038. dasherize: function() {
  1039. return this.underscored().replace(/_/g, '-');
  1040. },
  1041. /**
  1042. * checks if the string contains the given substring
  1043. *
  1044. * @param String string
  1045. * @return boolean check result
  1046. */
  1047. includes: function(string) {
  1048. return this.indexOf(string) != -1;
  1049. },
  1050. /**
  1051. * checks if the string starts with the given substring
  1052. *
  1053. * @param String string
  1054. * @param boolean ignore the letters case
  1055. * @return boolean check result
  1056. */
  1057. startsWith: function(string, ignorecase) {
  1058. return (ignorecase !== true ? this.indexOf(string) :
  1059. this.toLowerCase().indexOf(string.toLowerCase())
  1060. ) === 0;
  1061. },
  1062. /**
  1063. * checks if the string ends with the given substring
  1064. *
  1065. * @param String substring
  1066. * @param boolean ignore the letters case
  1067. * @return boolean check result
  1068. */
  1069. endsWith: function(string, ignorecase) {
  1070. return this.length - (
  1071. ignorecase !== true ? this.lastIndexOf(string) :
  1072. this.toLowerCase().lastIndexOf(string.toLowerCase())
  1073. ) === string.length;
  1074. },
  1075. /**
  1076. * converts the string to an integer value
  1077. * @param Integer base
  1078. * @return Integer or NaN
  1079. */
  1080. toInt: function(base) {
  1081. return parseInt(this, base === undefined ? 10 : base);
  1082. },
  1083. /**
  1084. * converts the string to a float value
  1085. * @param boolean flat if the method should not use a flexible matching
  1086. * @return Float or NaN
  1087. */
  1088. toFloat: function(strict) {
  1089. return parseFloat(strict === true ? this :
  1090. this.replace(',', '.').replace(/(\d)-(\d)/, '$1.$2'));
  1091. }
  1092. });
  1093. String.prototype.include = String.prototype.includes;
  1094. /**
  1095. * The Function class extentions
  1096. *
  1097. * Credits:
  1098. * Some of the functionality inspired by
  1099. * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
  1100. *
  1101. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  1102. */
  1103. Function.include({
  1104. /**
  1105. * binds the function to be executed in the given scope
  1106. *
  1107. * @param Object scope
  1108. * @param mixed optional curry (left) argument
  1109. * ....
  1110. * @return Function binded function
  1111. */
  1112. bind: function() {
  1113. var args = $A(arguments), scope = args.shift(), func = this;
  1114. return function() {
  1115. return func.apply(scope,
  1116. (args.length !== 0 || arguments.length !== 0) ?
  1117. args.concat($A(arguments)) : args
  1118. );
  1119. };
  1120. },
  1121. /**
  1122. * binds the function as an event listener to the given scope object
  1123. *
  1124. * @param Object scope
  1125. * @param mixed optional curry (left) argument
  1126. * .......
  1127. * @return Function binded function
  1128. */
  1129. bindAsEventListener: function() {
  1130. var args = $A(arguments), scope = args.shift(), func = this;
  1131. return function(event) {
  1132. return func.apply(scope, [event].concat(args).concat($A(arguments)));
  1133. };
  1134. },
  1135. /**
  1136. * allows you to put some curry in your cookery
  1137. *
  1138. * @param mixed value to curry
  1139. * ....
  1140. * @return Function curried function
  1141. */
  1142. curry: function() {
  1143. return this.bind.apply(this, [this].concat($A(arguments)));
  1144. },
  1145. /**
  1146. * The right side curry feature
  1147. *
  1148. * @param mixed value to curry
  1149. * ....
  1150. * @return Function curried function
  1151. */
  1152. rcurry: function() {
  1153. var curry = $A(arguments), func = this;
  1154. return function() {
  1155. return func.apply(func, $A(arguments).concat(curry));
  1156. };
  1157. },
  1158. /**
  1159. * delays the function execution
  1160. *
  1161. * @param Integer delay ms
  1162. * @param mixed value to curry
  1163. * .....
  1164. * @return Integer timeout marker
  1165. */
  1166. delay: function() {
  1167. var args = $A(arguments), timeout = args.shift(),
  1168. timer = new Number(setTimeout(this.bind.apply(this, [this].concat(args)), timeout));
  1169. timer.cancel = function() { clearTimeout(this); };
  1170. return timer;
  1171. },
  1172. /**
  1173. * creates a periodical execution of the function with the given timeout
  1174. *
  1175. * @param Integer delay ms
  1176. * @param mixed value to curry
  1177. * ...
  1178. * @return Ineger interval marker
  1179. */
  1180. periodical: function() {
  1181. var args = $A(arguments), timeout = args.shift(),
  1182. timer = new Number(setInterval(this.bind.apply(this, [this].concat(args)), timeout));
  1183. timer.stop = function() { clearInterval(this); };
  1184. return timer;
  1185. },
  1186. /**
  1187. * Chains the given function after the current one
  1188. *
  1189. * @param Function the next function
  1190. * @param mixed optional value to curry
  1191. * ......
  1192. * @return Function chained function
  1193. */
  1194. chain: function() {
  1195. var args = $A(arguments), func = args.shift(), current = this;
  1196. return function() {
  1197. var result = current.apply(current, arguments);
  1198. func.apply(func, args);
  1199. return result;
  1200. };
  1201. }
  1202. });
  1203. /**
  1204. * The Number class extentions
  1205. *
  1206. * Credits:
  1207. * Some methods inspired by
  1208. * - Ruby (http://www.ruby-lang.org) Copyright (C) Yukihiro Matsumoto
  1209. *
  1210. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  1211. */
  1212. Number.include({
  1213. /**
  1214. * executes the given callback the given number of times
  1215. *
  1216. * @param Function callback
  1217. * @param Object optional callback execution scope
  1218. * @return void
  1219. */
  1220. times: function(callback, scope) {
  1221. for (var i=0; i < this; i++) {
  1222. callback.call(scope, i);
  1223. }
  1224. return this;
  1225. },
  1226. upto: function(number, callback, scope) {
  1227. for (var i=this+0; i <= number; i++) {
  1228. callback.call(scope, i);
  1229. }
  1230. return this;
  1231. },
  1232. downto: function(number, callback, scope) {
  1233. for (var i=this+0; i >= number; i--) {
  1234. callback.call(scope, i);
  1235. }
  1236. return this;
  1237. },
  1238. /**
  1239. * Maps a list of numbers from current to given
  1240. * or map a result of calls of the callback on those numbers
  1241. *
  1242. * @param {Number} end number
  1243. * @param {Function} optional callback
  1244. * @param {Object} optional callback scope
  1245. * @return {Array} the result list
  1246. */
  1247. to: function(number, callback, scope) {
  1248. var start = this + 0, end = number, result = [], i=start;
  1249. callback = callback || function(i) { return i; };
  1250. if (end > start) {
  1251. for (; i <= end; i++) {
  1252. result.push(callback.call(scope, i));
  1253. }
  1254. } else {
  1255. for (; i >= end; i--) {
  1256. result.push(callback.call(scope, i));
  1257. }
  1258. }
  1259. return result;
  1260. },
  1261. abs: function() {
  1262. return Math.abs(this);
  1263. },
  1264. round: function(size) {
  1265. return size ? parseFloat(this.toFixed(size)) : Math.round(this);
  1266. },
  1267. ceil: function() {
  1268. return Math.ceil(this);
  1269. },
  1270. floor: function() {
  1271. return Math.floor(this);
  1272. },
  1273. min: function(value) {
  1274. return this < value ? value : this + 0;
  1275. },
  1276. max: function(value) {
  1277. return this > value ? value : this + 0;
  1278. }
  1279. });
  1280. /**
  1281. * The Regexp class extentions
  1282. *
  1283. * Credits:
  1284. * Inspired by
  1285. * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
  1286. *
  1287. * Copyright (C) 2008-2010 Nikolay V. Nemshilov
  1288. */
  1289. /**
  1290. * Escapes the string for safely use as a regular expression
  1291. *
  1292. * @param String raw string
  1293. * @return String escaped string
  1294. */
  1295. RegExp.escape = function(string) {
  1296. return (''+string).replace(/([.*+?\^=!:${}()|\[\]\/\\])/g, '\\$1');
  1297. };
  1298. if (!window.JSON) {
  1299. window.JSON = (function() {
  1300. var
  1301. // see the original JSON decoder implementation for descriptions http://www.json.org/json2.js
  1302. cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
  1303. specials = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'},
  1304. quotables = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
  1305. // quotes the string
  1306. function quote(string) {
  1307. return string.replace(quotables, function(chr) {
  1308. return specials[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
  1309. });
  1310. }
  1311. // adds the leading zero symbol
  1312. function zerofy(num) {
  1313. return (num < 10 ? '0' : '')+num;
  1314. }
  1315. return {
  1316. stringify: function(value) {
  1317. switch(typeof(value)) {
  1318. case 'boolean': return String(value);
  1319. case 'number': return String(value+0);
  1320. case 'string': return '"'+ quote(value) + '"';
  1321. case 'object':
  1322. if (value === null) {
  1323. return 'null';
  1324. } else if (isArray(value)) {
  1325. return '['+$A(value).map(JSON.stringify).join(',')+']';
  1326. } else if (to_s.call(value) === '[object Date]') {
  1327. return '"' + value.getUTCFullYear() + '-' +
  1328. zerofy(value.getUTCMonth() + 1) + '-' +
  1329. zerofy(value.getUTCDate()) + 'T' +
  1330. zerofy(value.getUTCHours()) + ':' +
  1331. zerofy(value.getUTCMinutes()) + ':' +
  1332. zerofy(value.getUTCSeconds()) + '.' +
  1333. zerofy(value.getMilliseconds()) + 'Z' +
  1334. '"';
  1335. } else {
  1336. var result = [], key;
  1337. for (key in value) {
  1338. result.push('"'+key+'":'+JSON.stringify(value[key]));
  1339. }
  1340. return '{'+result.join(',')+'}';
  1341. }
  1342. }
  1343. },
  1344. parse: function(string) {
  1345. if (isString(string) && string) {
  1346. // getting back the UTF-8 symbols
  1347. string = string.replace(cx, function (a) {
  1348. return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
  1349. });
  1350. // checking the JSON string consistency
  1351. if (/^[\],:{}\s]*$/.test(string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
  1352. .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
  1353. .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
  1354. return new Function('return '+string)();
  1355. }
  1356. }
  1357. throw "JSON parse error: "+string;
  1358. }
  1359. };
  1360. })();
  1361. }
  1362. /**
  1363. * The basic Class unit
  1364. *
  1365. * Credits:
  1366. * The Class unit is inspired by its implementation in
  1367. * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
  1368. * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
  1369. * - Ruby (http://www.ruby-lang.org) Copyright (C) Yukihiro Matsumoto
  1370. *
  1371. * Copyright (C) 2008-2011 Nikolay Nemshilov
  1372. */
  1373. var Class = RightJS.Class = function() {
  1374. var args = $A(arguments).slice(0,2),
  1375. props = args.pop() || {},
  1376. parent = args.pop(),
  1377. klass = arguments[2], // you can send your own klass as the third argument
  1378. SKlass = function() {};
  1379. // if the parent class only was specified
  1380. if (!args.length && !isHash(props)) {
  1381. parent = props; props = {};
  1382. }
  1383. // !#server:begin
  1384. if (!klass && parent && (parent === Wrapper || parent.ancestors.include(Wrapper))) {
  1385. klass = Wrapper_makeKlass();
  1386. }
  1387. // !#server:end
  1388. // defining the basic klass function
  1389. klass = $ext(klass || function() {
  1390. Class_checkPrebind(this);
  1391. return 'initialize' in this ?
  1392. this.initialize.apply(this, arguments) :
  1393. this;
  1394. }, Class_Methods);
  1395. // handling the inheritance
  1396. parent = parent || Class;
  1397. SKlass.prototype = parent.prototype;
  1398. klass.prototype = new SKlass();
  1399. klass.parent = parent;
  1400. klass.prototype.constructor = klass;
  1401. // collecting the list of ancestors
  1402. klass.ancestors = [];
  1403. while (parent) {
  1404. klass.ancestors.push(parent);
  1405. parent = parent.parent;
  1406. }
  1407. // handling the module injections
  1408. ['extend', 'include'].each(function(name) {
  1409. if (name in props) {
  1410. klass[name].apply(klass, ensure_array(props[name]));
  1411. }
  1412. });
  1413. return klass.include(props);
  1414. },
  1415. /**
  1416. * Class utility methods
  1417. *
  1418. * Copyright (C) 2008-2011 Nikolay Nemshilov
  1419. */
  1420. Class_Methods = {
  1421. /**
  1422. * this method will extend the class-level with the given objects
  1423. *
  1424. * NOTE: this method _WILL_OVERWRITE_ the existing itercecting entries
  1425. *
  1426. * NOTE: this method _WILL_NOT_OVERWRITE_ the class prototype and
  1427. * the class 'name' and 'parent' attributes. If one of those
  1428. * exists in one of the received modeuls, the attribute will be
  1429. * skipped
  1430. *
  1431. * @param Object module to extend
  1432. * ....
  1433. * @return Class the klass
  1434. */
  1435. extend: function() {
  1436. $A(arguments).filter(isHash).each(function(module) {
  1437. $ext(this, Class_clean_module(module, true));
  1438. Class_handle_module_callbacks(this, module, true);
  1439. }, this);
  1440. return this;
  1441. },
  1442. /**
  1443. * extends the class prototype with the given objects
  1444. * NOTE: this method _WILL_OVERWRITE_ the existing itercecting entries
  1445. * NOTE: this method _WILL_NOT_OVERWRITE_ the 'klass' attribute of the klass.prototype
  1446. *
  1447. * @param Object module to include
  1448. * ....
  1449. * @return Class the klass
  1450. */
  1451. include: function() {
  1452. var klasses = [this].concat(this.ancestors);
  1453. $A(arguments).filter(isHash).each(function(module) {
  1454. Object.each(Class_clean_module(module, false), function(name, method) {
  1455. // searching for the super-method
  1456. for (var super_method, i=0, l = klasses.length; i < l; i++) {
  1457. if (name in klasses[i].prototype) {
  1458. super_method = klasses[i].prototype[name];
  1459. break;
  1460. }
  1461. }
  1462. this.prototype[name] = isFunction(method) && isFunction(super_method) ?
  1463. function() {
  1464. this.$super = super_method;
  1465. return method.apply(this, arguments);
  1466. } : method;
  1467. }, this);
  1468. Class_handle_module_callbacks(this, module, false);
  1469. }, this);
  1470. return this;
  1471. }
  1472. },
  1473. Class_module_callback_names = $w(
  1474. 'selfExtended self_extended selfIncluded self_included extend include'
  1475. );
  1476. // hooking up the class-methods to the root class
  1477. $ext(Class, Class_Methods);
  1478. Class.prototype.$super = undefined;
  1479. function Class_clean_module(module, extend) {
  1480. return Object.without.apply(Object, [module].concat(
  1481. Class_module_callback_names.concat( extend ?
  1482. $w('prototype parent ancestors') : ['constructor']
  1483. )
  1484. ));
  1485. }
  1486. function Class_handle_module_callbacks(klass, module, extend) {
  1487. (module[Class_module_callback_names[extend ? 0 : 2]] ||
  1488. module[Class_module_callback_names[extend ? 1 : 3]] ||
  1489. function() {}
  1490. ).call(module, klass);
  1491. }
  1492. /**
  1493. * This method gets through a list of the object its class and all the ancestors
  1494. * and finds a hash named after property, used for configuration purposes with
  1495. * the Observer and Options modules
  1496. *
  1497. * NOTE: this method will look for capitalized and uppercased versions of the
  1498. * property name
  1499. *
  1500. * @param Object a class instance
  1501. * @param String property name
  1502. * @return Object hash or null if nothing found
  1503. */
  1504. function Class_findSet(object, property) {
  1505. var upcased = property.toUpperCase(),
  1506. constructor = object.constructor,
  1507. candidates = [object, constructor].concat(constructor.ancestors || []),
  1508. i = 0;
  1509. for (l = candidates.length; i < l; i++) {
  1510. if (upcased in candidates[i]) {
  1511. return candidates[i][upcased];
  1512. } else if (property in candidates[i]) {
  1513. return candidates[i][property];
  1514. }
  1515. }
  1516. return null;
  1517. }
  1518. /**
  1519. * Handles the 'prebind' feature for Class instances
  1520. *
  1521. * @param Class instance
  1522. * @return void
  1523. */
  1524. function Class_checkPrebind(object) {
  1525. if ('prebind' in object && isArray(object.prebind)) {
  1526. object.prebind.each(function(method) {
  1527. object[method] = object[method].bind(object);
  1528. });
  1529. }
  1530. }
  1531. /**
  1532. * This is a simple mix-in module to be included in other classes
  1533. *
  1534. * Basically it privdes the <tt>setOptions</tt> method which processes
  1535. * an instance options assigment and merging with the default options
  1536. *
  1537. * Credits:
  1538. * The idea of the module is inspired by
  1539. * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
  1540. *
  1541. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  1542. */
  1543. var Options = RightJS.Options = {
  1544. /**
  1545. * assigns the options by merging them with the default ones
  1546. *
  1547. * @param Object options
  1548. * @return Object current instance
  1549. */
  1550. setOptions: function(opts) {
  1551. var options = this.options = $ext($ext({},
  1552. Object.clone(Class_findSet(this, 'Options'))), opts
  1553. ), match, key;
  1554. // hooking up the observer options
  1555. if (isFunction(this.on)) {
  1556. for (key in options) {
  1557. if ((match = key.match(/on([A-Z][A-Za-z]+)/))) {
  1558. this.on(match[1].toLowerCase(), options[key]);
  1559. delete(options[key]);
  1560. }
  1561. }
  1562. }
  1563. return this;
  1564. },
  1565. /**
  1566. * Cuts of an options hash from the end of the arguments list
  1567. * assigns them using the #setOptions method and then
  1568. * returns the list of other arguments as an Array instance
  1569. *
  1570. * @param mixed iterable
  1571. * @return Array of the arguments
  1572. */
  1573. cutOptions: function(in_args) {
  1574. var args = $A(in_args);
  1575. this.setOptions(isHash(args.last()) ? args.pop() : {});
  1576. return args;
  1577. }
  1578. };
  1579. /**
  1580. * standard Observer class.
  1581. *
  1582. * Might be used as a usual class or as a builder over another objects
  1583. *
  1584. * Credits:
  1585. * The naming principle is inspired by
  1586. * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
  1587. *
  1588. * Copyright (C) 2008-2011 Nikolay Nemshilov
  1589. */
  1590. var Observer = RightJS.Observer = new Class({
  1591. include: Options,
  1592. /**
  1593. * general constructor
  1594. *
  1595. * @param Object options
  1596. */
  1597. initialize: function(options) {
  1598. this.setOptions(options);
  1599. Observer_createShortcuts(this, Class_findSet(this, 'Events'));
  1600. return this;
  1601. },
  1602. /**
  1603. * binds an event listener
  1604. *
  1605. * USAGE:
  1606. * on(String event, Function callback[, arguments, ...]);
  1607. * on(String event, String method_name[, arguments, ...]);
  1608. * on(Object events_hash);
  1609. *
  1610. * @return Observer self
  1611. */
  1612. on: function() {
  1613. Observer_on(this, arguments, function(h) { return h; });
  1614. return this;
  1615. },
  1616. /**
  1617. * checks if the observer observes given event and/or callback
  1618. *
  1619. * USAGE:
  1620. * observes(String event)
  1621. * observes(Function callback)
  1622. * observes(String event, Function callback)
  1623. *
  1624. * @retun boolean check result
  1625. */
  1626. observes: function(event, callback) {
  1627. if (!isString(event)) { callback = event; event = null; }
  1628. if (isString(callback)) { callback = callback in this ? this[callback] : null; }
  1629. return (this.$listeners || []).some(function(i) {
  1630. return (event && callback) ? i.e === event && i.f === callback :
  1631. event ? i.e === event : i.f === callback;
  1632. });
  1633. },
  1634. /**
  1635. * stops observing an event or/and function
  1636. *
  1637. * USAGE:
  1638. * stopObserving(String event)
  1639. * stopObserving(Function callback)
  1640. * stopObserving(String event, Function callback)
  1641. *
  1642. * @return Observer self
  1643. */
  1644. stopObserving: function(event, callback) {
  1645. Observer_stopObserving(this, event, callback, function() {});
  1646. return this;
  1647. },
  1648. /**
  1649. * returns the listeners list for the event
  1650. *
  1651. * NOTE: if no event was specified the method will return _all_
  1652. * event listeners for _all_ the events
  1653. *
  1654. * @param String event name
  1655. * @return Array of listeners
  1656. */
  1657. listeners: function(event) {
  1658. return (this.$listeners || []).filter(function(i) {
  1659. return !event || i.e === event;
  1660. }).map(function(i) { return i.f; }).uniq();
  1661. },
  1662. /**
  1663. * initiates the event handling
  1664. *
  1665. * @param String event name
  1666. * @param mixed optional argument
  1667. * ........
  1668. * @return Observer self
  1669. */
  1670. fire: function() {
  1671. var args = $A(arguments), event = args.shift();
  1672. (this.$listeners || []).each(function(i) {
  1673. if (i.e === event) {
  1674. i.f.apply(this, i.a.concat(args));
  1675. }
  1676. }, this);
  1677. return this;
  1678. }
  1679. }),
  1680. /**
  1681. * adds an observer functionality to any object
  1682. *
  1683. * @param Object object
  1684. * @param Array optional events list to build shortcuts
  1685. * @return Object extended object
  1686. */
  1687. Observer_create = Observer.create = function(object, events) {
  1688. $ext(object, Object.without(Observer.prototype, 'initialize', 'setOptions'), true);
  1689. return Observer_createShortcuts(object, events || Class_findSet(object, 'Events'));
  1690. },
  1691. /**
  1692. * builds shortcut methods to wire/fire events on the object
  1693. *
  1694. * @param Object object to extend
  1695. * @param Array list of event names
  1696. * @return Object extended object
  1697. */
  1698. Observer_createShortcuts = Observer.createShortcuts = function(object, names) {
  1699. (names || []).each(function(name) {
  1700. var method_name = 'on'+name.replace(/(^|_|:)([a-z])/g,
  1701. function(match, pre, chr) { return chr.toUpperCase(); }
  1702. );
  1703. if (!(method_name in object)) {
  1704. object[method_name] = function() {
  1705. return this.on.apply(this, [name].concat($A(arguments)));
  1706. };
  1707. }
  1708. });
  1709. return object;
  1710. };
  1711. function Observer_on(object, o_args, preprocess) {
  1712. var args = slice.call(o_args, 2),
  1713. event = o_args[0],
  1714. callback = o_args[1],
  1715. name = false;
  1716. if (isString(event)) {
  1717. switch (typeof callback) {
  1718. case "string":
  1719. name = callback;
  1720. callback = callback in object ? object[callback] : function() {};
  1721. case "function":
  1722. ('$listeners' in object ? object.$listeners : (
  1723. object.$listeners = []
  1724. )).push(preprocess({
  1725. e: event, f: callback, a: args, r: name || false, t: object
  1726. }));
  1727. break;
  1728. default:
  1729. if (isArray(callback)) {
  1730. for (var i=0; i < callback.length; i++) {
  1731. object.on.apply(object, [event].concat(
  1732. ensure_array(callback[i])
  1733. ).concat(args));
  1734. }
  1735. }
  1736. }
  1737. } else {
  1738. // assuming it's a hash of key-value pairs
  1739. args = slice.call(o_args, 1);
  1740. for (name in event) {
  1741. object.on.apply(object, [name].concat(
  1742. ensure_array(event[name])
  1743. ).concat(args));
  1744. }
  1745. }
  1746. }
  1747. function Observer_stopObserving(object, event, callback, preprocess) {
  1748. if (isHash(event)) {
  1749. for (var key in event) {
  1750. object.stopObserving(key, event[key]);
  1751. }
  1752. } else {
  1753. if (!isString(event)) { callback = event; event = null; }
  1754. if (isString(callback)){ callback = object[callback]; }
  1755. object.$listeners = (object.$listeners || []).filter(function(i) {
  1756. var result = (event && callback) ?
  1757. (i.e !== event || i.f !== callback) :
  1758. (event ? i.e !== event : i.f !== callback);
  1759. if (!result) { preprocess(i); }
  1760. return result;
  1761. });
  1762. }
  1763. }
  1764. /**
  1765. * this object will contain info about the current browser
  1766. *
  1767. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  1768. */
  1769. var agent = navigator.userAgent,
  1770. Browser_Opera = 'opera' in window,
  1771. Browser_IE = 'attachEvent' in window && !Browser_Opera,
  1772. Browser = RightJS.Browser = {
  1773. IE: Browser_IE,
  1774. Opera: Browser_Opera,
  1775. WebKit: agent.include('AppleWebKit/'),
  1776. Gecko: agent.include('Gecko') && !agent.include('KHTML'),
  1777. MobileSafari: /Apple.*Mobile.*Safari/.test(agent),
  1778. Konqueror: agent.include('Konqueror'),
  1779. // internal marker for the browsers which require the olds module
  1780. OLD: !document.querySelector,
  1781. // internal marker for IE browsers version <= 8
  1782. IE8L: false
  1783. },
  1784. IE8_OR_LESS = false,
  1785. IE_OPACITY = !('opacity' in HTML.style) && ('filter' in HTML.style);
  1786. try {
  1787. // checking if that an IE version <= 8
  1788. document.createElement('<input/>');
  1789. Browser.OLD = Browser.IE8L = IE8_OR_LESS = true;
  1790. } catch(e) {}
  1791. /**
  1792. * The dom-wrapper main unit
  1793. *
  1794. * This unit is basically for the internal use
  1795. * so that we could control the common functionality
  1796. * among all the wrappers
  1797. *
  1798. * Copyright (C) 2010-2011 Nikolay Nemshilov
  1799. */
  1800. var Wrapper = RightJS.Wrapper = new Class({
  1801. // predefining the property in the prototype
  1802. _: undefined,
  1803. /**
  1804. * Default constructor
  1805. *
  1806. * @param mixed raw dom unit
  1807. * @return void
  1808. */
  1809. initialize: function(raw_object) {
  1810. this._ = raw_object;
  1811. }
  1812. });
  1813. // exposing the cache so it could be manupulated externally
  1814. Wrapper.Cache = Wrappers_Cache;
  1815. // instantiating the actual class object for a wrapper
  1816. function Wrapper_makeKlass() {
  1817. /**
  1818. * Default wrappers Klass function
  1819. *
  1820. * @param mixed the raw object
  1821. * @param Object options
  1822. * @return void
  1823. */
  1824. return function(object, options) {
  1825. Class_checkPrebind(this);
  1826. this.initialize.apply(this, arguments); // <- there might be a different number of args in a subclass
  1827. var item = this._, uid = UID_KEY in item ? item[UID_KEY] :
  1828. // NOTE we use positive indexes for dom-elements and negative for everything else
  1829. (item[UID_KEY] = (item.nodeType === 1 ? 1 : -1) * UID++);
  1830. Wrappers_Cache[uid] = this;
  1831. };
  1832. }
  1833. /**
  1834. * Element's own Klass function
  1835. * we need that because it does some dynamic typecasting mumbo jumbo
  1836. * plus we would like to optimize some stuff here and there
  1837. *
  1838. * @param raw dom element or the tag name
  1839. * @param Object options
  1840. * @return Element instance
  1841. */
  1842. function Element_Klass(element, options) {
  1843. Element_initialize(this, element, options);
  1844. var inst = this, raw = inst._, cast = Wrapper.Cast(raw),
  1845. uid = UID_KEY in raw ? raw[UID_KEY] : (raw[UID_KEY] = UID++);
  1846. if (cast !== undefined) {
  1847. inst = new cast(raw, options);
  1848. if ('$listeners' in this) {
  1849. inst.$listeners = this.$listeners;
  1850. }
  1851. }
  1852. Wrappers_Cache[uid] = inst;
  1853. return inst;
  1854. }
  1855. // searches for a suitable class for dynamic typecasting
  1856. Wrapper.Cast = function(unit) {
  1857. return unit.tagName in Element_wrappers ? Element_wrappers[unit.tagName] : undefined;
  1858. };
  1859. /**
  1860. * Event's own Klass function, we don't need to check
  1861. * nothing in here, don't need to hit the wrappers cache and so one
  1862. *
  1863. * @param raw dom-event or a string event-name
  1864. * @param bounding element or an object with options
  1865. * @return void
  1866. */
  1867. function Event_Klass(event, bound_element) {
  1868. if (typeof(event) === 'string') {
  1869. event = $ext({type: event}, bound_element);
  1870. this.stopped = event.bubbles === false;
  1871. if (isHash(bound_element)) {
  1872. $ext(this, bound_element);
  1873. }
  1874. }
  1875. this._ = event;
  1876. this.type = event.type;
  1877. this.which = event.which;
  1878. this.keyCode = event.keyCode;
  1879. this.target = wrap(
  1880. // Webkit throws events on textual nodes as well, gotta fix that
  1881. event.target != null && 'nodeType' in event.target && event.target.nodeType === 3 ?
  1882. event.target.parentNode : event.target
  1883. );
  1884. this.currentTarget = wrap(event.currentTarget);
  1885. this.relatedTarget = wrap(event.relatedTarget);
  1886. this.pageX = event.pageX;
  1887. this.pageY = event.pageY;
  1888. // making old IE attrs looks like w3c standards
  1889. if (IE8_OR_LESS && 'srcElement' in event) {
  1890. this.which = event.button === 2 ? 3 : event.button === 4 ? 2 : 1;
  1891. this.target = wrap(event.srcElement) || bound_element;
  1892. this.relatedTarget = this.target._ === event.fromElement ? wrap(event.toElement) : this.target;
  1893. this.currentTarget = bound_element;
  1894. var scrolls = this.target.win().scrolls();
  1895. this.pageX = event.clientX + scrolls.x;
  1896. this.pageY = event.clientY + scrolls.y;
  1897. }
  1898. }
  1899. /**
  1900. * Private quick wrapping function, unlike `$`
  1901. * it doesn't search by ID and handle double-wrapps
  1902. * just pure dom-wrapping functionality
  1903. *
  1904. * @param raw dom unit
  1905. * @return Wrapper dom-wrapper
  1906. */
  1907. function wrap(object) {
  1908. if (object != null) {
  1909. var wrapper = UID_KEY in object ? Wrappers_Cache[object[UID_KEY]] : undefined;
  1910. if (wrapper !== undefined) {
  1911. return wrapper;
  1912. } else if (object.nodeType === 1) {
  1913. return new Element(object);
  1914. } else if (object.nodeType === 9) {
  1915. return new Document(object);
  1916. } else if (object.window == object) {
  1917. return new Window(object);
  1918. } else if (isElement(object.target) || isElement(object.srcElement)) {
  1919. return new Event(object);
  1920. }
  1921. }
  1922. return object;
  1923. }
  1924. /**
  1925. * A simple document wrapper
  1926. *
  1927. * Copyright (C) 2010-2011 Nikolay Nemshilov
  1928. */
  1929. var Document = RightJS.Document = new Class(Wrapper, {
  1930. // returns the window reference
  1931. win: function() {
  1932. return wrap(this._.defaultView || this._.parentWindow);
  1933. }
  1934. }),
  1935. // a common local wrapped document reference
  1936. current_Document = wrap(document);
  1937. /**
  1938. * the window object extensions
  1939. *
  1940. * Copyright (C) 2008-2011 Nikolay Nemshilov
  1941. */
  1942. var Window = RightJS.Window = new Class(Wrapper, {
  1943. /**
  1944. * Selfreference to have a common interface with the rest of the wrappers
  1945. * in case of events handling
  1946. *
  1947. * @return Window
  1948. */
  1949. win: function() {
  1950. return this;
  1951. },
  1952. /**
  1953. * returns the inner-size of the window
  1954. *
  1955. * @return Object x: d+, y: d+
  1956. */
  1957. size: function() {
  1958. var win = this._, html = win.document.documentElement;
  1959. return win.innerWidth ? {x: win.innerWidth, y: win.innerHeight} :
  1960. {x: html.clientWidth, y: html.clientHeight};
  1961. },
  1962. /**
  1963. * returns the scrolls for the window
  1964. *
  1965. * @return Object x: d+, y: d+
  1966. */
  1967. scrolls: function() {
  1968. var win = this._, doc = win.document, body = doc.body, html = doc.documentElement;
  1969. return (win.pageXOffset || win.pageYOffset) ? {x: win.pageXOffset, y: win.pageYOffset} :
  1970. (body && (body.scrollLeft || body.scrollTop)) ? {x: body.scrollLeft, y: body.scrollTop} :
  1971. {x: html.scrollLeft, y: html.scrollTop};
  1972. },
  1973. /**
  1974. * overloading the native scrollTo method to support hashes and element references
  1975. *
  1976. * @param mixed number left position, a hash position, element or a string element id
  1977. * @param number top position
  1978. * @param Object fx options
  1979. * @return window self
  1980. */
  1981. scrollTo: function(left, top, fx_options) {
  1982. var left_pos = left, top_pos = top,
  1983. element = isNumber(left) ? null : $(left);
  1984. if(element instanceof Element) {
  1985. left = element.position();
  1986. }
  1987. if (isHash(left)) {
  1988. top_pos = left.y;
  1989. left_pos = left.x;
  1990. }
  1991. // checking if a smooth scroll was requested
  1992. if (isHash(fx_options = fx_options || top) && RightJS.Fx) {
  1993. new Fx.Scroll(this, fx_options).start({x: left_pos, y: top_pos});
  1994. } else {
  1995. this._.scrollTo(left_pos, top_pos);
  1996. }
  1997. return this;
  1998. }
  1999. });
  2000. /**
  2001. * represents some additional functionality for the Event class
  2002. *
  2003. * NOTE: there more additional functionality for the Event class in the rightjs-goods project
  2004. *
  2005. * Credits:
  2006. * The additional method names are inspired by
  2007. * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
  2008. *
  2009. * Copyright (C) 2008-2011 Nikolay Nemshilov
  2010. */
  2011. var Event = RightJS.Event = new Class(Wrapper, {
  2012. // predefining the keys to spped up the assignments
  2013. type: null,
  2014. which: null,
  2015. keyCode: null,
  2016. target: null,
  2017. currentTarget: null,
  2018. relatedTarget: null,
  2019. pageX: null,
  2020. pageY: null,
  2021. /**
  2022. * the class constructor
  2023. *
  2024. * @param raw dom-event
  2025. * @param HTMLElement the bound element
  2026. * @return void
  2027. */
  2028. initialize: Event_Klass, // the actual initialization happens in the Klass function
  2029. /**
  2030. * Stops the event bubbling process
  2031. *
  2032. * @return RightJS.Event this
  2033. */
  2034. stopPropagation: function() {
  2035. if (this._.stopPropagation) {
  2036. this._.stopPropagation();
  2037. } else {
  2038. this._.cancelBubble = true;
  2039. }
  2040. this.stopped = true;
  2041. return this;
  2042. },
  2043. /**
  2044. * Prevents the default browser action on the event
  2045. *
  2046. * @return RightJS.Event this
  2047. */
  2048. preventDefault: function() {
  2049. if (this._.preventDefault) {
  2050. this._.preventDefault();
  2051. } else {
  2052. this._.returnValue = false;
  2053. }
  2054. return this;
  2055. },
  2056. /**
  2057. * Fully stops the event
  2058. *
  2059. * @return RightJS.Event this
  2060. */
  2061. stop: function() {
  2062. return this.stopPropagation().preventDefault();
  2063. },
  2064. /**
  2065. * Returns the event position
  2066. *
  2067. * @return Object {x: ..., y: ...}
  2068. */
  2069. position: function() {
  2070. return {x: this.pageX, y: this.pageY};
  2071. },
  2072. /**
  2073. * Returns the event's offset relative to the target element
  2074. *
  2075. * @return Object {x: ..., y: ...} or null
  2076. */
  2077. offset: function() {
  2078. if(this.target instanceof Element) {
  2079. var element_position = this.target.position();
  2080. return {
  2081. x: this.pageX - element_position.x,
  2082. y: this.pageY - element_position.y
  2083. };
  2084. }
  2085. // triggered outside browser window (at toolbar etc.)
  2086. return null;
  2087. },
  2088. /**
  2089. * Finds the element between the event target
  2090. * and the boundary element that matches the
  2091. * css-rule
  2092. *
  2093. * @param String css-rule
  2094. * @return Element element or null
  2095. */
  2096. find: function(css_rule) {
  2097. if (this.target instanceof Wrapper && this.currentTarget instanceof Wrapper) {
  2098. var target = this.target._,
  2099. search = this.currentTarget.find(css_rule, true);
  2100. while (target) {
  2101. if (search.indexOf(target) !== -1) {
  2102. return wrap(target);
  2103. }
  2104. target = target.parentNode;
  2105. }
  2106. }
  2107. return undefined;
  2108. }
  2109. }, Event_Klass),
  2110. Event_delegation_shortcuts = [];
  2111. /**
  2112. * The DOM Element unit handling
  2113. *
  2114. * Copyright (C) 2008-2011 Nikolay Nemshilov
  2115. */
  2116. var Element = RightJS.Element = new Class(Wrapper, {
  2117. /**
  2118. * constructor
  2119. *
  2120. * NOTE: this constructor will dynamically typecast
  2121. * the wrappers depending on the element tag-name
  2122. *
  2123. * @param String element tag name or an HTMLElement instance
  2124. * @param Object options
  2125. * @return Element element
  2126. */
  2127. initialize: function(element, options) {
  2128. Element_initialize(this, element, options);
  2129. }
  2130. }, Element_Klass),
  2131. Element_wrappers = Element.Wrappers = {},
  2132. elements_cache = {},
  2133. /**
  2134. * bulds dom-elements
  2135. *
  2136. * @param String element tag name
  2137. * @param Object options
  2138. * @return HTMLElement
  2139. */
  2140. make_element = function (tag, options) {
  2141. return (tag in elements_cache ? elements_cache[tag] : (
  2142. elements_cache[tag] = document.createElement(tag)
  2143. )).cloneNode(false);
  2144. };
  2145. //
  2146. // IE 6,7,8 (not 9!) browsers have a bug with checkbox and radio input elements
  2147. // it doesn't place the 'checked' property correctly, plus there are some issues
  2148. // with clonned SELECT objects, so we are replaceing the elements maker in here
  2149. //
  2150. if (IE8_OR_LESS) {
  2151. make_element = function(tag, options) {
  2152. if (options !== undefined && (tag === 'input' || tag === 'button')) {
  2153. tag = '<'+ tag +' name="'+ options.name +
  2154. '" type="'+ options.type +'"'+
  2155. (options.checked ? ' checked' : '') + ' />';
  2156. delete(options.name);
  2157. delete(options.type);
  2158. }
  2159. return document.createElement(tag);
  2160. };
  2161. }
  2162. /**
  2163. * Basic element's constructor
  2164. *
  2165. * @param Element wrapper instance
  2166. * @param mixed raw dom element of a string tag name
  2167. * @param Object options
  2168. * @return void
  2169. */
  2170. function Element_initialize(inst, element, options) {
  2171. if (typeof element === 'string') {
  2172. inst._ = make_element(element, options);
  2173. if (options !== undefined) {
  2174. for (var key in options) {
  2175. switch (key) {
  2176. case 'id': inst._.id = options[key]; break;
  2177. case 'html': inst._.innerHTML = options[key]; break;
  2178. case 'class': inst._.className = options[key]; break;
  2179. case 'on': inst.on(options[key]); break;
  2180. default: inst.set(key, options[key]);
  2181. }
  2182. }
  2183. }
  2184. } else {
  2185. inst._ = element;
  2186. }
  2187. }
  2188. /**
  2189. * The DOM Element unit structures handling module
  2190. *
  2191. * NOTE: all the methods will process and return only the Element nodes
  2192. * all the textual nodes will be skipped
  2193. *
  2194. * NOTE: if a css-rule was specified then the result of the method
  2195. * will be filtered/adjusted depends on the rule
  2196. *
  2197. * Credits:
  2198. * The naming principle and most of the names are taken from
  2199. * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
  2200. * The insertions system implementation is inspired by
  2201. * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
  2202. *
  2203. * Copyright (C) 2008-2011 Nikolay Nemshilov
  2204. */
  2205. Element.include({
  2206. parent: function(css_rule) {
  2207. var parent = this._.parentNode, parent_type = parent && parent.nodeType;
  2208. return css_rule ? this.parents(css_rule)[0] :
  2209. (parent_type === 1 || parent_type === 9) ? // <- IE6 sometimes has a fragment node in there
  2210. wrap(parent) : null;
  2211. },
  2212. parents: function(css_rule) {
  2213. return recursively_collect(this, 'parentNode', css_rule);
  2214. },
  2215. children: function(css_rule) {
  2216. return this.find(css_rule).filter(function(element) {
  2217. return element._.parentNode === this._;
  2218. }, this);
  2219. },
  2220. siblings: function(css_rule) {
  2221. return this.prevSiblings(css_rule).reverse().concat(this.nextSiblings(css_rule));
  2222. },
  2223. nextSiblings: function(css_rule) {
  2224. return recursively_collect(this, 'nextSibling', css_rule);
  2225. },
  2226. prevSiblings: function(css_rule) {
  2227. return recursively_collect(this, 'previousSibling', css_rule);
  2228. },
  2229. next: function(css_rule) {
  2230. return !css_rule && this._.nextElementSibling !== undefined ?
  2231. wrap(this._.nextElementSibling) : this.nextSiblings(css_rule)[0];
  2232. },
  2233. prev: function(css_rule) {
  2234. return !css_rule && this._.previousElementSibling !== undefined ?
  2235. wrap(this._.previousElementSibling) : this.prevSiblings(css_rule)[0];
  2236. },
  2237. /**
  2238. * removes the elemnt out of this parent node
  2239. *
  2240. * @return Element self
  2241. */
  2242. remove: function() {
  2243. var element = this._, parent = element.parentNode;
  2244. if (parent) {
  2245. parent.removeChild(element);
  2246. }
  2247. return this;
  2248. },
  2249. /**
  2250. * handles the elements insertion functionality
  2251. *
  2252. * The content might be one of the following data
  2253. *
  2254. * o) an element instance
  2255. * o) a String (all the scripts will be parsed out and executed)
  2256. * o) a list of Elements
  2257. * o) a hash like {position: content}
  2258. *
  2259. * @param mixed data to insert
  2260. * @param String position to insert top/bottom/before/after/instead
  2261. * @return Element self
  2262. */
  2263. insert: function(content, position) {
  2264. var scripts = null, element = this._;
  2265. position = position === undefined ? 'bottom' : position;
  2266. if (typeof(content) !== 'object') {
  2267. scripts = content = (''+content);
  2268. } else if (content instanceof Element) {
  2269. content = content._;
  2270. }
  2271. Element_insertions[position](element,
  2272. content.nodeType === undefined ?
  2273. Element_createFragment(
  2274. (position === 'bottom' || position === 'top') ?
  2275. element : element.parentNode, content
  2276. ) : content
  2277. );
  2278. if (scripts !== null) { scripts.evalScripts(); }
  2279. return this;
  2280. },
  2281. /**
  2282. * Inserts the element inside the given one at the given position
  2283. *
  2284. * @param mixed destination element reference
  2285. * @param String optional position
  2286. * @return Element this
  2287. */
  2288. insertTo: function(element, position) {
  2289. $(element).insert(this, position);
  2290. return this;
  2291. },
  2292. /**
  2293. * A shortcut to uppend several units into the element
  2294. *
  2295. * @param mixed data
  2296. * ..................
  2297. * @return Element this
  2298. */
  2299. append: function(first) {
  2300. return this.insert(isString(first) ? $A(arguments).join('') : arguments);
  2301. },
  2302. /**
  2303. * updates the content of the element by the given content
  2304. *
  2305. * @param mixed content (a String, an Element or a list of elements)
  2306. * @return Element self
  2307. */
  2308. update: function(content) {
  2309. if (typeof(content) !== 'object') {
  2310. content = '' + content;
  2311. try {
  2312. this._.innerHTML = content;
  2313. } catch(e) {
  2314. return this.clean().insert(content);
  2315. }
  2316. content.evalScripts();
  2317. return this;
  2318. } else {
  2319. return this.clean().insert(content);
  2320. }
  2321. },
  2322. /**
  2323. * Works with the Element's innerHTML property
  2324. * This method works both ways! if a content is provided
  2325. * then it will be assigned, otherwise will return
  2326. * the innerHTML property
  2327. *
  2328. * @param String html content
  2329. * @return String html content or Element this
  2330. */
  2331. html: function(content) {
  2332. return content === undefined ? this._.innerHTML : this.update(content);
  2333. },
  2334. /**
  2335. * Works with the Element's innerHTML property as a text
  2336. * when set something, it will appear as is with everything quoted
  2337. * when get, will return a string without any tags in it
  2338. *
  2339. * @param String text content
  2340. * @return String text content or Element this
  2341. */
  2342. text: function(text) {
  2343. return text === undefined ? (
  2344. this._.textContent === undefined ? this._.innerText : this._.textContent
  2345. ) : this.update(this.doc()._.createTextNode(text));
  2346. },
  2347. /**
  2348. * replaces the current element by the given content
  2349. *
  2350. * @param mixed content (a String, an Element or a list of elements)
  2351. * @return Element self
  2352. */
  2353. replace: function(content) {
  2354. return this.insert(content, 'instead');
  2355. },
  2356. /**
  2357. * wraps the element with the given element
  2358. *
  2359. * @param Element wrapper
  2360. * @return Element self
  2361. */
  2362. wrap: function(wrapper) {
  2363. var element = this._, parent = element.parentNode;
  2364. if (parent) {
  2365. wrapper = $(wrapper)._;
  2366. parent.replaceChild(wrapper, element);
  2367. wrapper.appendChild(element);
  2368. }
  2369. return this;
  2370. },
  2371. /**
  2372. * removes all the child nodes out of the element
  2373. *
  2374. * @return Element self
  2375. */
  2376. clean: function() {
  2377. while (this._.firstChild) {
  2378. this._.removeChild(this._.firstChild);
  2379. }
  2380. return this;
  2381. },
  2382. /**
  2383. * checks if the element has no child nodes
  2384. *
  2385. * @return boolean check result
  2386. */
  2387. empty: function() {
  2388. return this.html().blank();
  2389. },
  2390. /**
  2391. * Creates a clean clone of the element without any events attached to it
  2392. *
  2393. * @return Element new clone
  2394. */
  2395. clone: function() {
  2396. return new Element(this._.cloneNode(true));
  2397. },
  2398. /**
  2399. * Returns an index of the element among the other child elements
  2400. *
  2401. * NOTE: doesn't count the textual nodes!
  2402. *
  2403. * @return Integer index
  2404. */
  2405. index: function() {
  2406. var node = this._,
  2407. sibling = node.parentNode.firstChild,
  2408. index = 0;
  2409. while (sibling !== node) {
  2410. if (sibling.nodeType === 1) { // counting elements only
  2411. index ++;
  2412. }
  2413. sibling = sibling.nextSibling;
  2414. }
  2415. return index;
  2416. }
  2417. });
  2418. /**
  2419. * Recursively collects the target element's related nodes
  2420. *
  2421. * @param Element context
  2422. * @param name String pointer attribute name
  2423. * @param rule String optional css-atom rule
  2424. * @return Array found elements
  2425. */
  2426. function recursively_collect(where, attr, css_rule) {
  2427. var node = where._, result = [], i=0, no_rule = !css_rule;
  2428. while ((node = node[attr])) {
  2429. if (node.nodeType === 1 && (no_rule || wrap(node).match(css_rule))) {
  2430. result[i++] = wrap(node);
  2431. }
  2432. }
  2433. return result;
  2434. }
  2435. // list of insertions handling functions
  2436. // NOTE: each of the methods will be called in the contects of the current element
  2437. var Element_insertions = {
  2438. bottom: function(target, content) {
  2439. target.appendChild(content);
  2440. },
  2441. top: function(target, content) {
  2442. if (target.firstChild !== null) {
  2443. target.insertBefore(content, target.firstChild);
  2444. } else {
  2445. target.appendChild(content);
  2446. }
  2447. },
  2448. after: function(target, content) {
  2449. var parent = target.parentNode, sibling = target.nextSibling;
  2450. if (sibling !== null) {
  2451. parent.insertBefore(content, sibling);
  2452. } else {
  2453. parent.appendChild(content);
  2454. }
  2455. },
  2456. before: function(target, content) {
  2457. target.parentNode.insertBefore(content, target);
  2458. },
  2459. instead: function(target, content) {
  2460. target.parentNode.replaceChild(content, target);
  2461. }
  2462. },
  2463. // the element insertion wrappers list
  2464. Element_wraps = {
  2465. TBODY: ['<TABLE>', '</TABLE>', 2],
  2466. TR: ['<TABLE><TBODY>', '</TBODY></TABLE>', 3],
  2467. TD: ['<TABLE><TBODY><TR>', '</TR></TBODY></TABLE>', 4],
  2468. COL: ['<TABLE><COLGROUP>', '</COLGROUP><TBODY></TBODY></TABLE>', 2],
  2469. LEGEND: ['<FIELDSET>', '</FIELDSET>', 2],
  2470. AREA: ['<map>', '</map>', 2],
  2471. OPTION: ['<SELECT>', '</SELECT>', 2]
  2472. };
  2473. $alias(Element_wraps, {
  2474. OPTGROUP: 'OPTION',
  2475. THEAD: 'TBODY',
  2476. TFOOT: 'TBODY',
  2477. TH: 'TD'
  2478. });
  2479. // converts any data into a html fragment unit
  2480. var fragment = document.createDocumentFragment(),
  2481. tmp_cont = document.createElement('DIV');
  2482. function Element_createFragment(context, content) {
  2483. if (typeof(content) === 'string') {
  2484. var tag = context.tagName,
  2485. tmp = tmp_cont,
  2486. wrap = tag in Element_wraps ? Element_wraps[tag] : ['', '', 1],
  2487. depth = wrap[2];
  2488. tmp.innerHTML = wrap[0] + '<'+ tag + '>' + content + '</'+ tag + '>' + wrap[1];
  2489. while (depth-- !== 0) {
  2490. tmp = tmp.firstChild;
  2491. }
  2492. content = tmp.childNodes;
  2493. while (content.length !== 0) {
  2494. fragment.appendChild(content[0]);
  2495. }
  2496. } else {
  2497. for (var i=0, length = content.length, node; i < length; i++) {
  2498. node = content[content.length === length ? i : 0];
  2499. fragment.appendChild(node instanceof Element ? node._ : node);
  2500. }
  2501. }
  2502. return fragment;
  2503. }
  2504. /**
  2505. * this module contains the element unit styles related methods
  2506. *
  2507. * Credits:
  2508. * Some of the functionality is inspired by
  2509. * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
  2510. * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
  2511. * - Dojo (www.dojotoolkit.org) Copyright (C) The Dojo Foundation
  2512. *
  2513. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  2514. */
  2515. Element.include({
  2516. /**
  2517. * assigns styles out of the hash to the element
  2518. *
  2519. * NOTE: the style keys might be camelized or dasherized, both cases should work
  2520. *
  2521. * @param Object styles list or String style name
  2522. * @param String style value in case of the first param a string style name
  2523. * @return Element self
  2524. */
  2525. setStyle: function(hash, value) {
  2526. var key, c_key, style = {}, element_style = this._.style;
  2527. if (value !== undefined) { style[hash] = value; hash = style; }
  2528. else if(isString(hash)) {
  2529. hash.split(';').each(function(option) {
  2530. var els = option.split(':').map('trim');
  2531. if (els[0] && els[1]) {
  2532. style[els[0]] = els[1];
  2533. }
  2534. });
  2535. hash = style;
  2536. }
  2537. for (key in hash) {
  2538. c_key = key.indexOf('-') < 0 ? key : key.camelize();
  2539. if (IE_OPACITY && key === 'opacity') {
  2540. element_style.filter = 'alpha(opacity='+ hash[key] * 100 +')';
  2541. } else if (key === 'float') {
  2542. c_key = Browser_IE ? 'styleFloat' : 'cssFloat';
  2543. }
  2544. element_style[c_key] = hash[key];
  2545. }
  2546. return this;
  2547. },
  2548. /**
  2549. * returns style of the element
  2550. *
  2551. * NOTE: will include the CSS level definitions
  2552. *
  2553. * @param String style key
  2554. * @return String style value or null if not set
  2555. */
  2556. getStyle: function(key) {
  2557. return clean_style(this._.style, key) || clean_style(this.computedStyles(), key);
  2558. },
  2559. /**
  2560. * returns the hash of computed styles for the element
  2561. *
  2562. * @return Object/CSSDefinition computed styles
  2563. */
  2564. computedStyles: HTML.currentStyle ? function() {
  2565. return this._.currentStyle || {};
  2566. } : HTML.runtimeStyle ? function() {
  2567. return this._.runtimeStyle || {};
  2568. } : function() {
  2569. return this._.ownerDocument.defaultView.getComputedStyle(this._, null);
  2570. },
  2571. /**
  2572. * checks if the element has the given class name
  2573. *
  2574. * @param String class name
  2575. * @return boolean check result
  2576. */
  2577. hasClass: function(name) {
  2578. return (' '+this._.className+' ').indexOf(' '+name+' ') != -1;
  2579. },
  2580. /**
  2581. * sets the whole class-name string for the element
  2582. *
  2583. * @param String class-name
  2584. * @return Element self
  2585. */
  2586. setClass: function(class_name) {
  2587. this._.className = class_name;
  2588. return this;
  2589. },
  2590. /**
  2591. * Returns the current class-name
  2592. *
  2593. * @return String class-name
  2594. */
  2595. getClass: function() {
  2596. return this._.className;
  2597. },
  2598. /**
  2599. * adds the given class name to the element
  2600. *
  2601. * @param String class name
  2602. * @return Element self
  2603. */
  2604. addClass: function(name) {
  2605. var testee = ' '+this._.className+' ';
  2606. if (testee.indexOf(' '+name+' ') == -1) {
  2607. this._.className += (testee === ' ' ? '' : ' ') + name;
  2608. }
  2609. return this;
  2610. },
  2611. /**
  2612. * removes the given class name
  2613. *
  2614. * @param String class name
  2615. * @return Element self
  2616. */
  2617. removeClass: function(name) {
  2618. this._.className = (' '+this._.className+' ').replace(' '+name+' ', ' ').trim();
  2619. return this;
  2620. },
  2621. /**
  2622. * toggles the given class name on the element
  2623. *
  2624. * @param String class name
  2625. * @return Element self
  2626. */
  2627. toggleClass: function(name) {
  2628. return this[this.hasClass(name) ? 'removeClass' : 'addClass'](name);
  2629. },
  2630. /**
  2631. * adds the given class-name to the element
  2632. * and removes it from all the element siblings
  2633. *
  2634. * @param String class name
  2635. * @return Element self
  2636. */
  2637. radioClass: function(name) {
  2638. this.siblings().each('removeClass', name);
  2639. return this.addClass(name);
  2640. }
  2641. });
  2642. /**
  2643. * cleans up a style value
  2644. *
  2645. * @param Object styles hash
  2646. * @param String style-key
  2647. * @return String clean style
  2648. */
  2649. function clean_style(style, key) {
  2650. key = key.camelize();
  2651. if (key === 'opacity') {
  2652. return IE_OPACITY ? (
  2653. (/opacity=(\d+)/i.exec(style.filter || '') ||
  2654. ['', '100'])[1].toInt() / 100
  2655. )+'' :style[key].replace(',', '.');
  2656. }
  2657. if (key === 'float') {
  2658. key = Browser_IE ? 'styleFloat' : 'cssFloat';
  2659. }
  2660. var value = style[key];
  2661. // Opera returns named colors with quotes
  2662. if (Browser_Opera && /color/i.test(key) && value) {
  2663. value = value.replace(/"/g, '');
  2664. }
  2665. return value;
  2666. }
  2667. /**
  2668. * Common DOM Element unit methods
  2669. *
  2670. * Credits:
  2671. * Most of the naming system in the module inspired by
  2672. * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
  2673. *
  2674. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  2675. */
  2676. Element.include({
  2677. /**
  2678. * sets the element attributes
  2679. *
  2680. * @param String attr name or Object attributes hash
  2681. * @param mixed attribute value
  2682. * @return Element self
  2683. */
  2684. set: function(hash, value) {
  2685. if (typeof(hash) === 'string') { var val = {}; val[hash] = value; hash = val; }
  2686. var key, element = this._;
  2687. for (key in hash) {
  2688. if (key === 'style') {
  2689. this.setStyle(hash[key]);
  2690. } else {
  2691. // some attributes are not available as properties
  2692. if (!(key in element)) {
  2693. element.setAttribute(key, ''+hash[key]);
  2694. }
  2695. if (key.substr(0,5) !== 'data-') {
  2696. element[key] = hash[key];
  2697. }
  2698. }
  2699. }
  2700. return this;
  2701. },
  2702. /**
  2703. * returns the attribute value for the name
  2704. *
  2705. * @param String attr name
  2706. * @return mixed value
  2707. */
  2708. get: function(name) {
  2709. var element = this._, value = element[name] || element.getAttribute(name);
  2710. return value === '' ? null : value;
  2711. },
  2712. /**
  2713. * checks if the element has that attribute
  2714. *
  2715. * @param String attr name
  2716. * @return Boolean check result
  2717. */
  2718. has: function(name) {
  2719. return this.get(name) !== null;
  2720. },
  2721. /**
  2722. * erases the given attribute of the element
  2723. *
  2724. * @param String attr name
  2725. * @return Element self
  2726. */
  2727. erase: function(name) {
  2728. this._.removeAttribute(name);
  2729. return this;
  2730. },
  2731. /**
  2732. * checks if the elemnt is hidden
  2733. *
  2734. * NOTE: will check css level computed styles too
  2735. *
  2736. * @return boolean check result
  2737. */
  2738. hidden: function() {
  2739. return this.getStyle('display') === 'none';
  2740. },
  2741. /**
  2742. * checks if the element is visible
  2743. *
  2744. * @return boolean check result
  2745. */
  2746. visible: function() {
  2747. return !this.hidden();
  2748. },
  2749. /**
  2750. * hides the element
  2751. *
  2752. * @param String optional effect name
  2753. * @param Object the optional effect options
  2754. * @return Element self
  2755. */
  2756. hide: function(effect, options) {
  2757. if (this.visible()) {
  2758. this._d = this.getStyle('display');
  2759. this._.style.display = 'none';
  2760. }
  2761. return this;
  2762. },
  2763. /**
  2764. * shows the element
  2765. *
  2766. * @return Element self
  2767. */
  2768. show: function() {
  2769. if (this.hidden()) {
  2770. var element = this._, value = this._d, dummy;
  2771. // trying to guess the default 'style.display' for this kind of elements
  2772. if (!value || value === 'none') {
  2773. dummy = $E(element.tagName).insertTo(HTML);
  2774. value = dummy.getStyle('display');
  2775. dummy.remove();
  2776. }
  2777. // failsafe in case the user been naughty
  2778. if (value === 'none') {
  2779. value = 'block';
  2780. }
  2781. element.style.display = value;
  2782. }
  2783. return this;
  2784. },
  2785. /**
  2786. * toggles the visibility state of the element
  2787. *
  2788. * @return Element self
  2789. */
  2790. toggle: function() {
  2791. return this[this.visible() ? 'hide' : 'show']();
  2792. },
  2793. /**
  2794. * shows the element and hides all the sibligns
  2795. *
  2796. * @param String optional effect name
  2797. * @param Object the optional effect options
  2798. * @return Element self
  2799. */
  2800. radio: function(effect, options) {
  2801. this.siblings().each('hide', effect, options);
  2802. return this.show();
  2803. },
  2804. /**
  2805. * Sets/gets the `data-smth` data attribute and
  2806. * automatically converts everything in/out JSON
  2807. *
  2808. * @param String key name
  2809. * @param mixed data or `undefined` to erase
  2810. * @return mixed Element self or extracted data
  2811. */
  2812. data: function(key, value) {
  2813. var name, result, match, attrs, attr, i;
  2814. if (isHash(key)) {
  2815. for (name in key) {
  2816. value = this.data(name, key[name]);
  2817. }
  2818. } else if (value === undefined) {
  2819. key = 'data-'+ (''+key).dasherize();
  2820. for (result = {}, match = false, attrs = this._.attributes, i=0; i < attrs.length; i++) {
  2821. value = attrs[i].value;
  2822. try { value = JSON.parse(value); } catch (e) {}
  2823. if (attrs[i].name === key) {
  2824. result = value;
  2825. match = true;
  2826. break;
  2827. } else if (attrs[i].name.indexOf(key) === 0) {
  2828. result[attrs[i].name.substring(key.length+1).camelize()] = value;
  2829. match = true;
  2830. }
  2831. }
  2832. value = match ? result : null;
  2833. } else {
  2834. key = 'data-'+ (''+key).dasherize();
  2835. if (!isHash(value)) { value = {'': value}; }
  2836. for (name in value) {
  2837. attr = name.blank() ? key : key+'-'+name.dasherize();
  2838. if (value[name] === null) {
  2839. this._.removeAttribute(attr);
  2840. } else {
  2841. this._.setAttribute(attr, isString(value[name]) ? value[name] : JSON.stringify(value[name]));
  2842. }
  2843. }
  2844. value = this;
  2845. }
  2846. return value;
  2847. }
  2848. });
  2849. /**
  2850. * this module contains the Element's part of functionality
  2851. * responsible for the dimensions and positions getting/setting
  2852. *
  2853. * Copyright (C) 2008-2011 Nikolay Nemshilov
  2854. */
  2855. Element.include({
  2856. /**
  2857. * Returns the reference to this element document
  2858. *
  2859. * @return RightJS.Document
  2860. */
  2861. doc: function() {
  2862. return wrap(this._.ownerDocument);
  2863. },
  2864. /**
  2865. * Returns the reference to this elements window
  2866. *
  2867. * @return RightJS.Window
  2868. */
  2869. win: function() {
  2870. return this.doc().win();
  2871. },
  2872. /**
  2873. * Returns the element size as a hash
  2874. *
  2875. * @return Object {x: NNN, y: NNN}
  2876. */
  2877. size: function() {
  2878. return { x: this._.offsetWidth, y: this._.offsetHeight };
  2879. },
  2880. /**
  2881. * Returns the element absolute position
  2882. *
  2883. * NOTE: see the konq.js file for the manual version of the method
  2884. *
  2885. * @return Object {x: NNN, y: NNN}
  2886. */
  2887. position: function() {
  2888. var rect = this._.getBoundingClientRect(),
  2889. html = this.doc()._.documentElement,
  2890. scrolls = this.win().scrolls();
  2891. return {
  2892. x: rect.left + scrolls.x - html.clientLeft,
  2893. y: rect.top + scrolls.y - html.clientTop
  2894. };
  2895. },
  2896. /**
  2897. * Returns the element scrolls
  2898. *
  2899. * @return Object {x: NNN, y: NNN}
  2900. */
  2901. scrolls: function() {
  2902. return { x: this._.scrollLeft, y: this._.scrollTop };
  2903. },
  2904. /**
  2905. * returns the element dimensions hash
  2906. *
  2907. * @return Object dimensions (top, left, width, height, scrollLeft, scrollTop)
  2908. */
  2909. dimensions: function() {
  2910. var size = this.size(),
  2911. scrolls = this.scrolls(),
  2912. position = this.position();
  2913. return {
  2914. top: position.y,
  2915. left: position.x,
  2916. width: size.x,
  2917. height: size.y,
  2918. scrollLeft: scrolls.x,
  2919. scrollTop: scrolls.y
  2920. };
  2921. },
  2922. /**
  2923. * Checks if the element overlaps the given position
  2924. *
  2925. * @param Object position {x: NNN, y: NNN}
  2926. * @return boolean check result
  2927. */
  2928. overlaps: function(target) {
  2929. var pos = this.position(), size = this.size();
  2930. return target.x > pos.x && target.x < (pos.x + size.x) &&
  2931. target.y > pos.y && target.y < (pos.y + size.y);
  2932. },
  2933. /**
  2934. * sets the width of the element in pixels
  2935. *
  2936. * NOTE: will double assign the size of the element, so it match the exact
  2937. * size including any possible borders and paddings
  2938. *
  2939. * @param Integer width in pixels
  2940. * @return Element self
  2941. */
  2942. setWidth: function(width_px) {
  2943. var style = this._.style;
  2944. style.width = width_px + 'px';
  2945. style.width = (2 * width_px - this._.offsetWidth) + 'px';
  2946. return this;
  2947. },
  2948. /**
  2949. * sets the width of the element in pixels
  2950. *
  2951. * NOTE: will double assign the size of the element, so it match the exact
  2952. * size including any possible borders and paddings
  2953. *
  2954. * @param Integer height in pixels
  2955. * @return Element self
  2956. */
  2957. setHeight: function(height_px) {
  2958. var style = this._.style;
  2959. style.height = height_px + 'px';
  2960. style.height = (2 * height_px - this._.offsetHeight) + 'px';
  2961. return this;
  2962. },
  2963. /**
  2964. * sets the size of the element in pixels
  2965. *
  2966. * NOTE: will double assign the size of the element, so it match the exact
  2967. * size including any possible borders and paddings
  2968. *
  2969. * @param width Integer width in pixels or {x: 10, y: 20} like object
  2970. * @param height Integer height
  2971. * @return Element self
  2972. */
  2973. resize: function(width, height) {
  2974. if (isHash(width)) {
  2975. height = width.y;
  2976. width = width.x;
  2977. }
  2978. return this.setWidth(width).setHeight(height);
  2979. },
  2980. /**
  2981. * sets the element position (against the window corner)
  2982. *
  2983. * @param left Number left position in pixels or an object like {x: 10, y: 20}
  2984. * @param top Number top position in pixels
  2985. * @return Element self
  2986. */
  2987. moveTo: function(left, top) {
  2988. if (isHash(left)) {
  2989. top = left.y;
  2990. left = left.x;
  2991. }
  2992. return this.setStyle({
  2993. left: left + 'px',
  2994. top: top + 'px'
  2995. });
  2996. },
  2997. /**
  2998. * sets the scroll position
  2999. *
  3000. * @param left Integer left scroll px or an object like {x: 22, y: 33}
  3001. * @param top Integer top scroll px
  3002. * @return Element self
  3003. */
  3004. scrollTo: function(left, top) {
  3005. if (isHash(left)) {
  3006. top = left.y;
  3007. left = left.x;
  3008. }
  3009. this._.scrollLeft = left;
  3010. this._.scrollTop = top;
  3011. return this;
  3012. },
  3013. /**
  3014. * makes the window be scrolled to the element
  3015. *
  3016. * @param Object fx options
  3017. * @return Element self
  3018. */
  3019. scrollThere: function(options) {
  3020. this.win().scrollTo(this, options);
  3021. return this;
  3022. }
  3023. });
  3024. /**
  3025. * DOM Element events handling methods
  3026. *
  3027. * Copyright (C) 2008-2011 Nikolay Nemshilov
  3028. */
  3029. [Element, Document, Window].each('include', $ext(Observer_create({}), {
  3030. /**
  3031. * The basic events handling attachment method
  3032. * SEE Observer#on for more details about supported arguments
  3033. *
  3034. * @returnt this
  3035. */
  3036. on: function() {
  3037. Observer_on(this, arguments, function(hash) {
  3038. if (hash.e === 'mouseenter' || hash.e === 'mouseleave') {
  3039. mouse_io_activate();
  3040. hash.n = hash.e;
  3041. hash.w = function() {};
  3042. // NOTE: we don't attach this listener to the actual element!
  3043. // so it didn't screw with IE's native enter/leave handlers
  3044. } else {
  3045. if (hash.e === 'contextmenu' && Browser.Konqueror) {
  3046. hash.n = 'rightclick';
  3047. } else if (hash.e === 'mousewheel' && Browser.Gecko) {
  3048. hash.n = 'DOMMouseScroll';
  3049. } else {
  3050. hash.n = hash.e;
  3051. }
  3052. hash.w = function(event) {
  3053. event = new Event(event, hash.t);
  3054. if (hash.f.apply(hash.t, (hash.r?[]:[event]).concat(hash.a)) === false) {
  3055. event.stop();
  3056. }
  3057. };
  3058. if (IE8_OR_LESS) {
  3059. hash.t._.attachEvent('on'+hash.n, hash.w);
  3060. } else {
  3061. hash.t._.addEventListener(hash.n, hash.w, false);
  3062. }
  3063. }
  3064. return hash;
  3065. });
  3066. return this;
  3067. },
  3068. /**
  3069. * Stops an event handling
  3070. *
  3071. * @param String event name or a function callback
  3072. * @param function callback or nothing
  3073. * @return this
  3074. */
  3075. stopObserving: function(event, callback) {
  3076. Observer_stopObserving(this, event, callback, function(hash) {
  3077. if (IE8_OR_LESS) {
  3078. hash.t._.detachEvent('on'+ hash.n, hash.w);
  3079. } else {
  3080. hash.t._.removeEventListener(hash.n, hash.w, false);
  3081. }
  3082. });
  3083. return this;
  3084. },
  3085. /**
  3086. * Artificially trigers the event on the element
  3087. *
  3088. * @param string event name or an Event instance
  3089. * @param Object options
  3090. * @return this
  3091. */
  3092. fire: function(event, options) {
  3093. var parent = this.parent && this.parent();
  3094. if (!(event instanceof Event)) {
  3095. event = new Event(event, $ext({target: this._}, options));
  3096. }
  3097. // setting up the currentTarget reference
  3098. event.currentTarget = this;
  3099. (this.$listeners || []).each(function(hash) {
  3100. if (hash.e === event.type &&
  3101. hash.f.apply(this, (hash.r?[]:[event]).concat(hash.a)) === false
  3102. ) {
  3103. event.stop();
  3104. }
  3105. }, this);
  3106. // manually bypassing the event to the parent one if it should bubble
  3107. if (parent && parent.fire && !event.stopped) {
  3108. parent.fire(event);
  3109. }
  3110. return this;
  3111. },
  3112. /**
  3113. * a simple events terminator method to be hooked like this.onClick('stopEvent');
  3114. *
  3115. * @return false
  3116. */
  3117. stopEvent: function() { return false; }
  3118. }));
  3119. // couple more shortcuts for the window
  3120. Observer_createShortcuts(Window.prototype, $w('blur focus scroll resize load'));
  3121. /**
  3122. * Registers a list of event-binding shortcuts like
  3123. * $(element).onClick
  3124. * $(element).onMouseover
  3125. *
  3126. * @param String space separated event names
  3127. * @return void
  3128. */
  3129. function Element_add_event_shortcuts(tokens) {
  3130. tokens = $w(tokens);
  3131. Event_delegation_shortcuts = Event_delegation_shortcuts.concat(tokens);
  3132. Observer_createShortcuts(Element.prototype, tokens);
  3133. Observer_createShortcuts(Document.prototype, tokens);
  3134. }
  3135. Element_add_event_shortcuts(
  3136. 'click rightclick contextmenu mousedown mouseup '+
  3137. 'mouseover mouseout mousemove keypress keydown keyup'
  3138. );
  3139. /**
  3140. * The DOM elements selection handling
  3141. *
  3142. * NOTE: this module is just a wrap over the native CSS-selectors feature
  3143. * see the olds/css.js file for the manual selector code
  3144. *
  3145. * Copyright (C) 2008-2011 Nikolay Nemshilov
  3146. */
  3147. [Element, Document].each('include', {
  3148. /**
  3149. * Extracts the first element matching the css-rule,
  3150. * or just any first element if no css-rule was specified
  3151. *
  3152. * @param String css-rule
  3153. * @return Element matching node or null
  3154. */
  3155. first: function(css_rule) {
  3156. return wrap(
  3157. css_rule === undefined && this._.firstElementChild !== undefined ?
  3158. this._.firstElementChild : this._.querySelector(css_rule || '*')
  3159. );
  3160. },
  3161. /**
  3162. * Finds a list of matching nodes, or all the descendant nodes if no css-rule provided
  3163. *
  3164. * @param String css-rule
  3165. * @param boolean raw-search
  3166. * @return Array of elements
  3167. */
  3168. find: function(css_rule, raw) {
  3169. var query = this._.querySelectorAll(css_rule || '*'), result, i=0, l = query.length;
  3170. if (raw === true) {
  3171. result = $A(query);
  3172. } else {
  3173. for (result = []; i < l; i++) {
  3174. result[i] = wrap(query[i]);
  3175. }
  3176. }
  3177. return result;
  3178. },
  3179. /**
  3180. * checks if the element matches this css-rule
  3181. *
  3182. * NOTE: the element should be attached to the page
  3183. *
  3184. * @param String css-rule
  3185. * @return Boolean check result
  3186. */
  3187. match: function(css_rule) {
  3188. // finding the top parent element (the element might not be on the document)
  3189. var element = this._, parent = element, result, faking = false;
  3190. while (parent.parentNode !== null && parent.parentNode.nodeType !== 11) {
  3191. parent = parent.parentNode;
  3192. }
  3193. // creating a fake context when needed
  3194. if (element === parent) {
  3195. parent = document.createElement('div');
  3196. parent.appendChild(element);
  3197. faking = true;
  3198. }
  3199. result = wrap(parent).find(css_rule, true).indexOf(element) !== -1;
  3200. if (faking) {
  3201. parent.removeChild(element);
  3202. }
  3203. return result;
  3204. }
  3205. });
  3206. /**
  3207. * The dom-ready event handling code
  3208. *
  3209. * Credits:
  3210. * The basic principles of the module are originated from
  3211. * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
  3212. *
  3213. * Copyright (C) 2009-2011 Nikolay Nemshilov
  3214. */
  3215. Document.include({
  3216. on: function(name) {
  3217. if (name === 'ready' && !this._iR) {
  3218. var document = this._, ready = this.fire.bind(this, 'ready');
  3219. // IE and Konqueror browsers
  3220. if ('readyState' in document) {
  3221. (function() {
  3222. if (['loaded','complete'].include(document.readyState)) {
  3223. ready();
  3224. } else {
  3225. arguments.callee.delay(50);
  3226. }
  3227. })();
  3228. } else {
  3229. document.addEventListener('DOMContentLoaded', ready, false);
  3230. }
  3231. this._iR = true;
  3232. }
  3233. return this.$super.apply(this, arguments);
  3234. }
  3235. });
  3236. Observer_createShortcuts(Document.prototype, ['ready']);
  3237. /**
  3238. * The form unit class and extensions
  3239. *
  3240. * Credits:
  3241. * The basic principles of the module are inspired by
  3242. * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
  3243. *
  3244. * Copyright (C) 2009-2011 Nikolay Nemshilov
  3245. */
  3246. var Form = RightJS.Form = Element_wrappers.FORM = new Class(Element, {
  3247. /**
  3248. * constructor
  3249. *
  3250. * NOTE: this constructor can be called as a normal Element constructor
  3251. * or with the options only, which will make a FORM element
  3252. *
  3253. * var form = new Form(raw_form_object_element);
  3254. * var form = new Form({method: 'post', action: '/boo/hoo'});
  3255. *
  3256. * @param Object options or HTMLFormElement object
  3257. * @return void
  3258. */
  3259. initialize: function(in_options) {
  3260. var options = in_options || {}, remote = 'remote' in options, element = options;
  3261. if (isHash(options) && !isElement(options)) {
  3262. element = 'form';
  3263. options = Object.without(options, 'remote');
  3264. }
  3265. this.$super(element, options);
  3266. if (remote) {
  3267. this.remotize();
  3268. }
  3269. },
  3270. /**
  3271. * returns the form elements as an array of extended units
  3272. *
  3273. * @return Array of elements
  3274. */
  3275. elements: function() {
  3276. return this.find('input,button,select,textarea');
  3277. },
  3278. /**
  3279. * returns the list of all the input elements on the form
  3280. *
  3281. * @return Array of elements
  3282. */
  3283. inputs: function() {
  3284. return this.elements().filter(function(input) {
  3285. return !['submit', 'button', 'reset', 'image', null].include(input._.type);
  3286. });
  3287. },
  3288. /**
  3289. * Accessing an input by name
  3290. *
  3291. * @param String name
  3292. * @return Input field
  3293. */
  3294. input: function(name) {
  3295. var input = this._[name];
  3296. if ('tagName' in input) {
  3297. input = wrap(input);
  3298. } else { // a list of radio-buttons (coz they have all the same name)
  3299. input = $A(input).map(wrap);
  3300. }
  3301. return input;
  3302. },
  3303. /**
  3304. * focuses on the first input element on the form
  3305. *
  3306. * @return Form this
  3307. */
  3308. focus: function() {
  3309. var element = this.inputs().first(function(input) {
  3310. return input._.type !== 'hidden';
  3311. });
  3312. if (element) { element.focus(); }
  3313. return this;
  3314. },
  3315. /**
  3316. * removes focus out of all the form elements
  3317. *
  3318. * @return Form this
  3319. */
  3320. blur: function() {
  3321. this.elements().each('blur');
  3322. return this;
  3323. },
  3324. /**
  3325. * disables all the elements on the form
  3326. *
  3327. * @return Form this
  3328. */
  3329. disable: function() {
  3330. this.elements().each('disable');
  3331. return this;
  3332. },
  3333. /**
  3334. * enables all the elements on the form
  3335. *
  3336. * @return Form this
  3337. */
  3338. enable: function() {
  3339. this.elements().each('enable');
  3340. return this;
  3341. },
  3342. /**
  3343. * returns the list of the form values
  3344. *
  3345. * @return Object values
  3346. */
  3347. values: function() {
  3348. var values = {};
  3349. this.inputs().each(function (element) {
  3350. var input = element._,
  3351. hash = values, key,
  3352. keys = input.name.match(/[^\[]+/g);
  3353. if (!input.disabled && input.name && (!(input.type === 'checkbox' || input.type === 'radio') || input.checked)) {
  3354. // getting throught the smth[smth][smth][] in the name
  3355. while (keys.length > 1) {
  3356. key = keys.shift();
  3357. if (key.endsWith(']')) {
  3358. key = key.substr(0, key.length-1);
  3359. }
  3360. if (!hash[key]) {
  3361. hash[key] = keys[0] === ']' ? [] : {};
  3362. }
  3363. hash = hash[key];
  3364. }
  3365. key = keys.shift();
  3366. if (key.endsWith(']')) {
  3367. key = key.substr(0, key.length-1);
  3368. }
  3369. if (key === '') { // an array
  3370. hash.push(element.value());
  3371. } else {
  3372. hash[key] = element.value();
  3373. }
  3374. }
  3375. });
  3376. return values;
  3377. },
  3378. /**
  3379. * returns the key/values organized ready to be sent via a get request
  3380. *
  3381. * @return String serialized values
  3382. */
  3383. serialize: function() {
  3384. return Object.toQueryString(this.values());
  3385. },
  3386. /**
  3387. * Delegating the submit method
  3388. *
  3389. * @return Form this
  3390. */
  3391. submit: function() {
  3392. this._.submit();
  3393. return this;
  3394. },
  3395. /**
  3396. * Delegating the 'reset' method
  3397. *
  3398. * @return Form this
  3399. */
  3400. reset: function() {
  3401. this._.reset();
  3402. return this;
  3403. }
  3404. });
  3405. // creating the event shortcuts
  3406. Element_add_event_shortcuts('submit reset focus blur disable enable change');
  3407. /**
  3408. * The form input element class
  3409. *
  3410. * Copyright (C) 2010-2011 Nikolay Nemshilov
  3411. */
  3412. var Input = RightJS.Input =
  3413. // retgistering the typecasted wrappers
  3414. Element_wrappers.INPUT =
  3415. Element_wrappers.BUTTON =
  3416. Element_wrappers.SELECT =
  3417. Element_wrappers.TEXTAREA =
  3418. Element_wrappers.OPTGROUP =
  3419. new Class(Element, {
  3420. /**
  3421. * Constructor
  3422. *
  3423. * NOTE: this constructor can be called in several ways
  3424. *
  3425. * Like normal Element
  3426. * var input = new Input('texarea', {...});
  3427. * var input = new Input(document.createElement('select'));
  3428. *
  3429. * Or with options only which will make an INPUT element by default
  3430. * var input = new Input({type: 'password', name: 'password'});
  3431. *
  3432. * @param HTMLElement or a String tag name or Options for default 'input' tag
  3433. * @param Object options
  3434. * @return void
  3435. */
  3436. initialize: function(element, options) {
  3437. // type to tag name conversion
  3438. if (!element || (isHash(element) && !isElement(element))) {
  3439. options = element || {};
  3440. if (/textarea|select/.test(options.type || '')) {
  3441. element = options.type;
  3442. delete(options.type);
  3443. } else {
  3444. element = 'input';
  3445. }
  3446. }
  3447. this.$super(element, options);
  3448. },
  3449. /**
  3450. * Returns a reference to the input's form
  3451. *
  3452. * @return Form wrapped form
  3453. */
  3454. form: function() {
  3455. return wrap(this._.form);
  3456. },
  3457. /**
  3458. * Overloading the method to fix some issues with IE and FF
  3459. *
  3460. * @param mixed content
  3461. * @param string optional position
  3462. * @return Input this
  3463. */
  3464. insert: function(content, position) {
  3465. this.$super(content, position);
  3466. // manually resetting the selected option in here
  3467. this.find('option').each(function(option) {
  3468. option._.selected = !!option.get('selected');
  3469. });
  3470. return this;
  3471. },
  3472. /**
  3473. * Overloading the method so it always called the '#insert' method
  3474. *
  3475. * @param mixed content
  3476. * @return Input this
  3477. */
  3478. update: function(content) {
  3479. return this.clean().insert(content);
  3480. },
  3481. /**
  3482. * uniform access to the element values
  3483. *
  3484. * @return String element value
  3485. */
  3486. getValue: function() {
  3487. if (this._.type == 'select-multiple') {
  3488. return this.find('option').map(function(option) {
  3489. return option._.selected ? option._.value : null;
  3490. }).compact();
  3491. } else {
  3492. return this._.value;
  3493. }
  3494. },
  3495. /**
  3496. * uniform accesss to set the element value
  3497. *
  3498. * @param String value
  3499. * @return Element this
  3500. */
  3501. setValue: function(value) {
  3502. if (this._.type == 'select-multiple') {
  3503. value = ensure_array(value).map(String);
  3504. this.find('option').each(function(option) {
  3505. option._.selected = value.include(option._.value);
  3506. });
  3507. } else {
  3508. this._.value = value;
  3509. }
  3510. return this;
  3511. },
  3512. /**
  3513. * Both ways getter/setter for the value parameter
  3514. *
  3515. * @param mixed value
  3516. * @return mixed this or the value
  3517. */
  3518. value: function(value) {
  3519. return this[value === undefined ? 'getValue' : 'setValue'](value);
  3520. },
  3521. /**
  3522. * focuses on the first input element on the form
  3523. *
  3524. * @return Form this
  3525. */
  3526. focus: function() {
  3527. this._.focus();
  3528. this.focused = true;
  3529. if (Browser_IE) { this.fire('focus', {bubbles: false}); }
  3530. return this;
  3531. },
  3532. /**
  3533. * removes focus out of all the form elements
  3534. *
  3535. * @return Form this
  3536. */
  3537. blur: function() {
  3538. this._.blur();
  3539. this.focused = false;
  3540. if (Browser_IE) { this.fire('blur', {bubbles: false}); }
  3541. return this;
  3542. },
  3543. /**
  3544. * focuses on the element and selects its content
  3545. *
  3546. * @return Element this
  3547. */
  3548. select: function() {
  3549. this._.select();
  3550. return this.focus();
  3551. },
  3552. /**
  3553. * disables all the elements on the form
  3554. *
  3555. * @return Form this
  3556. */
  3557. disable: function() {
  3558. this._.disabled = true;
  3559. return this.fire('disable');
  3560. },
  3561. /**
  3562. * enables all the elements on the form
  3563. *
  3564. * @return Form this
  3565. */
  3566. enable: function() {
  3567. this._.disabled = false;
  3568. return this.fire('enable');
  3569. },
  3570. /**
  3571. * A bidirectional method to set/get the disabled status of the input field
  3572. *
  3573. * @param boolean optional value
  3574. * @return Input in setter mode boolean in getter
  3575. */
  3576. disabled: function(value) {
  3577. return value === undefined ? this._.disabled : this[value ? 'disable' : 'enable']();
  3578. },
  3579. /**
  3580. * A bidirectional method to set/get the checked status of the input field
  3581. *
  3582. * @param boolean optional value
  3583. * @return Input in setter mode boolean in getter
  3584. */
  3585. checked: function(value) {
  3586. if (value === undefined) {
  3587. value = this._.checked;
  3588. } else {
  3589. this._.checked = value;
  3590. value = this;
  3591. }
  3592. return value;
  3593. }
  3594. });
  3595. /**
  3596. * This module provides correct focus/blur events bubbling
  3597. *
  3598. * Copyright (C) 2010-2011 Nikolay Nemshilov
  3599. */
  3600. /**
  3601. * Triggers a manual focus/blur events bubbling
  3602. *
  3603. * @param raw dom-event
  3604. * @return void
  3605. */
  3606. function focus_boobler(raw_event) {
  3607. var event = new Event(raw_event),
  3608. target = event.target,
  3609. parent = target.parent && target.parent();
  3610. event.type = raw_event.type === 'focusin' || raw_event.type === 'focus' ? 'focus' : 'blur';
  3611. if (parent) { parent.fire(event); }
  3612. }
  3613. /**
  3614. * Hooking up the 'focus' and 'blur' events
  3615. * at the document level and then rebooble them
  3616. * manually like they were normal events
  3617. *
  3618. */
  3619. if (IE8_OR_LESS) {
  3620. document.attachEvent('onfocusin', focus_boobler);
  3621. document.attachEvent('onfocusout', focus_boobler);
  3622. } else {
  3623. document.addEventListener('focus', focus_boobler, true);
  3624. document.addEventListener('blur', focus_boobler, true);
  3625. }
  3626. /**
  3627. * Provides the mouse enter/leave events handling emulation
  3628. *
  3629. * Copyright (C) 2010-2011 Nikolay Nemshilov
  3630. */
  3631. var mouse_io_index = [], mouse_io_inactive = true;
  3632. /**
  3633. * Fires the actual mouseenter/mouseleave event
  3634. *
  3635. * @param original event
  3636. * @param raw dom element
  3637. * @param integer uid
  3638. * @param boolean mouseenter or mouseleave
  3639. * @return void
  3640. */
  3641. function mouse_io_fire(raw, element, uid, enter) {
  3642. var event = new Event(raw);
  3643. event.type = enter === true ? 'mouseenter' : 'mouseleave';
  3644. event.bubbles = false;
  3645. event.stopped = true;
  3646. event.target = wrap(element);
  3647. // replacing the #find method so that UJS didn't
  3648. // get broke with trying to find nested elements
  3649. event.find = function(css_rule) {
  3650. return $$(css_rule, true)
  3651. .indexOf(this.target._) === -1 ?
  3652. undefined : this.target;
  3653. };
  3654. event.target.fire(event);
  3655. current_Document.fire(event);
  3656. }
  3657. /**
  3658. * Figures out the enter/leave events by listening the
  3659. * mouseovers in the document
  3660. *
  3661. * @param raw dom event
  3662. * @return void
  3663. */
  3664. function mouse_io_handler(e) {
  3665. var target = e.target || e.srcElement,
  3666. from = e.relatedTarget || e.fromElement,
  3667. element = target,
  3668. passed = false,
  3669. parents = [],
  3670. uid, event;
  3671. while (element.nodeType === 1) {
  3672. uid = $uid(element);
  3673. if (mouse_io_index[uid] === undefined) {
  3674. mouse_io_fire(e, element, uid,
  3675. mouse_io_index[uid] = true
  3676. );
  3677. }
  3678. if (element === from) {
  3679. passed = true;
  3680. }
  3681. parents.push(element);
  3682. element = element.parentNode;
  3683. }
  3684. if (from && !passed) {
  3685. while (from !== null && from.nodeType === 1 && parents.indexOf(from) === -1) {
  3686. uid = $uid(from);
  3687. if (mouse_io_index[uid] !== undefined) {
  3688. mouse_io_fire(e, from, uid,
  3689. mouse_io_index[uid] = undefined
  3690. );
  3691. }
  3692. from = from.parentNode;
  3693. }
  3694. }
  3695. }
  3696. /**
  3697. * Calling 'mouseleave' for all currently active elements on the page
  3698. *
  3699. * @return void
  3700. */
  3701. function mouse_io_reset(e) {
  3702. mouse_io_index.each(function(value, uid) {
  3703. if (value && Wrappers_Cache[uid]) {
  3704. mouse_io_fire(e, Wrappers_Cache[uid]._, uid, false);
  3705. }
  3706. });
  3707. }
  3708. /**
  3709. * Activating the mouse-io events emulation
  3710. *
  3711. * @return void
  3712. */
  3713. function mouse_io_activate() {
  3714. if (mouse_io_inactive) {
  3715. mouse_io_inactive = false;
  3716. if (Browser_IE) {
  3717. document.attachEvent('onmouseover', mouse_io_handler);
  3718. window.attachEvent('blur', mouse_io_reset);
  3719. } else {
  3720. document.addEventListener('mouseover', mouse_io_handler, false);
  3721. window.addEventListener('blur', mouse_io_reset, false);
  3722. }
  3723. }
  3724. }
  3725. Element_add_event_shortcuts('mouseenter mouseleave');
  3726. /**
  3727. * This module the standard events delegation interface
  3728. *
  3729. * Copyright (C) 2010-2011 Nikolay Nemshilov
  3730. */
  3731. [Element, Document].each('include', {
  3732. /**
  3733. * Attaches a delegative event listener to the element/document
  3734. *
  3735. * USAGE:
  3736. * $(element).delegate('click', '#css.rule', function() {...});
  3737. * $(element).delegate('click', '#css.rule', [func1, func2, ...]);
  3738. * $(element).delegate('click', '#css.rule', 'addClass', 'boo');
  3739. * $(element).delegate('click', '#css.rule', 'hide');
  3740. *
  3741. * $(element).delegate('click', {
  3742. * '#css.rule1': function() {},
  3743. * '#css.rule2': [func1, func2, ...],
  3744. * '#css.rule3': ['addClass', 'boo'],
  3745. * '#css.rule4': 'hide'
  3746. * });
  3747. *
  3748. * @param event name
  3749. * @param css-rule a hash or rules
  3750. * @param callback
  3751. * @return this
  3752. */
  3753. delegate: function(event) {
  3754. var rules = delegation_rules(arguments), css_rule, i, j, list;
  3755. for (css_rule in rules) {
  3756. for (i=0, list = rules[css_rule]; i < list.length; i++) {
  3757. // registering the delegative listener
  3758. this.on(event, build_delegative_listener(css_rule, list[i], this));
  3759. // adding the css-rule and callback references to the store
  3760. $ext(this.$listeners.last(), { dr: css_rule, dc: list[i][0] });
  3761. }
  3762. }
  3763. return this;
  3764. },
  3765. /**
  3766. * Removes a delegative event listener from the element
  3767. *
  3768. * USAGE:
  3769. * $(element).undelegate('click');
  3770. * $(element).undelegate('click', '#css.rule');
  3771. * $(element).undelegate('click', '#css.rule', function() {});
  3772. * $(element).undelegate('click', '#css.rule', [func1, func2, ...]);
  3773. * $(element).undelegate('click', '#css.rule', 'addClass', 'boo');
  3774. * $(element).undelegate('click', '#css.rule', 'hide');
  3775. *
  3776. * $(element).undelegate('click', {
  3777. * '#css.rule1': function() {},
  3778. * '#css.rule2': [func1, func2, ...],
  3779. * '#css.rule3': ['addClass', 'boo'],
  3780. * '#css.rule4': 'hide'
  3781. * });
  3782. *
  3783. * @param event name
  3784. * @param css-rule or a hash or rules
  3785. * @param callback
  3786. * @return this
  3787. */
  3788. undelegate: function(event) {
  3789. delegation_listeners(arguments, this).each(function(h) {
  3790. this.stopObserving(h.n, h.f);
  3791. }, this);
  3792. return this;
  3793. },
  3794. /**
  3795. * Checks if there is sucha delegative event listener
  3796. *
  3797. * USAGE:
  3798. * $(element).delegates('click');
  3799. * $(element).delegates('click', '#css.rule');
  3800. * $(element).delegates('click', '#css.rule', function() {});
  3801. * $(element).delegates('click', '#css.rule', [func1, func2, ...]);
  3802. * $(element).delegates('click', '#css.rule', 'addClass', 'boo');
  3803. * $(element).delegates('click', '#css.rule', 'hide');
  3804. *
  3805. * $(element).delegates('click', {
  3806. * '#css.rule1': function() {},
  3807. * '#css.rule2': [func1, func2, ...],
  3808. * '#css.rule3': ['addClass', 'boo'],
  3809. * '#css.rule4': 'hide'
  3810. * });
  3811. *
  3812. * NOTE:
  3813. * if several rules are specified then it will check if
  3814. * _any_ of them are delegateed
  3815. *
  3816. * @param event name
  3817. * @param css-rule or a hash of rules
  3818. * @param callback
  3819. * @return boolean check result
  3820. */
  3821. delegates: function() {
  3822. return !!delegation_listeners(arguments, this).length;
  3823. }
  3824. });
  3825. /**
  3826. * Builds the actual event listener that will delegate stuff
  3827. * to other elements as they reach the element where the listener
  3828. * attached
  3829. *
  3830. * @param String css rule
  3831. * @param Arguments the original arguments list
  3832. * @param Object scope
  3833. * @return Function the actual event listener
  3834. */
  3835. function build_delegative_listener(css_rule, entry, scope) {
  3836. var args = $A(entry), callback = args.shift();
  3837. return function(event) {
  3838. var target = event.find(css_rule);
  3839. return target === undefined ? target :
  3840. typeof(callback) === 'string' ?
  3841. target[callback].apply(target, args) :
  3842. callback.apply(target, [event].concat(args));
  3843. };
  3844. }
  3845. /**
  3846. * Converts the events-delegation api arguments
  3847. * into a systematic hash of rules
  3848. *
  3849. * @param Arguments arguments
  3850. * @return Object hash of rules
  3851. */
  3852. function delegation_rules(raw_args) {
  3853. var args = $A(raw_args), rules = args[1] || {}, hash = {}, css_rule;
  3854. if (isString(rules)) {
  3855. hash[rules] = args.slice(2);
  3856. if (isArray(hash[rules][0])) {
  3857. hash[rules] = hash[rules][0].map(ensure_array);
  3858. }
  3859. } else {
  3860. hash = rules;
  3861. }
  3862. // converting everything into a hash of lists of callbacks
  3863. for (css_rule in hash) {
  3864. hash[css_rule] = ensure_array(hash[css_rule]);
  3865. hash[css_rule] = isArray(hash[css_rule][0]) ? hash[css_rule] : [hash[css_rule]];
  3866. }
  3867. return hash;
  3868. }
  3869. /**
  3870. * Returns the list of delegative listeners that match the conditions
  3871. *
  3872. * @param Arguments raw-arguments
  3873. * @param Element the element
  3874. * @return Array list of matching listeners
  3875. */
  3876. function delegation_listeners(args, object) {
  3877. var event = args[0], i, list,
  3878. rules = delegation_rules(args),
  3879. rules_are_empty = !Object.keys(rules).length;
  3880. return (object.$listeners || []).filter(function(hash) {
  3881. return hash.dr && hash.n === event && (
  3882. rules_are_empty || (function() {
  3883. for (var css_rule in rules) {
  3884. if (hash.dr === css_rule) {
  3885. for (i=0, list = rules[css_rule]; i < list.length; i++) {
  3886. if (!list[i].length || list[i][0] === hash.dc) {
  3887. return true;
  3888. }
  3889. }
  3890. }
  3891. }
  3892. return false;
  3893. })()
  3894. );
  3895. });
  3896. }
  3897. /**
  3898. * Some String level shortcuts to handle collections of elements
  3899. *
  3900. * Copyright (C) 2011 Nikolay Nemshilov
  3901. */
  3902. /**
  3903. * Some nice shortcuts for the document-level events delegation handling
  3904. *
  3905. * USAGE:
  3906. *
  3907. * "ul#main-menu li".on("click", function() { alert('clicked'); });
  3908. * "ul#main-menu li".on("mouseover", "addClass", "hovered");
  3909. * "ul#main-menu li".on("mouseout", "removeClass", "hovered");
  3910. *
  3911. * // or like that in a shash
  3912. * "ul#main-menu li".on({
  3913. * click: function() { alert('clicked'); },
  3914. * mouseover: ['addClass', 'hovered'],
  3915. * mouseout: ['removeClass', 'hovered'],
  3916. * dblclick: 'hide'
  3917. * });
  3918. *
  3919. *
  3920. * "#css.rule".observes('click');
  3921. * "#css.rule".observes('click', function() {});
  3922. * "#css.rule".observes('click', 'method_name');
  3923. * ....
  3924. *
  3925. * "#css.rule".stopObserving('click');
  3926. * "#css.rule".stopObserving('click', function() {});
  3927. * "#css.rule".stopObserving('click', 'method_name');
  3928. * ....
  3929. */
  3930. Object.each({
  3931. on: 'delegate',
  3932. stopObserving: 'undelegate',
  3933. observes: 'delegates'
  3934. }, function(name, method) {
  3935. String.prototype[name] = function() {
  3936. var args = $A(arguments), result;
  3937. args.splice(1,0,''+this);
  3938. result = current_Document[method].apply(current_Document, args);
  3939. return result === current_Document ? this : result;
  3940. };
  3941. });
  3942. var old_on = String.prototype.on;
  3943. String.prototype.on = function(hash) {
  3944. if (isHash(hash)) {
  3945. for (var key in hash) {
  3946. old_on.apply(this, [key].concat([hash[key]]));
  3947. }
  3948. } else {
  3949. old_on.apply(this, arguments);
  3950. }
  3951. return this;
  3952. };
  3953. /**
  3954. * building the list of String#onEvent shortucts
  3955. *
  3956. * USAGE:
  3957. *
  3958. * "#css.rule".onClick(function() {...});
  3959. * "#css.rule".onMouseover('method_name');
  3960. */
  3961. Event_delegation_shortcuts.each(function(name) {
  3962. String.prototype['on'+name.capitalize()] = function() {
  3963. return this.on.apply(this, [name].concat($A(arguments)));
  3964. };
  3965. });
  3966. /**
  3967. * The rest of the DOM methods access
  3968. *
  3969. * USAGE:
  3970. * "#css.rule".addClass('boo-hoo');
  3971. * "#css.rule".setStyle({color: 'red'});
  3972. *
  3973. */
  3974. $w('Element Input Form').each(function(klass) {
  3975. Object.each(klass in RightJS ? RightJS[klass].prototype : {}, function(name, method) {
  3976. if (isFunction(method) && !(name in String.prototype)) {
  3977. String.prototype[name] = function() {
  3978. var nodes = $$(this, true), i=0, l = nodes.length, first=true, element, result;
  3979. for (; i < l; i++) {
  3980. element = wrap(nodes[i]);
  3981. result = element[name].apply(element, arguments);
  3982. // checking if that's a data-retrieving call
  3983. if (first) {
  3984. if (result !== element) {
  3985. return result;
  3986. }
  3987. first = false;
  3988. }
  3989. }
  3990. // don't return the string itself in here,
  3991. // it will screw with data-retrieving calls on empty collections
  3992. return null;
  3993. };
  3994. }
  3995. });
  3996. });
  3997. /**
  3998. * XMLHttpRequest wrapper
  3999. *
  4000. * Credits:
  4001. * Some of the functionality inspired by
  4002. * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
  4003. * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
  4004. * - jQuery (http://jquery.com) Copyright (C) John Resig
  4005. *
  4006. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  4007. */
  4008. var Xhr = RightJS.Xhr = new Class(Observer, {
  4009. extend: {
  4010. // supported events list
  4011. EVENTS: $w('success failure complete request cancel create'),
  4012. // default options
  4013. Options: {
  4014. headers: {
  4015. 'X-Requested-With': 'XMLHttpRequest',
  4016. 'Accept': 'text/javascript,text/html,application/xml,text/xml,*/*'
  4017. },
  4018. method: 'post',
  4019. encoding: 'utf-8',
  4020. async: true,
  4021. evalScripts: false,
  4022. evalResponse: false,
  4023. evalJS: true,
  4024. evalJSON: true,
  4025. secureJSON: true,
  4026. urlEncoded: true,
  4027. spinner: null,
  4028. spinnerFx: 'fade',
  4029. params: null,
  4030. iframed: false,
  4031. jsonp: false
  4032. },
  4033. /**
  4034. * Shortcut to initiate and send an XHR in a single call
  4035. *
  4036. * @param String url
  4037. * @param Object options
  4038. * @return Xhr request
  4039. */
  4040. load: function(url, options) {
  4041. return new this(url, $ext({method: 'get'}, options)).send();
  4042. }
  4043. },
  4044. /**
  4045. * basic constructor
  4046. *
  4047. * @param String url
  4048. * @param Object options
  4049. */
  4050. initialize: function(url, options) {
  4051. this.initCallbacks(); // system level callbacks should be initialized before the user callbacks
  4052. this.url = url;
  4053. // copying some options to the instance level attributes
  4054. $ext(this.$super(options), this.options);
  4055. // merging in the global params
  4056. if (this.params != Xhr.Options.params) {
  4057. this.params = this.prepareData(Xhr.Options.params, this.params);
  4058. }
  4059. // removing the local spinner if it's the same as the global one
  4060. if (Xhr.Options.spinner && $(this.spinner) === $(Xhr.Options.spinner)) {
  4061. this.spinner = null;
  4062. }
  4063. },
  4064. /**
  4065. * sets a header
  4066. *
  4067. * @param name String header name
  4068. * @param value String header value
  4069. * @return Xhr self
  4070. */
  4071. setHeader: function(name, value) {
  4072. this.headers[name] = value;
  4073. return this;
  4074. },
  4075. /**
  4076. * tries to get a response header
  4077. *
  4078. * @return mixed String header value or undefined
  4079. */
  4080. getHeader: function(name) {
  4081. var value;
  4082. try {
  4083. value = this.xhr.getResponseHeader(name);
  4084. } catch(e) {}
  4085. return value;
  4086. },
  4087. /**
  4088. * checks if the request was successful
  4089. *
  4090. * @return boolean check result
  4091. */
  4092. successful: function() {
  4093. return (this.status >= 200) && (this.status < 300);
  4094. },
  4095. /**
  4096. * performs the actual request sending
  4097. *
  4098. * @param Object options
  4099. * @return Xhr self
  4100. */
  4101. send: function(params) {
  4102. var add_params = {},
  4103. url = this.url,
  4104. method = this.method.toLowerCase(),
  4105. headers = this.headers,
  4106. key, xhr;
  4107. if (method == 'put' || method == 'delete') {
  4108. add_params._method = method;
  4109. method = 'post';
  4110. }
  4111. var data = this.prepareData(this.params, this.prepareParams(params), add_params);
  4112. if (this.urlEncoded && method == 'post' && !headers['Content-type']) {
  4113. this.setHeader('Content-type', 'application/x-www-form-urlencoded;charset='+this.encoding);
  4114. }
  4115. if (method == 'get') {
  4116. if (data) { url += (url.include('?') ? '&' : '?') + data; }
  4117. data = null;
  4118. }
  4119. xhr = this.xhr = this.createXhr();
  4120. this.fire('create');
  4121. xhr.open(method, url, this.async);
  4122. xhr.onreadystatechange = this.stateChanged.bind(this);
  4123. for (key in headers) {
  4124. xhr.setRequestHeader(key, headers[key]);
  4125. }
  4126. xhr.send(data);
  4127. this.fire('request');
  4128. if (!this.async) { this.stateChanged(); }
  4129. return this;
  4130. },
  4131. /**
  4132. * elements automaticall update method, creates an Xhr request
  4133. * and updates the element innerHTML value onSuccess.
  4134. *
  4135. * @param Element element
  4136. * @param Object optional request params
  4137. * @return Xhr self
  4138. */
  4139. update: function(element, params) {
  4140. return this.onSuccess(function(r) { element.update(r.text); }).send(params);
  4141. },
  4142. /**
  4143. * stops the request processing
  4144. *
  4145. * @return Xhr self
  4146. */
  4147. cancel: function() {
  4148. var xhr = this.xhr;
  4149. if (!xhr || xhr.canceled) { return this; }
  4150. xhr.abort();
  4151. xhr.onreadystatechange = function() {};
  4152. xhr.canceled = true;
  4153. return this.fire('cancel');
  4154. },
  4155. // protected
  4156. // wrapping the original method to send references to the xhr objects
  4157. fire: function(name) {
  4158. return this.$super(name, this, this.xhr);
  4159. },
  4160. // creates new request instance
  4161. createXhr: function() {
  4162. if (this.jsonp) {
  4163. return new Xhr.JSONP(this);
  4164. } else if (this.form && this.form.first('input[type=file]')) {
  4165. return new Xhr.IFramed(this.form);
  4166. } else if ('ActiveXObject' in window){
  4167. return new ActiveXObject('MSXML2.XMLHTTP');
  4168. } else {
  4169. return new XMLHttpRequest();
  4170. }
  4171. },
  4172. // prepares user sending params
  4173. prepareParams: function(params) {
  4174. return params;
  4175. },
  4176. // converts all the params into a url params string
  4177. prepareData: function() {
  4178. return $A(arguments).map(function(param) {
  4179. if (!isString(param)) {
  4180. param = Object.toQueryString(param);
  4181. }
  4182. return param.blank() ? null : param;
  4183. }).compact().join('&');
  4184. },
  4185. // handles the state change
  4186. stateChanged: function() {
  4187. var xhr = this.xhr;
  4188. if (xhr.readyState != 4 || xhr.canceled) { return; }
  4189. try { this.status = xhr.status;
  4190. } catch(e) { this.status = 0; }
  4191. this.text = this.responseText = xhr.responseText;
  4192. this.xml = this.responseXML = xhr.responseXML;
  4193. this.fire('complete').fire(this.successful() ? 'success' : 'failure');
  4194. },
  4195. // called on success
  4196. tryScripts: function(response) {
  4197. var content_type = this.getHeader('Content-type');
  4198. var x_json_data = this.getHeader('X-JSON');
  4199. if (x_json_data) {
  4200. this.json = this.responseJSON = this.headerJSON = JSON.parse(x_json_data);
  4201. }
  4202. if (this.evalResponse || (this.evalJS && /(ecma|java)script/i.test(content_type))) {
  4203. $eval(this.text);
  4204. } else if (/json/.test(content_type) && this.evalJSON) {
  4205. this.json = this.responseJSON = JSON.parse(this.text);
  4206. } else if (this.evalScripts) {
  4207. this.text.evalScripts();
  4208. }
  4209. },
  4210. // initializes the request callbacks
  4211. initCallbacks: function() {
  4212. // connecting basic callbacks
  4213. this.on({
  4214. create: 'showSpinner',
  4215. complete: 'hideSpinner',
  4216. cancel: 'hideSpinner'
  4217. });
  4218. this.on('complete', 'tryScripts');
  4219. // wiring the global xhr callbacks
  4220. Xhr.EVENTS.each(function(name) {
  4221. this.on(name, function() { Xhr.fire(name, this, this.xhr); });
  4222. }, this);
  4223. },
  4224. showSpinner: function() { Xhr.showSpinner.call(this, this); },
  4225. hideSpinner: function() { Xhr.hideSpinner.call(this, this); }
  4226. });
  4227. // attaching the common spinner handling
  4228. $ext(Observer_create(Xhr), {
  4229. counter: 0,
  4230. // shows the spinner
  4231. showSpinner: function(context) {
  4232. Xhr.trySpinner(context, 'show');
  4233. },
  4234. // hides the spinner
  4235. hideSpinner: function(context) {
  4236. Xhr.trySpinner(context, 'hide');
  4237. },
  4238. trySpinner: function(context, method) {
  4239. var object = context || Xhr.Options, spinner = $(object.spinner);
  4240. if (spinner) { spinner[method](object.spinnerFx, {duration: 100}); }
  4241. },
  4242. // counts a request in
  4243. countIn: function() {
  4244. Xhr.counter ++;
  4245. Xhr.showSpinner();
  4246. },
  4247. // counts a request out
  4248. countOut: function() {
  4249. Xhr.counter --;
  4250. if (Xhr.counter < 1) {
  4251. Xhr.hideSpinner();
  4252. }
  4253. }
  4254. }).on({
  4255. create: 'countIn',
  4256. complete: 'countOut',
  4257. cancel: 'countOut'
  4258. });
  4259. /**
  4260. * Here are the Form unit Xhr extensions
  4261. *
  4262. * Credits:
  4263. * Some of the functionality inspired by
  4264. * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
  4265. * - jQuery (http://jquery.com) Copyright (C) John Resig
  4266. *
  4267. * Copyright (C) 2009-2011 Nikolay V. Nemshilov
  4268. */
  4269. Form.include({
  4270. /**
  4271. * sends the form via xhr request
  4272. *
  4273. * @param Options xhr request options
  4274. * @return Form this
  4275. */
  4276. send: function(options) {
  4277. options = options || {};
  4278. options.method = options.method || this._.method || 'post';
  4279. this.xhr = new Xhr(
  4280. this._.action || document.location.href,
  4281. $ext({spinner: this.first('.spinner')}, options)
  4282. )
  4283. .onComplete(this.enable.bind(this))
  4284. .onCancel(this.enable.bind(this))
  4285. .send(this);
  4286. this.disable.bind(this).delay(1); // webkit needs this async call with iframed calls
  4287. return this;
  4288. },
  4289. /**
  4290. * Cancels current Xhr request (if there are any)
  4291. *
  4292. * @return Form this
  4293. */
  4294. cancelXhr: function() {
  4295. if (this.xhr instanceof Xhr) {
  4296. this.xhr.cancel();
  4297. }
  4298. return this;
  4299. },
  4300. /**
  4301. * makes the form be remote by default
  4302. *
  4303. * @param Object default options
  4304. * @return Form this
  4305. */
  4306. remotize: function(options) {
  4307. if (!this.remote) {
  4308. this.on('submit', Form_remote_send, options);
  4309. this.remote = true;
  4310. }
  4311. return this;
  4312. },
  4313. /**
  4314. * removes the remote call hook
  4315. *
  4316. * @return Form this
  4317. */
  4318. unremotize: function() {
  4319. this.stopObserving('submit', Form_remote_send);
  4320. this.remote = false;
  4321. return this;
  4322. }
  4323. });
  4324. /**
  4325. * Catches the form submit events and sends the form remotely
  4326. *
  4327. * @param Event submit
  4328. * @param Object xhr options
  4329. * @return void
  4330. */
  4331. function Form_remote_send(event, options) {
  4332. event.stop();
  4333. this.send(options);
  4334. }
  4335. /**
  4336. * Adds Xhr params handling if a Form element is passed to Xhr#send
  4337. *
  4338. * @param Object params - could be Hash or Form element
  4339. * @return Object
  4340. */
  4341. Xhr.include({
  4342. prepareParams: function(params) {
  4343. if (params && params instanceof Form) {
  4344. this.form = params;
  4345. params = params.values();
  4346. }
  4347. return params;
  4348. }
  4349. });
  4350. /**
  4351. * this module contains the Element unit XHR related extensions
  4352. *
  4353. * Credits:
  4354. * - jQuery (http://jquery.com) Copyright (C) John Resig
  4355. *
  4356. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  4357. */
  4358. Element.include({
  4359. /**
  4360. * performs an Xhr request to the given url
  4361. * and updates the element internals with the responseText
  4362. *
  4363. * @param String url address
  4364. * @param Object xhr options
  4365. * @return Element this
  4366. */
  4367. load: function(url, options) {
  4368. new Xhr(url, $ext({method: 'get'}, options)).update(this);
  4369. return this;
  4370. }
  4371. });
  4372. /**
  4373. * A dummy XmlHTTPRequest interface to be used in other
  4374. * fake requests
  4375. *
  4376. * Copyright (C) 2010-2011 Nikolay Nemshilov
  4377. */
  4378. Xhr.Dummy = {
  4379. open: function() {},
  4380. setRequestHeader: function() {},
  4381. onreadystatechange: function() {}
  4382. };
  4383. /**
  4384. * This unit presents a fake drop in replacement for the XmlHTTPRequest unit
  4385. * but works with an iframe targeting in the background
  4386. *
  4387. * Copyright (C) 2008-2011 Nikolay Nemshilov
  4388. */
  4389. Xhr.IFramed = new Class({
  4390. include: Xhr.Dummy,
  4391. /**
  4392. * constructor
  4393. *
  4394. * @param Form form which will be submitted via the frame
  4395. * @return void
  4396. */
  4397. initialize: function(form) {
  4398. this.form = form;
  4399. this.id = 'xhr_'+ new Date().getTime();
  4400. this.form.doc().first('body').append('<i><iframe name="'+this.id+'" id="'+this.id+
  4401. '" width="0" height="0" frameborder="0" src="about:blank"></iframe></i>',
  4402. 'after');
  4403. $(this.id).on('load', this.onLoad.bind(this));
  4404. },
  4405. send: function() {
  4406. this.form.set('target', this.id).submit();
  4407. },
  4408. onLoad: function() {
  4409. this.status = 200;
  4410. this.readyState = 4;
  4411. this.form.set('target', '');
  4412. try {
  4413. this.responseText = window[this.id].document.documentElement.innerHTML;
  4414. } catch(e) { }
  4415. this.onreadystatechange();
  4416. },
  4417. abort: function() {
  4418. $(this.id).set('src', 'about:blank');
  4419. }
  4420. });
  4421. /**
  4422. * The JSONP Xhr request tonnel
  4423. *
  4424. * Copyright (C) 2010-2011 Nikolay Nemshilov
  4425. */
  4426. Xhr.JSONP = new Class({
  4427. include: Xhr.Dummy,
  4428. prefix: 'jsonp',
  4429. /**
  4430. * Constructor
  4431. *
  4432. * @param Xhr the actual xhr request object
  4433. * @return void
  4434. */
  4435. initialize: function(xhr) {
  4436. this.xhr = xhr;
  4437. this.name = this.prefix + new Date().getTime();
  4438. this.param = (isString(xhr.jsonp) ?
  4439. xhr.jsonp : 'callback') + "=" + this.name;
  4440. this.script = $E('script', {
  4441. charset: xhr.encoding,
  4442. async: xhr.async
  4443. });
  4444. },
  4445. /**
  4446. * saving the url and method for the further use
  4447. *
  4448. * @param method String request method
  4449. * @param address String request url address
  4450. * @param Boolean async request marker
  4451. * @return void
  4452. */
  4453. open: function(method, url, async) {
  4454. this.url = url;
  4455. this.method = method;
  4456. },
  4457. /**
  4458. * Sends the actual request by inserting the script into the document body
  4459. *
  4460. * @param String data
  4461. * @return void
  4462. */
  4463. send: function(data) {
  4464. window[this.name] = this.finish.bind(this);
  4465. this.script.set('src', this.url + (this.url.include('?') ? '&' : '?') + this.param + "&" + data)
  4466. .insertTo($$('script').last(), 'after');
  4467. },
  4468. /**
  4469. * Receives the actual JSON data from the server
  4470. *
  4471. * @param Object JSON data
  4472. * @return void
  4473. */
  4474. finish: function(data) {
  4475. this.status = 200;
  4476. this.readyState = 4;
  4477. this.xhr.json = this.xhr.responseJSON = data;
  4478. this.onreadystatechange();
  4479. },
  4480. /**
  4481. * We can't really cancel a JSONP request
  4482. * but we can prevent the default handler to ckick in
  4483. *
  4484. * @return void
  4485. */
  4486. abort: function() {
  4487. window[this.name] = function() {};
  4488. }
  4489. });
  4490. /**
  4491. * Basic visual effects class
  4492. *
  4493. * Credits:
  4494. * The basic principles, structures and naming system are inspired by
  4495. * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
  4496. * The cubic bezier emulation is backported from
  4497. * - Lovely.IO (http://lovely.io) Copytirhgt (C) Nikolay Nemshilov
  4498. *
  4499. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  4500. */
  4501. var Fx = RightJS.Fx = new Class(Observer, {
  4502. extend: {
  4503. EVENTS: $w('start finish cancel'),
  4504. // named durations
  4505. Durations: {
  4506. 'short': 200,
  4507. 'normal': 400,
  4508. 'long': 800
  4509. },
  4510. // default options
  4511. Options: {
  4512. fps: IE8_OR_LESS ? 40 : 60,
  4513. duration: 'normal',
  4514. transition: 'default',
  4515. queue: true,
  4516. engine: 'css'
  4517. }
  4518. },
  4519. /**
  4520. * Basic constructor
  4521. *
  4522. * @param Object options
  4523. */
  4524. initialize: function(element, options) {
  4525. this.$super(options);
  4526. this.element = $(element);
  4527. fx_register(this);
  4528. },
  4529. /**
  4530. * starts the transition
  4531. *
  4532. * @return Fx this
  4533. */
  4534. start: function() {
  4535. if (fx_add_to_queue(this, arguments)) { return this; }
  4536. fx_mark_current(this);
  4537. this.prepare.apply(this, arguments);
  4538. fx_start_timer(this);
  4539. return this.fire('start', this);
  4540. },
  4541. /**
  4542. * finishes the transition
  4543. *
  4544. * @return Fx this
  4545. */
  4546. finish: function() {
  4547. fx_stop_timer(this);
  4548. fx_remove_from_queue(this);
  4549. this.fire('finish');
  4550. fx_run_next(this);
  4551. return this;
  4552. },
  4553. /**
  4554. * interrupts the transition
  4555. *
  4556. * NOTE:
  4557. * this method cancels all the scheduled effects
  4558. * in the element chain
  4559. *
  4560. * @return Fx this
  4561. */
  4562. cancel: function() {
  4563. fx_stop_timer(this);
  4564. fx_remove_from_queue(this);
  4565. return this.fire('cancel');
  4566. },
  4567. // protected
  4568. // dummy method, should be implemented in a subclass
  4569. prepare: function() {},
  4570. // dummy method, processes the element properties
  4571. render: function() {}
  4572. }),
  4573. // global effects registry
  4574. scheduled_fx = [], running_fx = [];
  4575. /**
  4576. * Registers the element in the effects queue
  4577. *
  4578. * @param Fx effect
  4579. * @return void
  4580. */
  4581. function fx_register(fx) {
  4582. var uid = $uid((fx.element || {})._ || {});
  4583. fx.ch = (scheduled_fx[uid] = scheduled_fx[uid] || []);
  4584. fx.cr = (running_fx[uid] = running_fx[uid] || []);
  4585. }
  4586. /**
  4587. * Registers the effect in the effects queue
  4588. *
  4589. * @param Fx fx
  4590. * @param Arguments original arguments list
  4591. * @return boolean true if it queued and false if it's ready to go
  4592. */
  4593. function fx_add_to_queue(fx, args) {
  4594. var chain = fx.ch, queue = fx.options.queue;
  4595. if (!chain || fx.$ch) {
  4596. return (fx.$ch = false);
  4597. }
  4598. if (queue) {
  4599. chain.push([args, fx]);
  4600. }
  4601. return queue && chain[0][1] !== fx;
  4602. }
  4603. /**
  4604. * Puts the fx into the list of currently running effects
  4605. *
  4606. * @param Fx fx
  4607. * @return void
  4608. */
  4609. function fx_mark_current(fx) {
  4610. if (fx.cr) {
  4611. fx.cr.push(fx);
  4612. }
  4613. }
  4614. /**
  4615. * Removes the fx from the queue
  4616. *
  4617. * @param Fx fx
  4618. * @return void
  4619. */
  4620. function fx_remove_from_queue(fx) {
  4621. var currents = fx.cr;
  4622. if (currents) {
  4623. currents.splice(currents.indexOf(fx), 1);
  4624. }
  4625. }
  4626. /**
  4627. * Tries to invoke the next effect in the queue
  4628. *
  4629. * @param Fx fx
  4630. * @return void
  4631. */
  4632. function fx_run_next(fx) {
  4633. var chain = fx.ch, next = chain.shift();
  4634. if ((next = chain[0])) {
  4635. next[1].$ch = true;
  4636. next[1].start.apply(next[1], next[0]);
  4637. }
  4638. }
  4639. /**
  4640. * Cancels all currently running and scheduled effects
  4641. * on the element
  4642. *
  4643. * @param Element element
  4644. * @return void
  4645. */
  4646. function fx_cancel_all(element) {
  4647. var uid = $uid(element._);
  4648. (running_fx[uid] || []).each('cancel');
  4649. (scheduled_fx[uid] || []).splice(0);
  4650. }
  4651. /**
  4652. * Initializes the fx rendering timer
  4653. *
  4654. * @param Fx fx
  4655. * @return void
  4656. */
  4657. function fx_start_timer(fx) {
  4658. var options = fx.options,
  4659. duration = Fx.Durations[options.duration] || options.duration,
  4660. steps = Math.ceil(duration / 1000 * options.fps),
  4661. transition = Bezier_sequence(options.transition, steps),
  4662. interval = Math.round(1000 / options.fps),
  4663. number = 0;
  4664. fx._timer = setInterval(function() {
  4665. if (number === steps) {
  4666. fx.finish();
  4667. } else {
  4668. fx.render(transition[number]);
  4669. number++;
  4670. }
  4671. }, interval);
  4672. }
  4673. /**
  4674. * Cancels the Fx rendering timer (if any)
  4675. *
  4676. * @param Fx fx
  4677. * @return void
  4678. */
  4679. function fx_stop_timer(fx) {
  4680. if (fx._timer) {
  4681. clearInterval(fx._timer);
  4682. }
  4683. }
  4684. ///////////////////////////////////////////////////////////////////////////////
  4685. // CSS3 Cubic Bezier sequentions emulator
  4686. // Backport from Lovely.IO (http://lovely.io)
  4687. // See also:
  4688. // http://st-on-it.blogspot.com/2011/05/calculating-cubic-bezier-function.html
  4689. ///////////////////////////////////////////////////////////////////////////////
  4690. // CSS3 cubic-bezier presets
  4691. var Bezier_presets = {
  4692. 'default': '(.25,.1,.25,1)',
  4693. 'linear': '(0,0,1,1)',
  4694. 'ease-in': '(.42,0,1,1)',
  4695. 'ease-out': '(0,0,.58,1)',
  4696. 'ease-in-out': '(.42,0,.58,1)',
  4697. 'ease-out-in': '(0,.42,1,.58)'
  4698. },
  4699. // Bezier loockup tables cache
  4700. Bezier_cache = {};
  4701. // builds a loockup table of parametric values with a given size
  4702. function Bezier_sequence(params, size) {
  4703. params = Bezier_presets[params] || native_fx_functions[params] || params;
  4704. params = params.match(/([\d\.]+)[\s,]+([\d\.]+)[\s,]+([\d\.]+)[\s,]+([\d\.]+)/);
  4705. params = [0, params[1]-0, params[2]-0, params[3]-0, params[4]-0]; // cleaning up
  4706. var name = params.join(',') + ',' + size, Cx, Bx, Ax, Cy, By, Ay, sequence, step, x;
  4707. function bezier_x(t) { return t * (Cx + t * (Bx + t * Ax)); }
  4708. function bezier_y(t) { return t * (Cy + t * (By + t * Ay)); }
  4709. // a quick search for a more or less close parametric
  4710. // value using several iterations by Newton's method
  4711. function bezier_x_der(t) { // bezier_x derivative
  4712. return Cx + t * (2*Bx + t * 3*Ax) + 1e-3;
  4713. }
  4714. function find_parametric(t) {
  4715. var x=t, i=0, z;
  4716. while (i < 5) {
  4717. z = bezier_x(x) - t;
  4718. if (Math.abs(z) < 1e-3) { break; }
  4719. x = x - z/bezier_x_der(x);
  4720. i++;
  4721. }
  4722. return x;
  4723. }
  4724. if (!(name in Bezier_cache)) {
  4725. // defining bezier functions in a polynomial form (coz it's faster)
  4726. Cx = 3 * params[1];
  4727. Bx = 3 * (params[3] - params[1]) - Cx;
  4728. Ax = 1 - Cx - Bx;
  4729. Cy = 3 * params[2];
  4730. By = 3 * (params[4] - params[2]) - Cy;
  4731. Ay = 1 - Cy - By;
  4732. // building the actual lookup table
  4733. Bezier_cache[name] = sequence = [];
  4734. x=0; step=1/size;
  4735. while (x < 1.0001) { // should include 1.0
  4736. sequence.push(bezier_y(find_parametric(x)));
  4737. x += step;
  4738. }
  4739. }
  4740. return Bezier_cache[name];
  4741. }
  4742. /**
  4743. * There are the String unit extensions for the effects library
  4744. *
  4745. * Copyright (C) 2008-2009 Nikolay V. Nemshilov
  4746. */
  4747. String.COLORS = {
  4748. maroon: '#800000',
  4749. red: '#ff0000',
  4750. orange: '#ffA500',
  4751. yellow: '#ffff00',
  4752. olive: '#808000',
  4753. purple: '#800080',
  4754. fuchsia: '#ff00ff',
  4755. white: '#ffffff',
  4756. lime: '#00ff00',
  4757. green: '#008000',
  4758. navy: '#000080',
  4759. blue: '#0000ff',
  4760. aqua: '#00ffff',
  4761. teal: '#008080',
  4762. black: '#000000',
  4763. silver: '#c0c0c0',
  4764. gray: '#808080',
  4765. brown: '#a52a2a'
  4766. };
  4767. String.include({
  4768. /**
  4769. * converts a #XXX or rgb(X, X, X) sring into standard #XXXXXX color string
  4770. *
  4771. * @return String hex color
  4772. */
  4773. toHex: function() {
  4774. var match = /^#(\w)(\w)(\w)$/.exec(this);
  4775. if (match) {
  4776. match = "#"+ match[1]+match[1]+match[2]+match[2]+match[3]+match[3];
  4777. } else if ((match = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/.exec(this))) {
  4778. match = "#"+ match.slice(1).map(function(bit) {
  4779. bit = (bit-0).toString(16);
  4780. return bit.length == 1 ? '0'+bit : bit;
  4781. }).join('');
  4782. } else {
  4783. match = String.COLORS[this] || this;
  4784. }
  4785. return match;
  4786. },
  4787. /**
  4788. * converts a hex string into an rgb array
  4789. *
  4790. * @param boolean flag if need an array
  4791. * @return String rgb(R,G,B) or Array [R,G,B]
  4792. */
  4793. toRgb: function(array) {
  4794. var match = /#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})/i.exec(this.toHex()||'');
  4795. if (match) {
  4796. match = match.slice(1).map('toInt', 16);
  4797. match = array ? match : 'rgb('+match+')';
  4798. }
  4799. return match;
  4800. }
  4801. });
  4802. /**
  4803. * This block contains additional Element shortcuts for effects easy handling
  4804. *
  4805. * Credits:
  4806. * Some ideas are inspired by
  4807. * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
  4808. *
  4809. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  4810. */
  4811. Element.include({
  4812. /**
  4813. * Stops all the visual effects on the element
  4814. *
  4815. * @return Element this
  4816. */
  4817. stop: function() {
  4818. fx_cancel_all(this);
  4819. return this;
  4820. },
  4821. /**
  4822. * hides the element with given visual effect
  4823. *
  4824. * @param String fx name
  4825. * @param Object fx options
  4826. * @return Element this
  4827. */
  4828. hide: function(fx, options) {
  4829. return (fx && this.visible()) ? call_fx(this, fx, ['out', options]) : this.$super();
  4830. },
  4831. /**
  4832. * shows the element with the given visual effect
  4833. *
  4834. * @param String fx name
  4835. * @param Object fx options
  4836. * @return Element this
  4837. */
  4838. show: function(fx, options) {
  4839. return (fx && !this.visible()) ? call_fx(this, fx, ['in', options]) : this.$super();
  4840. },
  4841. /**
  4842. * Toggles the element state with visual effect
  4843. *
  4844. * @param String fx name
  4845. * @param Object fx options
  4846. * @return Element this
  4847. */
  4848. toggle: function(fx, options) {
  4849. return fx ? call_fx(this, fx, ['toggle', options]) : this.$super();
  4850. },
  4851. /**
  4852. * Removes the element out of the DOM structure
  4853. *
  4854. * @param String fx name
  4855. * @param Object fx options
  4856. * @return Element this
  4857. */
  4858. remove: function(fx, options) {
  4859. return (fx && this.visible()) ? call_fx(this, fx, ['out', $ext(options || {}, {
  4860. onFinish: this.$super.bind(this)
  4861. })]) : this.$super();
  4862. },
  4863. /**
  4864. * runs the Fx.Morth effect to the given style
  4865. *
  4866. * @param style Object style
  4867. * @param options Object optional effect options
  4868. * @return Element self
  4869. */
  4870. morph: function(style, options) {
  4871. return call_fx(this, 'morph', [style, options || {}]); // <- don't replace with arguments
  4872. },
  4873. /**
  4874. * highlights the element
  4875. *
  4876. * @param start String start color
  4877. * @param end String optional end color
  4878. * @param Object effect options
  4879. * @return Element self
  4880. */
  4881. highlight: function() {
  4882. return call_fx(this, 'highlight', arguments);
  4883. },
  4884. /**
  4885. * runs the Fx.Fade effect on the element
  4886. *
  4887. * @param mixed fade direction 'in' 'out' or a float number
  4888. * @return Element self
  4889. */
  4890. fade: function() {
  4891. return call_fx(this, 'fade', arguments);
  4892. },
  4893. /**
  4894. * runs the Fx.Slide effect on the element
  4895. *
  4896. * @param String 'in' or 'out'
  4897. * @param Object effect options
  4898. * @return Element self
  4899. */
  4900. slide: function() {
  4901. return call_fx(this, 'slide', arguments);
  4902. },
  4903. /**
  4904. * Starts the smooth scrolling effect
  4905. *
  4906. * @param position Object {x: NNN, y: NNN} where to scroll
  4907. * @param options Object fx-options
  4908. * @return Element this
  4909. */
  4910. scroll: function(value, options) {
  4911. return call_fx(this, 'scroll', [value, options||{}]);
  4912. },
  4913. /**
  4914. * wraps the old scroll to be able to run it with fxes
  4915. *
  4916. * If you send two hashes then will start a smooth scrolling
  4917. * otherwise will just jump over with the usual method
  4918. *
  4919. * @return Element this
  4920. */
  4921. scrollTo: function(value, options) {
  4922. return isHash(options) ? this.scroll(value, options) : this.$super.apply(this, arguments);
  4923. }
  4924. });
  4925. /**
  4926. * Calls the visual effect on the element
  4927. *
  4928. * @param Element context
  4929. * @param String fx-name
  4930. * @param Object fx-options
  4931. * @return Element context
  4932. */
  4933. function call_fx(element, name, params) {
  4934. var args = $A(params).compact(),
  4935. options = isHash(args.last()) ? args.pop() : {},
  4936. fx = new Fx[name.capitalize()](element, options);
  4937. fx.start.apply(fx, args);
  4938. return element;
  4939. }
  4940. /**
  4941. * This class provides the basic effect for styles manipulation
  4942. *
  4943. * Copyright (C) 2008-2011 Nikolay Nemshilov
  4944. */
  4945. /////////////////////////////////////////////////////////////////////////////
  4946. // Native css-transitions based implementation
  4947. /////////////////////////////////////////////////////////////////////////////
  4948. var native_fx_prefix = ['WebkitT', 'OT', 'MozT', 'MsT', 't'].first(function(name) {
  4949. return name + 'ransition' in HTML.style;
  4950. }),
  4951. native_fx_transition = native_fx_prefix + 'ransition',
  4952. native_fx_property = native_fx_transition + 'Property',
  4953. native_fx_duration = native_fx_transition + 'Duration',
  4954. native_fx_function = native_fx_transition + 'TimingFunction',
  4955. // basic transition algorithm replacements
  4956. native_fx_functions = {
  4957. Sin: 'cubic-bezier(.3,0,.6,1)',
  4958. Cos: 'cubic-bezier(0,.3,.6,0)',
  4959. Log: 'cubic-bezier(0,.6,.3,.8)',
  4960. Exp: 'cubic-bezier(.6,0,.8,.3)',
  4961. Lin: 'cubic-bezier(0,0,1,1)'
  4962. };
  4963. function native_fx_prepare(style) {
  4964. var options = this.options,
  4965. element = this.element,
  4966. element_style = element._.style,
  4967. old_style = Object.only(
  4968. element.computedStyles(),
  4969. native_fx_property,
  4970. native_fx_duration,
  4971. native_fx_function
  4972. );
  4973. function reset_transitions_style() {
  4974. for (var key in old_style) {
  4975. element_style[key] = old_style[key];
  4976. }
  4977. }
  4978. this
  4979. .onFinish(reset_transitions_style)
  4980. .onCancel(function() {
  4981. element_style[native_fx_property] = 'none';
  4982. setTimeout(reset_transitions_style, 1);
  4983. });
  4984. // setting up the transition
  4985. element_style[native_fx_property] = 'all';
  4986. element_style[native_fx_duration] = (Fx.Durations[options.duration] || options.duration) +'ms';
  4987. element_style[native_fx_function] = native_fx_functions[options.transition] || options.transition;
  4988. setTimeout(function() { element.setStyle(style); }, 0);
  4989. }
  4990. // NOTE: OPERA's css-transitions are a bit jerky so we disable them by default
  4991. Fx.Options.engine = native_fx_prefix === undefined || Browser_Opera ? 'javascript' : 'native';
  4992. ////////////////////////////////////////////////////////////////////////////
  4993. // Manual version
  4994. ////////////////////////////////////////////////////////////////////////////
  4995. Fx.Morph = new Class(Fx, {
  4996. // protected
  4997. // parepares the effect
  4998. prepare: function(style) {
  4999. if (this.options.engine === 'native' && native_fx_prefix !== undefined) {
  5000. this.render = this.transition = function() {};
  5001. native_fx_prepare.call(this, style);
  5002. } else {
  5003. var keys = style_keys(style),
  5004. before = clone_style(this.element, keys),
  5005. after = end_style(this.element, style, keys);
  5006. clean_styles(this.element, before, after);
  5007. this.before = parse_style(before);
  5008. this.after = parse_style(after);
  5009. }
  5010. },
  5011. render: function(delta) {
  5012. var before, after, value, style = this.element._.style, key, i, l;
  5013. for (key in this.after) {
  5014. before = this.before[key];
  5015. after = this.after[key];
  5016. for (i=0, l = after.length; i < l; i++) {
  5017. value = before[i] + (after[i] - before[i]) * delta;
  5018. if (after.r) {
  5019. value = Math.round(value);
  5020. }
  5021. after.t[i*2 + 1] = value;
  5022. }
  5023. style[key] = after.t.join('');
  5024. }
  5025. }
  5026. });
  5027. // a list of common style names to compact the code a bit
  5028. var directions = $w('Top Left Right Bottom');
  5029. // adds variants to the style names list
  5030. function add_variants(keys, key, variants) {
  5031. for (var i=0; i < variants.length; i++) {
  5032. keys.push(key + variants[i]);
  5033. }
  5034. }
  5035. // creates an appropriate style-keys list out of the user styles
  5036. function style_keys(style) {
  5037. var keys = [], border_types = ['Style', 'Color', 'Width'], key, i, j;
  5038. for (key in style) {
  5039. if (key.startsWith('border')) {
  5040. for (i=0; i < 3; i++) {
  5041. for (j=0; j < 4; j++) {
  5042. keys.push('border' + directions[j] + border_types[i]);
  5043. }
  5044. }
  5045. } else if (key === 'margin' || key === 'padding') {
  5046. add_variants(keys, key, directions);
  5047. } else if (key.startsWith('background')) {
  5048. add_variants(keys, 'background', ['Color', 'Position', 'PositionX', 'PositionY']);
  5049. } else if (key === 'opacity' && IE_OPACITY) {
  5050. keys.push('filter');
  5051. } else {
  5052. keys.push(key);
  5053. }
  5054. }
  5055. return keys;
  5056. }
  5057. // checks if the color is transparent
  5058. function is_transparent(color) {
  5059. return color === 'transparent' || color === 'rgba(0, 0, 0, 0)';
  5060. }
  5061. // adjusts the border-styles
  5062. function check_border_styles(element, before, after) {
  5063. for (var i=0; i < 4; i++) {
  5064. var
  5065. bd_style = 'border' + directions[i] + 'Style',
  5066. bd_width = 'border' + directions[i] + 'Width',
  5067. bd_color = 'border' + directions[i] + 'Color';
  5068. if (bd_style in before && before[bd_style] != after[bd_style]) {
  5069. var style = element._.style;
  5070. if (before[bd_style] == 'none') {
  5071. style[bd_width] = '0px';
  5072. }
  5073. style[bd_style] = after[bd_style];
  5074. if (is_transparent(before[bd_color])) {
  5075. style[bd_color] = element.getStyle('Color');
  5076. }
  5077. }
  5078. }
  5079. }
  5080. // parses the style hash into a processable format
  5081. function parse_style(values) {
  5082. var result = {}, re = /[\d\.\-]+/g, m, key, value, i;
  5083. for (key in values) {
  5084. m = values[key].match(re);
  5085. value = m.map('toFloat');
  5086. value.t = values[key].split(re);
  5087. value.r = value.t[0] === 'rgb(';
  5088. if (value.t.length == 1) { value.t.unshift(''); }
  5089. for (i=0; i < value.length; i++) {
  5090. value.t.splice(i*2 + 1, 0, value[i]);
  5091. }
  5092. result[key] = value;
  5093. }
  5094. return result;
  5095. }
  5096. // cleans up and optimizies the styles
  5097. function clean_styles(element, before, after) {
  5098. var key;
  5099. for (key in after) {
  5100. // checking the height/width options
  5101. if ((key == 'width' || key == 'height') && before[key] == 'auto') {
  5102. before[key] = element._['offset'+key.capitalize()] + 'px';
  5103. }
  5104. }
  5105. // IE opacity filter fix
  5106. if (IE_OPACITY && after.filter && !before.filter) {
  5107. before.filter = 'alpha(opacity=100)';
  5108. }
  5109. // adjusting the border style
  5110. check_border_styles(element, before, after);
  5111. // cleaing up the list
  5112. for (key in after) {
  5113. // proprocessing colors
  5114. if (after[key] !== before[key] && /color/i.test(key)) {
  5115. if (Browser_Opera) {
  5116. after[key] = after[key].replace(/"/g, '');
  5117. before[key] = before[key].replace(/"/g, '');
  5118. }
  5119. if (!is_transparent(after[key])) { after[key] = after[key].toRgb(); }
  5120. if (!is_transparent(before[key])) { before[key] = before[key].toRgb(); }
  5121. if (!after[key] || !before[key]) { after[key] = before[key] = ''; }
  5122. }
  5123. // filling up the missing size
  5124. if (/\d/.test(after[key]) && !/\d/.test(before[key])) {
  5125. before[key] = after[key].replace(/[\d\.\-]+/g, '0');
  5126. }
  5127. // removing unprocessable keys
  5128. if (after[key] === before[key] || !/\d/.test(before[key]) || !/\d/.test(after[key])) {
  5129. delete(after[key]);
  5130. delete(before[key]);
  5131. }
  5132. }
  5133. }
  5134. // cloning the element current styles hash
  5135. function clone_style(element, keys) {
  5136. var i=0, len = keys.length, style = element.computedStyles(), clean = {}, key;
  5137. for (; i < len; i++) {
  5138. key = keys[i];
  5139. if (key in style) {
  5140. clean[key] = ''+ style[key];
  5141. // libwebkit bug fix for in case of languages pack applied
  5142. if (key === 'opacity') {
  5143. clean[key] = clean[key].replace(',', '.');
  5144. }
  5145. }
  5146. }
  5147. return clean;
  5148. }
  5149. // calculating the end styles hash
  5150. function end_style(element, style, keys) {
  5151. var dummy = element.clone()
  5152. .setStyle('position:absolute;z-index:-1;visibility:hidden')
  5153. .setWidth(element.size().x)
  5154. .setStyle(style), after;
  5155. if (element.parent()) {
  5156. element.insert(dummy, 'before');
  5157. }
  5158. after = clone_style(dummy, keys);
  5159. dummy.remove();
  5160. return after;
  5161. }
  5162. /**
  5163. * the elements hightlighting effect
  5164. *
  5165. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  5166. */
  5167. Fx.Highlight = new Class(Fx.Morph, {
  5168. extend: {
  5169. Options: Object.merge(Fx.Options, {
  5170. color: '#FF8',
  5171. transition: 'Exp'
  5172. })
  5173. },
  5174. // protected
  5175. /**
  5176. * starts the transition
  5177. *
  5178. * @param high String the hightlight color
  5179. * @param back String optional fallback color
  5180. * @return self
  5181. */
  5182. prepare: function(start, end) {
  5183. var element = this.element,
  5184. element_style = element._.style,
  5185. style_name = 'backgroundColor',
  5186. end_color = end || element.getStyle(style_name);
  5187. if (is_transparent(end_color)) {
  5188. this.onFinish(function() { element_style[style_name] = 'transparent'; });
  5189. // trying to find the end color
  5190. end_color = [element].concat(element.parents())
  5191. .map('getStyle', style_name)
  5192. .reject(is_transparent)
  5193. .compact().first() || '#FFF';
  5194. }
  5195. element_style[style_name] = (start || this.options.color);
  5196. return this.$super({backgroundColor: end_color});
  5197. }
  5198. });
  5199. /**
  5200. * this is a superclass for the bidirectional effects
  5201. *
  5202. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  5203. */
  5204. Fx.Twin = new Class(Fx.Morph, {
  5205. /**
  5206. * hides the element if it meant to be switched off
  5207. *
  5208. * @return Fx self
  5209. */
  5210. finish: function() {
  5211. if (this.how === 'out') {
  5212. // calling 'prototype' to prevent circular calls from subclasses
  5213. Element.prototype.hide.call(this.element);
  5214. }
  5215. return this.$super();
  5216. },
  5217. // protected
  5218. /**
  5219. * assigns the direction of the effect in or out
  5220. *
  5221. * @param String 'in', 'out' or 'toggle', 'toggle' by default
  5222. */
  5223. setHow: function(how) {
  5224. this.how = how || 'toggle';
  5225. if (this.how === 'toggle') {
  5226. this.how = this.element.visible() ? 'out' : 'in';
  5227. }
  5228. }
  5229. });
  5230. /**
  5231. * the slide effects wrapper
  5232. *
  5233. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  5234. */
  5235. Fx.Slide = new Class(Fx.Twin, {
  5236. extend: {
  5237. Options: Object.merge(Fx.Options, {
  5238. direction: 'top'
  5239. })
  5240. },
  5241. // protected
  5242. prepare: function(how) {
  5243. this.setHow(how);
  5244. // calling 'prototype' to prevent circular calls from subclasses
  5245. var element = Element.prototype.show.call(this.element),
  5246. element_style = element._.style,
  5247. old_styles = Object.only(
  5248. element_style,
  5249. 'overflow', 'width', 'height',
  5250. 'marginTop', 'marginLeft'
  5251. );
  5252. function restore_styles() {
  5253. for (var key in old_styles) {
  5254. element_style[key] = old_styles[key];
  5255. }
  5256. }
  5257. this.onFinish(restore_styles).onCancel(restore_styles);
  5258. element_style.overflow = 'hidden';
  5259. return this.$super(fx_slide_prepare_styles(
  5260. element_style,
  5261. element.size(),
  5262. this.options.direction,
  5263. this.how
  5264. ));
  5265. }
  5266. });
  5267. function fx_slide_prepare_styles(element_style, size, direction, how) {
  5268. var style = {},
  5269. margin_left = element_style.marginLeft.toFloat() || 0,
  5270. margin_top = element_style.marginTop.toFloat() || 0,
  5271. to_right = direction === 'right',
  5272. to_bottom = direction === 'bottom',
  5273. vertical = direction === 'top' || to_bottom;
  5274. if (how === 'out') {
  5275. style[vertical ? 'height' : 'width'] = '0px';
  5276. if (to_right) {
  5277. style.marginLeft = margin_left + size.x+'px';
  5278. } else if (to_bottom) {
  5279. style.marginTop = margin_top + size.y +'px';
  5280. }
  5281. } else {
  5282. if (vertical) {
  5283. style.height = size.y + 'px';
  5284. element_style.height = '0px';
  5285. } else {
  5286. style.width = size.x + 'px';
  5287. element_style.width = '0px';
  5288. }
  5289. if (to_right) {
  5290. style.marginLeft = margin_left + 'px';
  5291. element_style.marginLeft = margin_left + size.x + 'px';
  5292. } else if (to_bottom) {
  5293. style.marginTop = margin_top + 'px';
  5294. element_style.marginTop = margin_top + size.y + 'px';
  5295. }
  5296. }
  5297. return style;
  5298. }
  5299. /**
  5300. * The opacity effects wrapper
  5301. *
  5302. * Copyright (C) 2008-2011 Nikolay V. Nemshilov
  5303. */
  5304. Fx.Fade = new Class(Fx.Twin, {
  5305. prepare: function(how) {
  5306. this.setHow(how);
  5307. if (this.how === 'in') {
  5308. // calling 'prototype' to prevent circular calls from subclasses
  5309. Element.prototype.show.call(this.element.setStyle({opacity: 0}));
  5310. }
  5311. return this.$super({opacity: this.how === 'in' ? 1 : 0});
  5312. }
  5313. });
  5314. /**
  5315. * An abstract attributes based Fx
  5316. *
  5317. * Copyright (C) 2010 Nikolay Nemshilov
  5318. */
  5319. Fx.Attr = new Class(Fx, {
  5320. prepare: function(attrs) {
  5321. this.before = {};
  5322. this.after = attrs;
  5323. var key, element = this.element._;
  5324. for (key in attrs) {
  5325. this.before[key] = element[key];
  5326. }
  5327. },
  5328. render: function(delta) {
  5329. var key, element = this.element._, before = this.before;
  5330. for (key in before) {
  5331. element[key] = before[key] + (this.after[key] - before[key]) * delta;
  5332. }
  5333. }
  5334. });
  5335. /**
  5336. * A smooth scrolling visual effect
  5337. *
  5338. * Copyright (C) 2009-2011 Nikolay Nemshilov
  5339. */
  5340. Fx.Scroll = new Class(Fx.Attr, {
  5341. initialize: function(element, options) {
  5342. element = $(element);
  5343. // swapping the actual scrollable when it's the window
  5344. this.$super(
  5345. element instanceof Window ?
  5346. element._.document[
  5347. Browser.WebKit ? 'body' : 'documentElement'
  5348. ] : element,
  5349. options
  5350. );
  5351. },
  5352. prepare: function(value) {
  5353. var attrs = {};
  5354. if ('x' in value) { attrs.scrollLeft = value.x; }
  5355. if ('y' in value) { attrs.scrollTop = value.y; }
  5356. this.$super(attrs);
  5357. }
  5358. });
  5359. /**
  5360. * this module handles the work with cookies
  5361. *
  5362. * Credits:
  5363. * Most things in the unit are take from
  5364. * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
  5365. *
  5366. * Copyright (C) 2008-2010 Nikolay V. Nemshilov
  5367. */
  5368. var Cookie = RightJS.Cookie = new Class({
  5369. include: Options,
  5370. extend: {
  5371. // sets the cookie
  5372. set: function(name, value, options) {
  5373. return new this(name, options).set(value);
  5374. },
  5375. // gets the cookie
  5376. get: function(name, options) {
  5377. return new this(name, options).get();
  5378. },
  5379. // deletes the cookie
  5380. remove: function(name, options) {
  5381. return new this(name, options).remove();
  5382. },
  5383. // checks if the cookies are enabled
  5384. enabled: function() {
  5385. document.cookie = "__t=1";
  5386. return document.cookie.indexOf("__t=1")!=-1;
  5387. },
  5388. // some basic options
  5389. Options: {
  5390. secure: false,
  5391. document: document
  5392. }
  5393. },
  5394. /**
  5395. * constructor
  5396. * @param String cookie name
  5397. * @param Object options
  5398. * @return void
  5399. */
  5400. initialize: function(name, options) {
  5401. this.name = name;
  5402. this.setOptions(options);
  5403. },
  5404. /**
  5405. * sets the cookie with the name
  5406. *
  5407. * @param mixed value
  5408. * @return Cookie this
  5409. */
  5410. set: function(data) {
  5411. if (!isString(data)) { data = JSON.stringify(data); }
  5412. var value = encodeURIComponent(data), options = this.options;
  5413. if (options.domain) { value += '; domain=' + options.domain; }
  5414. if (options.path) { value += '; path=' + options.path; }
  5415. if (options.duration) {
  5416. var date = new Date();
  5417. date.setTime(date.getTime() + options.duration * 24 * 60 * 60 * 1000);
  5418. value += '; expires=' + date.toGMTString();
  5419. }
  5420. if (options.secure) { value += '; secure'; }
  5421. options.document.cookie = this.name + '=' + value;
  5422. return this;
  5423. },
  5424. /**
  5425. * searches for a cookie with the name
  5426. *
  5427. * @return mixed saved value or null if nothing found
  5428. */
  5429. get: function() {
  5430. var value = this.options.document.cookie.match(
  5431. '(?:^|;)\\s*' + RegExp.escape(this.name) + '=([^;]*)'
  5432. );
  5433. if (value) {
  5434. value = decodeURIComponent(value[1]);
  5435. try { value = JSON.parse(value); }
  5436. catch (e) {}
  5437. }
  5438. return value || null;
  5439. },
  5440. /**
  5441. * removes the cookie
  5442. *
  5443. * @return Cookie this
  5444. */
  5445. remove: function() {
  5446. this.options.duration = -1;
  5447. return this.set('');
  5448. }
  5449. });
  5450. // globalizing the top-level variables
  5451. $ext(window, Object.without(RightJS, 'version', 'modules'));
  5452. return RightJS;
  5453. })(window, document, Object, Array, String, Function, Number, Math);
  5454. /**
  5455. * The old browsers support patch loading script
  5456. * will be included in the core file when it's built
  5457. * with the no-olds option
  5458. *
  5459. * Basically it just checks all the script tags on the page
  5460. * finds the core inclusion tag and uses it's src attribute
  5461. * to dynamically load the olds patch
  5462. *
  5463. * Copyright (C) 2009-2011 Nikolay V. Nemshilov
  5464. */
  5465. if (RightJS.Browser.OLD) {
  5466. (function(d) {
  5467. var script = d.createElement('script'),
  5468. scripts = d.getElementsByTagName('script'),
  5469. rjs_spt = scripts[scripts.length - 1];
  5470. script.src = rjs_spt.src.replace(/(^|\/)(right)([^\/]+)$/, '$1$2-olds$3');
  5471. rjs_spt.parentNode.appendChild(script);
  5472. })(document);
  5473. }