Nessuna descrizione

fullcalendar.js 215KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593
  1. /*!
  2. * FullCalendar v1.6.4
  3. * Docs & License: http://arshaw.com/fullcalendar/
  4. * (c) 2013 Adam Shaw
  5. */
  6. /*
  7. * Use fullcalendar.css for basic styling.
  8. * For event drag & drop, requires jQuery UI draggable.
  9. * For event resizing, requires jQuery UI resizable.
  10. */
  11. (function($, undefined) {
  12. ;;
  13. var defaults = {
  14. // display
  15. defaultView: 'month',
  16. aspectRatio: 1.35,
  17. header: {
  18. left: 'prev,next today',
  19. center: 'title',
  20. right: 'month,agendaWeek,agendaDay'
  21. },
  22. weekends: true,
  23. weekNumbers: false,
  24. weekNumberCalculation: 'iso',
  25. weekNumberTitle: 'W',
  26. // editing
  27. //editable: false,
  28. //disableDragging: false,
  29. //disableResizing: false,
  30. allDayDefault: true,
  31. ignoreTimezone: true,
  32. // event ajax
  33. lazyFetching: true,
  34. startParam: 'start',
  35. endParam: 'end',
  36. // time formats
  37. titleFormat: {
  38. month: 'yyyy年MMMM月',
  39. week: "yyyy年MMMM月d{'—'d日}",
  40. day: 'yyyy年MMMM月d日 dddd'
  41. },
  42. columnFormat: {
  43. month: 'ddd',
  44. week: 'ddd M/d',
  45. day: 'dddd M/d'
  46. },
  47. timeFormat: { // for event elements
  48. "":"H:mm" // default
  49. },
  50. // locale
  51. isRTL: false,
  52. firstDay:1,
  53. monthNames: ['01','02','03','04','05','06','07','08','09','10','11','12'],
  54. monthNamesShort: ['01','02','03','04','05','06','07','08','09','10','11','12'],
  55. dayNames: ['周日','周一','周二','周三','周四','周五','周六'],
  56. dayNamesShort: ['日','一','二','三','四','五','六'],
  57. buttonText: {
  58. prev: "<span class='fc-text-arrow'>&lsaquo;</span>",
  59. next: "<span class='fc-text-arrow'>&rsaquo;</span>",
  60. prevYear: "<span class='fc-text-arrow'>&laquo;</span>",
  61. nextYear: "<span class='fc-text-arrow'>&raquo;</span>",
  62. today: '今天',
  63. month: '月',
  64. week: '周',
  65. day: '日'
  66. },
  67. // jquery-ui theming
  68. theme: false,
  69. buttonIcons: {
  70. prev: 'circle-triangle-w',
  71. next: 'circle-triangle-e'
  72. },
  73. //selectable: false,
  74. unselectAuto: true,
  75. dropAccept: '*',
  76. handleWindowResize: true
  77. };
  78. // right-to-left defaults
  79. var rtlDefaults = {
  80. header: {
  81. left: 'next,prev today',
  82. center: '',
  83. right: 'title'
  84. },
  85. buttonText: {
  86. prev: "<span class='fc-text-arrow'>&rsaquo;</span>",
  87. next: "<span class='fc-text-arrow'>&lsaquo;</span>",
  88. prevYear: "<span class='fc-text-arrow'>&raquo;</span>",
  89. nextYear: "<span class='fc-text-arrow'>&laquo;</span>"
  90. },
  91. buttonIcons: {
  92. prev: 'circle-triangle-e',
  93. next: 'circle-triangle-w'
  94. }
  95. };
  96. ;;
  97. var fc = $.fullCalendar = { version: "1.6.4" };
  98. var fcViews = fc.views = {};
  99. $.fn.fullCalendar = function(options) {
  100. // method calling
  101. if (typeof options == 'string') {
  102. var args = Array.prototype.slice.call(arguments, 1);
  103. var res;
  104. this.each(function() {
  105. var calendar = $.data(this, 'fullCalendar');
  106. if (calendar && $.isFunction(calendar[options])) {
  107. var r = calendar[options].apply(calendar, args);
  108. if (res === undefined) {
  109. res = r;
  110. }
  111. if (options == 'destroy') {
  112. $.removeData(this, 'fullCalendar');
  113. }
  114. }
  115. });
  116. if (res !== undefined) {
  117. return res;
  118. }
  119. return this;
  120. }
  121. options = options || {};
  122. // would like to have this logic in EventManager, but needs to happen before options are recursively extended
  123. var eventSources = options.eventSources || [];
  124. delete options.eventSources;
  125. if (options.events) {
  126. eventSources.push(options.events);
  127. delete options.events;
  128. }
  129. options = $.extend(true, {},
  130. defaults,
  131. (options.isRTL || options.isRTL===undefined && defaults.isRTL) ? rtlDefaults : {},
  132. options
  133. );
  134. this.each(function(i, _element) {
  135. var element = $(_element);
  136. var calendar = new Calendar(element, options, eventSources);
  137. element.data('fullCalendar', calendar); // TODO: look into memory leak implications
  138. calendar.render();
  139. });
  140. return this;
  141. };
  142. // function for adding/overriding defaults
  143. function setDefaults(d) {
  144. $.extend(true, defaults, d);
  145. }
  146. ;;
  147. function Calendar(element, options, eventSources) {
  148. var t = this;
  149. // exports
  150. t.options = options;
  151. t.render = render;
  152. t.destroy = destroy;
  153. t.refetchEvents = refetchEvents;
  154. t.reportEvents = reportEvents;
  155. t.reportEventChange = reportEventChange;
  156. t.rerenderEvents = rerenderEvents;
  157. t.changeView = changeView;
  158. t.select = select;
  159. t.unselect = unselect;
  160. t.prev = prev;
  161. t.next = next;
  162. t.prevYear = prevYear;
  163. t.nextYear = nextYear;
  164. t.today = today;
  165. t.gotoDate = gotoDate;
  166. t.incrementDate = incrementDate;
  167. t.formatDate = function(format, date) { return formatDate(format, date, options) };
  168. t.formatDates = function(format, date1, date2) { return formatDates(format, date1, date2, options) };
  169. t.getDate = getDate;
  170. t.getView = getView;
  171. t.option = option;
  172. t.trigger = trigger;
  173. // imports
  174. EventManager.call(t, options, eventSources);
  175. var isFetchNeeded = t.isFetchNeeded;
  176. var fetchEvents = t.fetchEvents;
  177. // locals
  178. var _element = element[0];
  179. var header;
  180. var headerElement;
  181. var content;
  182. var tm; // for making theme classes
  183. var currentView;
  184. var elementOuterWidth;
  185. var suggestedViewHeight;
  186. var resizeUID = 0;
  187. var ignoreWindowResize = 0;
  188. var date = new Date();
  189. var events = [];
  190. var _dragElement;
  191. /* Main Rendering
  192. -----------------------------------------------------------------------------*/
  193. setYMD(date, options.year, options.month, options.date);
  194. function render(inc) {
  195. if (!content) {
  196. initialRender();
  197. }
  198. else if (elementVisible()) {
  199. // mainly for the public API
  200. calcSize();
  201. _renderView(inc);
  202. }
  203. }
  204. function initialRender() {
  205. tm = options.theme ? 'ui' : 'fc';
  206. element.addClass('fc');
  207. if (options.isRTL) {
  208. element.addClass('fc-rtl');
  209. }
  210. else {
  211. element.addClass('fc-ltr');
  212. }
  213. if (options.theme) {
  214. element.addClass('ui-widget');
  215. }
  216. content = $("<div class='fc-content' style='position:relative'/>")
  217. .prependTo(element);
  218. header = new Header(t, options);
  219. headerElement = header.render();
  220. if (headerElement) {
  221. element.prepend(headerElement);
  222. }
  223. changeView(options.defaultView);
  224. if (options.handleWindowResize) {
  225. $(window).resize(windowResize);
  226. }
  227. // needed for IE in a 0x0 iframe, b/c when it is resized, never triggers a windowResize
  228. if (!bodyVisible()) {
  229. lateRender();
  230. }
  231. }
  232. // called when we know the calendar couldn't be rendered when it was initialized,
  233. // but we think it's ready now
  234. function lateRender() {
  235. setTimeout(function() { // IE7 needs this so dimensions are calculated correctly
  236. if (!currentView.start && bodyVisible()) { // !currentView.start makes sure this never happens more than once
  237. renderView();
  238. }
  239. },0);
  240. }
  241. function destroy() {
  242. if (currentView) {
  243. trigger('viewDestroy', currentView, currentView, currentView.element);
  244. currentView.triggerEventDestroy();
  245. }
  246. $(window).unbind('resize', windowResize);
  247. header.destroy();
  248. content.remove();
  249. element.removeClass('fc fc-rtl ui-widget');
  250. }
  251. function elementVisible() {
  252. return element.is(':visible');
  253. }
  254. function bodyVisible() {
  255. return $('body').is(':visible');
  256. }
  257. /* View Rendering
  258. -----------------------------------------------------------------------------*/
  259. function changeView(newViewName) {
  260. if (!currentView || newViewName != currentView.name) {
  261. _changeView(newViewName);
  262. }
  263. }
  264. function _changeView(newViewName) {
  265. ignoreWindowResize++;
  266. if (currentView) {
  267. trigger('viewDestroy', currentView, currentView, currentView.element);
  268. unselect();
  269. currentView.triggerEventDestroy(); // trigger 'eventDestroy' for each event
  270. freezeContentHeight();
  271. currentView.element.remove();
  272. header.deactivateButton(currentView.name);
  273. }
  274. header.activateButton(newViewName);
  275. currentView = new fcViews[newViewName](
  276. $("<div class='fc-view fc-view-" + newViewName + "' style='position:relative'/>")
  277. .appendTo(content),
  278. t // the calendar object
  279. );
  280. renderView();
  281. unfreezeContentHeight();
  282. ignoreWindowResize--;
  283. }
  284. function renderView(inc) {
  285. if (
  286. !currentView.start || // never rendered before
  287. inc || date < currentView.start || date >= currentView.end // or new date range
  288. ) {
  289. if (elementVisible()) {
  290. _renderView(inc);
  291. }
  292. }
  293. }
  294. function _renderView(inc) { // assumes elementVisible
  295. ignoreWindowResize++;
  296. if (currentView.start) { // already been rendered?
  297. trigger('viewDestroy', currentView, currentView, currentView.element);
  298. unselect();
  299. clearEvents();
  300. }
  301. freezeContentHeight();
  302. currentView.render(date, inc || 0); // the view's render method ONLY renders the skeleton, nothing else
  303. setSize();
  304. unfreezeContentHeight();
  305. (currentView.afterRender || noop)();
  306. updateTitle();
  307. updateTodayButton();
  308. trigger('viewRender', currentView, currentView, currentView.element);
  309. currentView.trigger('viewDisplay', _element); // deprecated
  310. ignoreWindowResize--;
  311. getAndRenderEvents();
  312. }
  313. /* Resizing
  314. -----------------------------------------------------------------------------*/
  315. function updateSize() {
  316. if (elementVisible()) {
  317. unselect();
  318. clearEvents();
  319. calcSize();
  320. setSize();
  321. renderEvents();
  322. }
  323. }
  324. function calcSize() { // assumes elementVisible
  325. if (options.contentHeight) {
  326. suggestedViewHeight = options.contentHeight;
  327. }
  328. else if (options.height) {
  329. suggestedViewHeight = options.height - (headerElement ? headerElement.height() : 0) - vsides(content);
  330. }
  331. else {
  332. suggestedViewHeight = Math.round(content.width() / Math.max(options.aspectRatio, .5));
  333. }
  334. }
  335. function setSize() { // assumes elementVisible
  336. if (suggestedViewHeight === undefined) {
  337. calcSize(); // for first time
  338. // NOTE: we don't want to recalculate on every renderView because
  339. // it could result in oscillating heights due to scrollbars.
  340. }
  341. ignoreWindowResize++;
  342. currentView.setHeight(suggestedViewHeight);
  343. currentView.setWidth(content.width());
  344. ignoreWindowResize--;
  345. elementOuterWidth = element.outerWidth();
  346. }
  347. function windowResize() {
  348. if (!ignoreWindowResize) {
  349. if (currentView.start) { // view has already been rendered
  350. var uid = ++resizeUID;
  351. setTimeout(function() { // add a delay
  352. if (uid == resizeUID && !ignoreWindowResize && elementVisible()) {
  353. if (elementOuterWidth != (elementOuterWidth = element.outerWidth())) {
  354. ignoreWindowResize++; // in case the windowResize callback changes the height
  355. updateSize();
  356. currentView.trigger('windowResize', _element);
  357. ignoreWindowResize--;
  358. }
  359. }
  360. }, 200);
  361. }else{
  362. // calendar must have been initialized in a 0x0 iframe that has just been resized
  363. lateRender();
  364. }
  365. }
  366. }
  367. /* Event Fetching/Rendering
  368. -----------------------------------------------------------------------------*/
  369. // TODO: going forward, most of this stuff should be directly handled by the view
  370. function refetchEvents() { // can be called as an API method
  371. clearEvents();
  372. fetchAndRenderEvents();
  373. }
  374. function rerenderEvents(modifiedEventID) { // can be called as an API method
  375. clearEvents();
  376. renderEvents(modifiedEventID);
  377. }
  378. function renderEvents(modifiedEventID) { // TODO: remove modifiedEventID hack
  379. if (elementVisible()) {
  380. currentView.setEventData(events); // for View.js, TODO: unify with renderEvents
  381. currentView.renderEvents(events, modifiedEventID); // actually render the DOM elements
  382. currentView.trigger('eventAfterAllRender');
  383. }
  384. }
  385. function clearEvents() {
  386. currentView.triggerEventDestroy(); // trigger 'eventDestroy' for each event
  387. currentView.clearEvents(); // actually remove the DOM elements
  388. currentView.clearEventData(); // for View.js, TODO: unify with clearEvents
  389. }
  390. function getAndRenderEvents() {
  391. if (!options.lazyFetching || isFetchNeeded(currentView.visStart, currentView.visEnd)) {
  392. fetchAndRenderEvents();
  393. }
  394. else {
  395. renderEvents();
  396. }
  397. }
  398. function fetchAndRenderEvents() {
  399. fetchEvents(currentView.visStart, currentView.visEnd);
  400. // ... will call reportEvents
  401. // ... which will call renderEvents
  402. }
  403. // called when event data arrives
  404. function reportEvents(_events) {
  405. events = _events;
  406. renderEvents();
  407. }
  408. // called when a single event's data has been changed
  409. function reportEventChange(eventID) {
  410. rerenderEvents(eventID);
  411. }
  412. /* Header Updating
  413. -----------------------------------------------------------------------------*/
  414. function updateTitle() {
  415. header.updateTitle(currentView.title);
  416. }
  417. function updateTodayButton() {
  418. var today = new Date();
  419. if (today >= currentView.start && today < currentView.end) {
  420. header.disableButton('today');
  421. }
  422. else {
  423. header.enableButton('today');
  424. }
  425. }
  426. /* Selection
  427. -----------------------------------------------------------------------------*/
  428. function select(start, end, allDay) {
  429. currentView.select(start, end, allDay===undefined ? true : allDay);
  430. }
  431. function unselect() { // safe to be called before renderView
  432. if (currentView) {
  433. currentView.unselect();
  434. }
  435. }
  436. /* Date
  437. -----------------------------------------------------------------------------*/
  438. function prev() {
  439. renderView(-1);
  440. }
  441. function next() {
  442. renderView(1);
  443. }
  444. function prevYear() {
  445. addYears(date, -1);
  446. renderView();
  447. }
  448. function nextYear() {
  449. addYears(date, 1);
  450. renderView();
  451. }
  452. function today() {
  453. date = new Date();
  454. renderView();
  455. }
  456. function gotoDate(year, month, dateOfMonth) {
  457. if (year instanceof Date) {
  458. date = cloneDate(year); // provided 1 argument, a Date
  459. }else{
  460. setYMD(date, year, month, dateOfMonth);
  461. }
  462. renderView();
  463. }
  464. function incrementDate(years, months, days) {
  465. if (years !== undefined) {
  466. addYears(date, years);
  467. }
  468. if (months !== undefined) {
  469. addMonths(date, months);
  470. }
  471. if (days !== undefined) {
  472. addDays(date, days);
  473. }
  474. renderView();
  475. }
  476. function getDate() {
  477. return cloneDate(date);
  478. }
  479. /* Height "Freezing"
  480. -----------------------------------------------------------------------------*/
  481. function freezeContentHeight() {
  482. content.css({
  483. width: '100%',
  484. height: content.height(),
  485. overflow: 'hidden'
  486. });
  487. }
  488. function unfreezeContentHeight() {
  489. content.css({
  490. width: '',
  491. height: '',
  492. overflow: ''
  493. });
  494. }
  495. /* Misc
  496. -----------------------------------------------------------------------------*/
  497. function getView() {
  498. return currentView;
  499. }
  500. function option(name, value) {
  501. if (value === undefined) {
  502. return options[name];
  503. }
  504. if (name == 'height' || name == 'contentHeight' || name == 'aspectRatio') {
  505. options[name] = value;
  506. updateSize();
  507. }
  508. }
  509. function trigger(name, thisObj) {
  510. if (options[name]) {
  511. return options[name].apply(
  512. thisObj || _element,
  513. Array.prototype.slice.call(arguments, 2)
  514. );
  515. }
  516. }
  517. /* External Dragging
  518. ------------------------------------------------------------------------*/
  519. if (options.droppable) {
  520. $(document)
  521. .bind('dragstart', function(ev, ui) {
  522. var _e = ev.target;
  523. var e = $(_e);
  524. if (!e.parents('.fc').length) { // not already inside a calendar
  525. var accept = options.dropAccept;
  526. if ($.isFunction(accept) ? accept.call(_e, e) : e.is(accept)) {
  527. _dragElement = _e;
  528. currentView.dragStart(_dragElement, ev, ui);
  529. }
  530. }
  531. })
  532. .bind('dragstop', function(ev, ui) {
  533. if (_dragElement) {
  534. currentView.dragStop(_dragElement, ev, ui);
  535. _dragElement = null;
  536. }
  537. });
  538. }
  539. }
  540. ;;
  541. function Header(calendar, options) {
  542. var t = this;
  543. // exports
  544. t.render = render;
  545. t.destroy = destroy;
  546. t.updateTitle = updateTitle;
  547. t.activateButton = activateButton;
  548. t.deactivateButton = deactivateButton;
  549. t.disableButton = disableButton;
  550. t.enableButton = enableButton;
  551. // locals
  552. var element = $([]);
  553. var tm;
  554. function render() {
  555. tm = options.theme ? 'ui' : 'fc';
  556. var sections = options.header;
  557. if (sections) {
  558. element = $("<table class='fc-header' style='width:100%'/>")
  559. .append(
  560. $("<tr/>")
  561. .append(renderSection('left'))
  562. .append(renderSection('center'))
  563. .append(renderSection('right'))
  564. );
  565. return element;
  566. }
  567. }
  568. function destroy() {
  569. element.remove();
  570. }
  571. function renderSection(position) {
  572. var e = $("<td class='fc-header-" + position + "'/>");
  573. var buttonStr = options.header[position];
  574. if (buttonStr) {
  575. $.each(buttonStr.split(' '), function(i) {
  576. if (i > 0) {
  577. e.append("<span class='fc-header-space'/>");
  578. }
  579. var prevButton;
  580. $.each(this.split(','), function(j, buttonName) {
  581. if (buttonName == 'title') {
  582. e.append("<span class='fc-header-title'><h2>&nbsp;</h2></span>");
  583. if (prevButton) {
  584. prevButton.addClass(tm + '-corner-right');
  585. }
  586. prevButton = null;
  587. }else{
  588. var buttonClick;
  589. if (calendar[buttonName]) {
  590. buttonClick = calendar[buttonName]; // calendar method
  591. }
  592. else if (fcViews[buttonName]) {
  593. buttonClick = function() {
  594. button.removeClass(tm + '-state-hover'); // forget why
  595. calendar.changeView(buttonName);
  596. };
  597. }
  598. if (buttonClick) {
  599. var icon = options.theme ? smartProperty(options.buttonIcons, buttonName) : null; // why are we using smartProperty here?
  600. var text = smartProperty(options.buttonText, buttonName); // why are we using smartProperty here?
  601. var button = $(
  602. "<span class='fc-button fc-button-" + buttonName + " " + tm + "-state-default'>" +
  603. (icon ?
  604. "<span class='fc-icon-wrap'>" +
  605. "<span class='ui-icon ui-icon-" + icon + "'/>" +
  606. "</span>" :
  607. text
  608. ) +
  609. "</span>"
  610. )
  611. .click(function() {
  612. if (!button.hasClass(tm + '-state-disabled')) {
  613. buttonClick();
  614. }
  615. })
  616. .mousedown(function() {
  617. button
  618. .not('.' + tm + '-state-active')
  619. .not('.' + tm + '-state-disabled')
  620. .addClass(tm + '-state-down');
  621. })
  622. .mouseup(function() {
  623. button.removeClass(tm + '-state-down');
  624. })
  625. .hover(
  626. function() {
  627. button
  628. .not('.' + tm + '-state-active')
  629. .not('.' + tm + '-state-disabled')
  630. .addClass(tm + '-state-hover');
  631. },
  632. function() {
  633. button
  634. .removeClass(tm + '-state-hover')
  635. .removeClass(tm + '-state-down');
  636. }
  637. )
  638. .appendTo(e);
  639. disableTextSelection(button);
  640. if (!prevButton) {
  641. button.addClass(tm + '-corner-left');
  642. }
  643. prevButton = button;
  644. }
  645. }
  646. });
  647. if (prevButton) {
  648. prevButton.addClass(tm + '-corner-right');
  649. }
  650. });
  651. }
  652. return e;
  653. }
  654. function updateTitle(html) {
  655. element.find('h2')
  656. .html(html);
  657. }
  658. function activateButton(buttonName) {
  659. element.find('span.fc-button-' + buttonName)
  660. .addClass(tm + '-state-active');
  661. }
  662. function deactivateButton(buttonName) {
  663. element.find('span.fc-button-' + buttonName)
  664. .removeClass(tm + '-state-active');
  665. }
  666. function disableButton(buttonName) {
  667. element.find('span.fc-button-' + buttonName)
  668. .addClass(tm + '-state-disabled');
  669. }
  670. function enableButton(buttonName) {
  671. element.find('span.fc-button-' + buttonName)
  672. .removeClass(tm + '-state-disabled');
  673. }
  674. }
  675. ;;
  676. fc.sourceNormalizers = [];
  677. fc.sourceFetchers = [];
  678. var ajaxDefaults = {
  679. dataType: 'json',
  680. cache: false
  681. };
  682. var eventGUID = 1;
  683. function EventManager(options, _sources) {
  684. var t = this;
  685. // exports
  686. t.isFetchNeeded = isFetchNeeded;
  687. t.fetchEvents = fetchEvents;
  688. t.addEventSource = addEventSource;
  689. t.removeEventSource = removeEventSource;
  690. t.updateEvent = updateEvent;
  691. t.renderEvent = renderEvent;
  692. t.removeEvents = removeEvents;
  693. t.clientEvents = clientEvents;
  694. t.normalizeEvent = normalizeEvent;
  695. // imports
  696. var trigger = t.trigger;
  697. var getView = t.getView;
  698. var reportEvents = t.reportEvents;
  699. // locals
  700. var stickySource = { events: [] };
  701. var sources = [ stickySource ];
  702. var rangeStart, rangeEnd;
  703. var currentFetchID = 0;
  704. var pendingSourceCnt = 0;
  705. var loadingLevel = 0;
  706. var cache = [];
  707. for (var i=0; i<_sources.length; i++) {
  708. _addEventSource(_sources[i]);
  709. }
  710. /* Fetching
  711. -----------------------------------------------------------------------------*/
  712. function isFetchNeeded(start, end) {
  713. return !rangeStart || start < rangeStart || end > rangeEnd;
  714. }
  715. function fetchEvents(start, end) {
  716. rangeStart = start;
  717. rangeEnd = end;
  718. cache = [];
  719. var fetchID = ++currentFetchID;
  720. var len = sources.length;
  721. pendingSourceCnt = len;
  722. for (var i=0; i<len; i++) {
  723. fetchEventSource(sources[i], fetchID);
  724. }
  725. }
  726. function fetchEventSource(source, fetchID) {
  727. _fetchEventSource(source, function(events) {
  728. if (fetchID == currentFetchID) {
  729. if (events) {
  730. if (options.eventDataTransform) {
  731. events = $.map(events, options.eventDataTransform);
  732. }
  733. if (source.eventDataTransform) {
  734. events = $.map(events, source.eventDataTransform);
  735. }
  736. // TODO: this technique is not ideal for static array event sources.
  737. // For arrays, we'll want to process all events right in the beginning, then never again.
  738. for (var i=0; i<events.length; i++) {
  739. events[i].source = source;
  740. normalizeEvent(events[i]);
  741. }
  742. cache = cache.concat(events);
  743. }
  744. pendingSourceCnt--;
  745. if (!pendingSourceCnt) {
  746. reportEvents(cache);
  747. }
  748. }
  749. });
  750. }
  751. function _fetchEventSource(source, callback) {
  752. var i;
  753. var fetchers = fc.sourceFetchers;
  754. var res;
  755. for (i=0; i<fetchers.length; i++) {
  756. res = fetchers[i](source, rangeStart, rangeEnd, callback);
  757. if (res === true) {
  758. // the fetcher is in charge. made its own async request
  759. return;
  760. }
  761. else if (typeof res == 'object') {
  762. // the fetcher returned a new source. process it
  763. _fetchEventSource(res, callback);
  764. return;
  765. }
  766. }
  767. var events = source.events;
  768. if (events) {
  769. if ($.isFunction(events)) {
  770. pushLoading();
  771. events(cloneDate(rangeStart), cloneDate(rangeEnd), function(events) {
  772. callback(events);
  773. popLoading();
  774. });
  775. }
  776. else if ($.isArray(events)) {
  777. callback(events);
  778. }
  779. else {
  780. callback();
  781. }
  782. }else{
  783. var url = source.url;
  784. if (url) {
  785. var success = source.success;
  786. var error = source.error;
  787. var complete = source.complete;
  788. // retrieve any outbound GET/POST $.ajax data from the options
  789. var customData;
  790. if ($.isFunction(source.data)) {
  791. // supplied as a function that returns a key/value object
  792. customData = source.data();
  793. }
  794. else {
  795. // supplied as a straight key/value object
  796. customData = source.data;
  797. }
  798. // use a copy of the custom data so we can modify the parameters
  799. // and not affect the passed-in object.
  800. var data = $.extend({}, customData || {});
  801. var startParam = firstDefined(source.startParam, options.startParam);
  802. var endParam = firstDefined(source.endParam, options.endParam);
  803. if (startParam) {
  804. data[startParam] = Math.round(+rangeStart / 1000);
  805. }
  806. if (endParam) {
  807. data[endParam] = Math.round(+rangeEnd / 1000);
  808. }
  809. pushLoading();
  810. $.ajax($.extend({}, ajaxDefaults, source, {
  811. data: data,
  812. success: function(events) {
  813. events = events || [];
  814. var res = applyAll(success, this, arguments);
  815. if ($.isArray(res)) {
  816. events = res;
  817. }
  818. callback(events);
  819. },
  820. error: function() {
  821. applyAll(error, this, arguments);
  822. callback();
  823. },
  824. complete: function() {
  825. applyAll(complete, this, arguments);
  826. popLoading();
  827. }
  828. }));
  829. }else{
  830. callback();
  831. }
  832. }
  833. }
  834. /* Sources
  835. -----------------------------------------------------------------------------*/
  836. function addEventSource(source) {
  837. source = _addEventSource(source);
  838. if (source) {
  839. pendingSourceCnt++;
  840. fetchEventSource(source, currentFetchID); // will eventually call reportEvents
  841. }
  842. }
  843. function _addEventSource(source) {
  844. if ($.isFunction(source) || $.isArray(source)) {
  845. source = { events: source };
  846. }
  847. else if (typeof source == 'string') {
  848. source = { url: source };
  849. }
  850. if (typeof source == 'object') {
  851. normalizeSource(source);
  852. sources.push(source);
  853. return source;
  854. }
  855. }
  856. function removeEventSource(source) {
  857. sources = $.grep(sources, function(src) {
  858. return !isSourcesEqual(src, source);
  859. });
  860. // remove all client events from that source
  861. cache = $.grep(cache, function(e) {
  862. return !isSourcesEqual(e.source, source);
  863. });
  864. reportEvents(cache);
  865. }
  866. /* Manipulation
  867. -----------------------------------------------------------------------------*/
  868. function updateEvent(event) { // update an existing event
  869. var i, len = cache.length, e,
  870. defaultEventEnd = getView().defaultEventEnd, // getView???
  871. startDelta = event.start - event._start,
  872. endDelta = event.end ?
  873. (event.end - (event._end || defaultEventEnd(event))) // event._end would be null if event.end
  874. : 0; // was null and event was just resized
  875. for (i=0; i<len; i++) {
  876. e = cache[i];
  877. if (e._id == event._id && e != event) {
  878. e.start = new Date(+e.start + startDelta);
  879. if (event.end) {
  880. if (e.end) {
  881. e.end = new Date(+e.end + endDelta);
  882. }else{
  883. e.end = new Date(+defaultEventEnd(e) + endDelta);
  884. }
  885. }else{
  886. e.end = null;
  887. }
  888. e.title = event.title;
  889. e.url = event.url;
  890. e.allDay = event.allDay;
  891. e.className = event.className;
  892. e.editable = event.editable;
  893. e.color = event.color;
  894. e.backgroundColor = event.backgroundColor;
  895. e.borderColor = event.borderColor;
  896. e.textColor = event.textColor;
  897. normalizeEvent(e);
  898. }
  899. }
  900. normalizeEvent(event);
  901. reportEvents(cache);
  902. }
  903. function renderEvent(event, stick) {
  904. normalizeEvent(event);
  905. if (!event.source) {
  906. if (stick) {
  907. stickySource.events.push(event);
  908. event.source = stickySource;
  909. }
  910. cache.push(event);
  911. }
  912. reportEvents(cache);
  913. }
  914. function removeEvents(filter) {
  915. if (!filter) { // remove all
  916. cache = [];
  917. // clear all array sources
  918. for (var i=0; i<sources.length; i++) {
  919. if ($.isArray(sources[i].events)) {
  920. sources[i].events = [];
  921. }
  922. }
  923. }else{
  924. if (!$.isFunction(filter)) { // an event ID
  925. var id = filter + '';
  926. filter = function(e) {
  927. return e._id == id;
  928. };
  929. }
  930. cache = $.grep(cache, filter, true);
  931. // remove events from array sources
  932. for (var i=0; i<sources.length; i++) {
  933. if ($.isArray(sources[i].events)) {
  934. sources[i].events = $.grep(sources[i].events, filter, true);
  935. }
  936. }
  937. }
  938. reportEvents(cache);
  939. }
  940. function clientEvents(filter) {
  941. if ($.isFunction(filter)) {
  942. return $.grep(cache, filter);
  943. }
  944. else if (filter) { // an event ID
  945. filter += '';
  946. return $.grep(cache, function(e) {
  947. return e._id == filter;
  948. });
  949. }
  950. return cache; // else, return all
  951. }
  952. /* Loading State
  953. -----------------------------------------------------------------------------*/
  954. function pushLoading() {
  955. if (!loadingLevel++) {
  956. trigger('loading', null, true, getView());
  957. }
  958. }
  959. function popLoading() {
  960. if (!--loadingLevel) {
  961. trigger('loading', null, false, getView());
  962. }
  963. }
  964. /* Event Normalization
  965. -----------------------------------------------------------------------------*/
  966. function normalizeEvent(event) {
  967. var source = event.source || {};
  968. var ignoreTimezone = firstDefined(source.ignoreTimezone, options.ignoreTimezone);
  969. event._id = event._id || (event.id === undefined ? '_fc' + eventGUID++ : event.id + '');
  970. if (event.date) {
  971. if (!event.start) {
  972. event.start = event.date;
  973. }
  974. delete event.date;
  975. }
  976. event._start = cloneDate(event.start = parseDate(event.start, ignoreTimezone));
  977. event.end = parseDate(event.end, ignoreTimezone);
  978. if (event.end && event.end <= event.start) {
  979. event.end = null;
  980. }
  981. event._end = event.end ? cloneDate(event.end) : null;
  982. if (event.allDay === undefined) {
  983. event.allDay = firstDefined(source.allDayDefault, options.allDayDefault);
  984. }
  985. if (event.className) {
  986. if (typeof event.className == 'string') {
  987. event.className = event.className.split(/\s+/);
  988. }
  989. }else{
  990. event.className = [];
  991. }
  992. // TODO: if there is no start date, return false to indicate an invalid event
  993. }
  994. /* Utils
  995. ------------------------------------------------------------------------------*/
  996. function normalizeSource(source) {
  997. if (source.className) {
  998. // TODO: repeat code, same code for event classNames
  999. if (typeof source.className == 'string') {
  1000. source.className = source.className.split(/\s+/);
  1001. }
  1002. }else{
  1003. source.className = [];
  1004. }
  1005. var normalizers = fc.sourceNormalizers;
  1006. for (var i=0; i<normalizers.length; i++) {
  1007. normalizers[i](source);
  1008. }
  1009. }
  1010. function isSourcesEqual(source1, source2) {
  1011. return source1 && source2 && getSourcePrimitive(source1) == getSourcePrimitive(source2);
  1012. }
  1013. function getSourcePrimitive(source) {
  1014. return ((typeof source == 'object') ? (source.events || source.url) : '') || source;
  1015. }
  1016. }
  1017. ;;
  1018. fc.addDays = addDays;
  1019. fc.cloneDate = cloneDate;
  1020. fc.parseDate = parseDate;
  1021. fc.parseISO8601 = parseISO8601;
  1022. fc.parseTime = parseTime;
  1023. fc.formatDate = formatDate;
  1024. fc.formatDates = formatDates;
  1025. /* Date Math
  1026. -----------------------------------------------------------------------------*/
  1027. var dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'],
  1028. DAY_MS = 86400000,
  1029. HOUR_MS = 3600000,
  1030. MINUTE_MS = 60000;
  1031. function addYears(d, n, keepTime) {
  1032. d.setFullYear(d.getFullYear() + n);
  1033. if (!keepTime) {
  1034. clearTime(d);
  1035. }
  1036. return d;
  1037. }
  1038. function addMonths(d, n, keepTime) { // prevents day overflow/underflow
  1039. if (+d) { // prevent infinite looping on invalid dates
  1040. var m = d.getMonth() + n,
  1041. check = cloneDate(d);
  1042. check.setDate(1);
  1043. check.setMonth(m);
  1044. d.setMonth(m);
  1045. if (!keepTime) {
  1046. clearTime(d);
  1047. }
  1048. while (d.getMonth() != check.getMonth()) {
  1049. d.setDate(d.getDate() + (d < check ? 1 : -1));
  1050. }
  1051. }
  1052. return d;
  1053. }
  1054. function addDays(d, n, keepTime) { // deals with daylight savings
  1055. if (+d) {
  1056. var dd = d.getDate() + n,
  1057. check = cloneDate(d);
  1058. check.setHours(9); // set to middle of day
  1059. check.setDate(dd);
  1060. d.setDate(dd);
  1061. if (!keepTime) {
  1062. clearTime(d);
  1063. }
  1064. fixDate(d, check);
  1065. }
  1066. return d;
  1067. }
  1068. function fixDate(d, check) { // force d to be on check's YMD, for daylight savings purposes
  1069. if (+d) { // prevent infinite looping on invalid dates
  1070. while (d.getDate() != check.getDate()) {
  1071. d.setTime(+d + (d < check ? 1 : -1) * HOUR_MS);
  1072. }
  1073. }
  1074. }
  1075. function addMinutes(d, n) {
  1076. d.setMinutes(d.getMinutes() + n);
  1077. return d;
  1078. }
  1079. function clearTime(d) {
  1080. d.setHours(0);
  1081. d.setMinutes(0);
  1082. d.setSeconds(0);
  1083. d.setMilliseconds(0);
  1084. return d;
  1085. }
  1086. function cloneDate(d, dontKeepTime) {
  1087. if (dontKeepTime) {
  1088. return clearTime(new Date(+d));
  1089. }
  1090. return new Date(+d);
  1091. }
  1092. function zeroDate() { // returns a Date with time 00:00:00 and dateOfMonth=1
  1093. var i=0, d;
  1094. do {
  1095. d = new Date(1970, i++, 1);
  1096. } while (d.getHours()); // != 0
  1097. return d;
  1098. }
  1099. function dayDiff(d1, d2) { // d1 - d2
  1100. return Math.round((cloneDate(d1, true) - cloneDate(d2, true)) / DAY_MS);
  1101. }
  1102. function setYMD(date, y, m, d) {
  1103. if (y !== undefined && y != date.getFullYear()) {
  1104. date.setDate(1);
  1105. date.setMonth(0);
  1106. date.setFullYear(y);
  1107. }
  1108. if (m !== undefined && m != date.getMonth()) {
  1109. date.setDate(1);
  1110. date.setMonth(m);
  1111. }
  1112. if (d !== undefined) {
  1113. date.setDate(d);
  1114. }
  1115. }
  1116. /* Date Parsing
  1117. -----------------------------------------------------------------------------*/
  1118. function parseDate(s, ignoreTimezone) { // ignoreTimezone defaults to true
  1119. if (typeof s == 'object') { // already a Date object
  1120. return s;
  1121. }
  1122. if (typeof s == 'number') { // a UNIX timestamp
  1123. return new Date(s * 1000);
  1124. }
  1125. if (typeof s == 'string') {
  1126. if (s.match(/^\d+(\.\d+)?$/)) { // a UNIX timestamp
  1127. return new Date(parseFloat(s) * 1000);
  1128. }
  1129. if (ignoreTimezone === undefined) {
  1130. ignoreTimezone = true;
  1131. }
  1132. return parseISO8601(s, ignoreTimezone) || (s ? new Date(s) : null);
  1133. }
  1134. // TODO: never return invalid dates (like from new Date(<string>)), return null instead
  1135. return null;
  1136. }
  1137. function parseISO8601(s, ignoreTimezone) { // ignoreTimezone defaults to false
  1138. // derived from http://delete.me.uk/2005/03/iso8601.html
  1139. // TODO: for a know glitch/feature, read tests/issue_206_parseDate_dst.html
  1140. var m = s.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2})(:?([0-9]{2}))?))?)?)?)?$/);
  1141. if (!m) {
  1142. return null;
  1143. }
  1144. var date = new Date(m[1], 0, 1);
  1145. if (ignoreTimezone || !m[13]) {
  1146. var check = new Date(m[1], 0, 1, 9, 0);
  1147. if (m[3]) {
  1148. date.setMonth(m[3] - 1);
  1149. check.setMonth(m[3] - 1);
  1150. }
  1151. if (m[5]) {
  1152. date.setDate(m[5]);
  1153. check.setDate(m[5]);
  1154. }
  1155. fixDate(date, check);
  1156. if (m[7]) {
  1157. date.setHours(m[7]);
  1158. }
  1159. if (m[8]) {
  1160. date.setMinutes(m[8]);
  1161. }
  1162. if (m[10]) {
  1163. date.setSeconds(m[10]);
  1164. }
  1165. if (m[12]) {
  1166. date.setMilliseconds(Number("0." + m[12]) * 1000);
  1167. }
  1168. fixDate(date, check);
  1169. }else{
  1170. date.setUTCFullYear(
  1171. m[1],
  1172. m[3] ? m[3] - 1 : 0,
  1173. m[5] || 1
  1174. );
  1175. date.setUTCHours(
  1176. m[7] || 0,
  1177. m[8] || 0,
  1178. m[10] || 0,
  1179. m[12] ? Number("0." + m[12]) * 1000 : 0
  1180. );
  1181. if (m[14]) {
  1182. var offset = Number(m[16]) * 60 + (m[18] ? Number(m[18]) : 0);
  1183. offset *= m[15] == '-' ? 1 : -1;
  1184. date = new Date(+date + (offset * 60 * 1000));
  1185. }
  1186. }
  1187. return date;
  1188. }
  1189. function parseTime(s) { // returns minutes since start of day
  1190. if (typeof s == 'number') { // an hour
  1191. return s * 60;
  1192. }
  1193. if (typeof s == 'object') { // a Date object
  1194. return s.getHours() * 60 + s.getMinutes();
  1195. }
  1196. var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/);
  1197. if (m) {
  1198. var h = parseInt(m[1], 10);
  1199. if (m[3]) {
  1200. h %= 12;
  1201. if (m[3].toLowerCase().charAt(0) == 'p') {
  1202. h += 12;
  1203. }
  1204. }
  1205. return h * 60 + (m[2] ? parseInt(m[2], 10) : 0);
  1206. }
  1207. }
  1208. /* Date Formatting
  1209. -----------------------------------------------------------------------------*/
  1210. // TODO: use same function formatDate(date, [date2], format, [options])
  1211. function formatDate(date, format, options) {
  1212. return formatDates(date, null, format, options);
  1213. }
  1214. function formatDates(date1, date2, format, options) {
  1215. options = options || defaults;
  1216. var date = date1,
  1217. otherDate = date2,
  1218. i, len = format.length, c,
  1219. i2, formatter,
  1220. res = '';
  1221. for (i=0; i<len; i++) {
  1222. c = format.charAt(i);
  1223. if (c == "'") {
  1224. for (i2=i+1; i2<len; i2++) {
  1225. if (format.charAt(i2) == "'") {
  1226. if (date) {
  1227. if (i2 == i+1) {
  1228. res += "'";
  1229. }else{
  1230. res += format.substring(i+1, i2);
  1231. }
  1232. i = i2;
  1233. }
  1234. break;
  1235. }
  1236. }
  1237. }
  1238. else if (c == '(') {
  1239. for (i2=i+1; i2<len; i2++) {
  1240. if (format.charAt(i2) == ')') {
  1241. var subres = formatDate(date, format.substring(i+1, i2), options);
  1242. if (parseInt(subres.replace(/\D/, ''), 10)) {
  1243. res += subres;
  1244. }
  1245. i = i2;
  1246. break;
  1247. }
  1248. }
  1249. }
  1250. else if (c == '[') {
  1251. for (i2=i+1; i2<len; i2++) {
  1252. if (format.charAt(i2) == ']') {
  1253. var subformat = format.substring(i+1, i2);
  1254. var subres = formatDate(date, subformat, options);
  1255. if (subres != formatDate(otherDate, subformat, options)) {
  1256. res += subres;
  1257. }
  1258. i = i2;
  1259. break;
  1260. }
  1261. }
  1262. }
  1263. else if (c == '{') {
  1264. date = date2;
  1265. otherDate = date1;
  1266. }
  1267. else if (c == '}') {
  1268. date = date1;
  1269. otherDate = date2;
  1270. }
  1271. else {
  1272. for (i2=len; i2>i; i2--) {
  1273. if (formatter = dateFormatters[format.substring(i, i2)]) {
  1274. if (date) {
  1275. res += formatter(date, options);
  1276. }
  1277. i = i2 - 1;
  1278. break;
  1279. }
  1280. }
  1281. if (i2 == i) {
  1282. if (date) {
  1283. res += c;
  1284. }
  1285. }
  1286. }
  1287. }
  1288. return res;
  1289. };
  1290. var dateFormatters = {
  1291. s : function(d) { return d.getSeconds() },
  1292. ss : function(d) { return zeroPad(d.getSeconds()) },
  1293. m : function(d) { return d.getMinutes() },
  1294. mm : function(d) { return zeroPad(d.getMinutes()) },
  1295. h : function(d) { return d.getHours() % 12 || 12 },
  1296. hh : function(d) { return zeroPad(d.getHours() % 12 || 12) },
  1297. H : function(d) { return d.getHours() },
  1298. HH : function(d) { return zeroPad(d.getHours()) },
  1299. d : function(d) { return d.getDate() },
  1300. dd : function(d) { return zeroPad(d.getDate()) },
  1301. ddd : function(d,o) { return o.dayNamesShort[d.getDay()] },
  1302. dddd: function(d,o) { return o.dayNames[d.getDay()] },
  1303. M : function(d) { return d.getMonth() + 1 },
  1304. MM : function(d) { return zeroPad(d.getMonth() + 1) },
  1305. MMM : function(d,o) { return o.monthNamesShort[d.getMonth()] },
  1306. MMMM: function(d,o) { return o.monthNames[d.getMonth()] },
  1307. yy : function(d) { return (d.getFullYear()+'').substring(2) },
  1308. yyyy: function(d) { return d.getFullYear() },
  1309. t : function(d) { return d.getHours() < 12 ? 'a' : 'p' },
  1310. tt : function(d) { return d.getHours() < 12 ? 'am' : 'pm' },
  1311. T : function(d) { return d.getHours() < 12 ? 'A' : 'P' },
  1312. TT : function(d) { return d.getHours() < 12 ? 'AM' : 'PM' },
  1313. u : function(d) { return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'") },
  1314. S : function(d) {
  1315. var date = d.getDate();
  1316. if (date > 10 && date < 20) {
  1317. return 'th';
  1318. }
  1319. return ['st', 'nd', 'rd'][date%10-1] || 'th';
  1320. },
  1321. w : function(d, o) { // local
  1322. return o.weekNumberCalculation(d);
  1323. },
  1324. W : function(d) { // ISO
  1325. return iso8601Week(d);
  1326. }
  1327. };
  1328. fc.dateFormatters = dateFormatters;
  1329. /* thanks jQuery UI (https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js)
  1330. *
  1331. * Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
  1332. * `date` - the date to get the week for
  1333. * `number` - the number of the week within the year that contains this date
  1334. */
  1335. function iso8601Week(date) {
  1336. var time;
  1337. var checkDate = new Date(date.getTime());
  1338. // Find Thursday of this week starting on Monday
  1339. checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
  1340. time = checkDate.getTime();
  1341. checkDate.setMonth(0); // Compare with Jan 1
  1342. checkDate.setDate(1);
  1343. return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
  1344. }
  1345. ;;
  1346. fc.applyAll = applyAll;
  1347. /* Event Date Math
  1348. -----------------------------------------------------------------------------*/
  1349. function exclEndDay(event) {
  1350. if (event.end) {
  1351. return _exclEndDay(event.end, event.allDay);
  1352. }else{
  1353. return addDays(cloneDate(event.start), 1);
  1354. }
  1355. }
  1356. function _exclEndDay(end, allDay) {
  1357. end = cloneDate(end);
  1358. return allDay || end.getHours() || end.getMinutes() ? addDays(end, 1) : clearTime(end);
  1359. // why don't we check for seconds/ms too?
  1360. }
  1361. /* Event Element Binding
  1362. -----------------------------------------------------------------------------*/
  1363. function lazySegBind(container, segs, bindHandlers) {
  1364. container.unbind('mouseover').mouseover(function(ev) {
  1365. var parent=ev.target, e,
  1366. i, seg;
  1367. while (parent != this) {
  1368. e = parent;
  1369. parent = parent.parentNode;
  1370. }
  1371. if ((i = e._fci) !== undefined) {
  1372. e._fci = undefined;
  1373. seg = segs[i];
  1374. bindHandlers(seg.event, seg.element, seg);
  1375. $(ev.target).trigger(ev);
  1376. }
  1377. ev.stopPropagation();
  1378. });
  1379. }
  1380. /* Element Dimensions
  1381. -----------------------------------------------------------------------------*/
  1382. function setOuterWidth(element, width, includeMargins) {
  1383. for (var i=0, e; i<element.length; i++) {
  1384. e = $(element[i]);
  1385. e.width(Math.max(0, width - hsides(e, includeMargins)));
  1386. }
  1387. }
  1388. function setOuterHeight(element, height, includeMargins) {
  1389. for (var i=0, e; i<element.length; i++) {
  1390. e = $(element[i]);
  1391. e.height(Math.max(0, height - vsides(e, includeMargins)));
  1392. }
  1393. }
  1394. function hsides(element, includeMargins) {
  1395. return hpadding(element) + hborders(element) + (includeMargins ? hmargins(element) : 0);
  1396. }
  1397. function hpadding(element) {
  1398. return (parseFloat($.css(element[0], 'paddingLeft', true)) || 0) +
  1399. (parseFloat($.css(element[0], 'paddingRight', true)) || 0);
  1400. }
  1401. function hmargins(element) {
  1402. return (parseFloat($.css(element[0], 'marginLeft', true)) || 0) +
  1403. (parseFloat($.css(element[0], 'marginRight', true)) || 0);
  1404. }
  1405. function hborders(element) {
  1406. return (parseFloat($.css(element[0], 'borderLeftWidth', true)) || 0) +
  1407. (parseFloat($.css(element[0], 'borderRightWidth', true)) || 0);
  1408. }
  1409. function vsides(element, includeMargins) {
  1410. return vpadding(element) + vborders(element) + (includeMargins ? vmargins(element) : 0);
  1411. }
  1412. function vpadding(element) {
  1413. return (parseFloat($.css(element[0], 'paddingTop', true)) || 0) +
  1414. (parseFloat($.css(element[0], 'paddingBottom', true)) || 0);
  1415. }
  1416. function vmargins(element) {
  1417. return (parseFloat($.css(element[0], 'marginTop', true)) || 0) +
  1418. (parseFloat($.css(element[0], 'marginBottom', true)) || 0);
  1419. }
  1420. function vborders(element) {
  1421. return (parseFloat($.css(element[0], 'borderTopWidth', true)) || 0) +
  1422. (parseFloat($.css(element[0], 'borderBottomWidth', true)) || 0);
  1423. }
  1424. /* Misc Utils
  1425. -----------------------------------------------------------------------------*/
  1426. //TODO: arraySlice
  1427. //TODO: isFunction, grep ?
  1428. function noop() { }
  1429. function dateCompare(a, b) {
  1430. return a - b;
  1431. }
  1432. function arrayMax(a) {
  1433. return Math.max.apply(Math, a);
  1434. }
  1435. function zeroPad(n) {
  1436. return (n < 10 ? '0' : '') + n;
  1437. }
  1438. function smartProperty(obj, name) { // get a camel-cased/namespaced property of an object
  1439. if (obj[name] !== undefined) {
  1440. return obj[name];
  1441. }
  1442. var parts = name.split(/(?=[A-Z])/),
  1443. i=parts.length-1, res;
  1444. for (; i>=0; i--) {
  1445. res = obj[parts[i].toLowerCase()];
  1446. if (res !== undefined) {
  1447. return res;
  1448. }
  1449. }
  1450. return obj[''];
  1451. }
  1452. function htmlEscape(s) {
  1453. return s.replace(/&/g, '&amp;')
  1454. .replace(/</g, '&lt;')
  1455. .replace(/>/g, '&gt;')
  1456. .replace(/'/g, '&#039;')
  1457. .replace(/"/g, '&quot;')
  1458. .replace(/\n/g, '<br />');
  1459. }
  1460. function disableTextSelection(element) {
  1461. element
  1462. .attr('unselectable', 'on')
  1463. .css('MozUserSelect', 'none')
  1464. .bind('selectstart.ui', function() { return false; });
  1465. }
  1466. /*
  1467. function enableTextSelection(element) {
  1468. element
  1469. .attr('unselectable', 'off')
  1470. .css('MozUserSelect', '')
  1471. .unbind('selectstart.ui');
  1472. }
  1473. */
  1474. function markFirstLast(e) {
  1475. e.children()
  1476. .removeClass('fc-first fc-last')
  1477. .filter(':first-child')
  1478. .addClass('fc-first')
  1479. .end()
  1480. .filter(':last-child')
  1481. .addClass('fc-last');
  1482. }
  1483. function setDayID(cell, date) {
  1484. cell.each(function(i, _cell) {
  1485. _cell.className = _cell.className.replace(/^fc-\w*/, 'fc-' + dayIDs[date.getDay()]);
  1486. // TODO: make a way that doesn't rely on order of classes
  1487. });
  1488. }
  1489. function getSkinCss(event, opt) {
  1490. var source = event.source || {};
  1491. var eventColor = event.color;
  1492. var sourceColor = source.color;
  1493. var optionColor = opt('eventColor');
  1494. var backgroundColor =
  1495. event.backgroundColor ||
  1496. eventColor ||
  1497. source.backgroundColor ||
  1498. sourceColor ||
  1499. opt('eventBackgroundColor') ||
  1500. optionColor;
  1501. var borderColor =
  1502. event.borderColor ||
  1503. eventColor ||
  1504. source.borderColor ||
  1505. sourceColor ||
  1506. opt('eventBorderColor') ||
  1507. optionColor;
  1508. var textColor =
  1509. event.textColor ||
  1510. source.textColor ||
  1511. opt('eventTextColor');
  1512. var statements = [];
  1513. if (backgroundColor) {
  1514. statements.push('background-color:' + backgroundColor);
  1515. }
  1516. if (borderColor) {
  1517. statements.push('border-color:' + borderColor);
  1518. }
  1519. if (textColor) {
  1520. statements.push('color:' + textColor);
  1521. }
  1522. return statements.join(';');
  1523. }
  1524. function applyAll(functions, thisObj, args) {
  1525. if ($.isFunction(functions)) {
  1526. functions = [ functions ];
  1527. }
  1528. if (functions) {
  1529. var i;
  1530. var ret;
  1531. for (i=0; i<functions.length; i++) {
  1532. ret = functions[i].apply(thisObj, args) || ret;
  1533. }
  1534. return ret;
  1535. }
  1536. }
  1537. function firstDefined() {
  1538. for (var i=0; i<arguments.length; i++) {
  1539. if (arguments[i] !== undefined) {
  1540. return arguments[i];
  1541. }
  1542. }
  1543. }
  1544. ;;
  1545. fcViews.month = MonthView;
  1546. function MonthView(element, calendar) {
  1547. var t = this;
  1548. // exports
  1549. t.render = render;
  1550. // imports
  1551. BasicView.call(t, element, calendar, 'month');
  1552. var opt = t.opt;
  1553. var renderBasic = t.renderBasic;
  1554. var skipHiddenDays = t.skipHiddenDays;
  1555. var getCellsPerWeek = t.getCellsPerWeek;
  1556. var formatDate = calendar.formatDate;
  1557. function render(date, delta) {
  1558. if (delta) {
  1559. addMonths(date, delta);
  1560. date.setDate(1);
  1561. }
  1562. var firstDay = opt('firstDay');
  1563. var start = cloneDate(date, true);
  1564. start.setDate(1);
  1565. var end = addMonths(cloneDate(start), 1);
  1566. var visStart = cloneDate(start);
  1567. addDays(visStart, -((visStart.getDay() - firstDay + 7) % 7));
  1568. skipHiddenDays(visStart);
  1569. var visEnd = cloneDate(end);
  1570. addDays(visEnd, (7 - visEnd.getDay() + firstDay) % 7);
  1571. skipHiddenDays(visEnd, -1, true);
  1572. var colCnt = getCellsPerWeek();
  1573. var rowCnt = Math.round(dayDiff(visEnd, visStart) / 7); // should be no need for Math.round
  1574. if (opt('weekMode') == 'fixed') {
  1575. addDays(visEnd, (6 - rowCnt) * 7); // add weeks to make up for it
  1576. rowCnt = 6;
  1577. }
  1578. t.title = formatDate(start, opt('titleFormat'));
  1579. t.start = start;
  1580. t.end = end;
  1581. t.visStart = visStart;
  1582. t.visEnd = visEnd;
  1583. renderBasic(rowCnt, colCnt, true);
  1584. }
  1585. }
  1586. ;;
  1587. fcViews.basicWeek = BasicWeekView;
  1588. function BasicWeekView(element, calendar) {
  1589. var t = this;
  1590. // exports
  1591. t.render = render;
  1592. // imports
  1593. BasicView.call(t, element, calendar, 'basicWeek');
  1594. var opt = t.opt;
  1595. var renderBasic = t.renderBasic;
  1596. var skipHiddenDays = t.skipHiddenDays;
  1597. var getCellsPerWeek = t.getCellsPerWeek;
  1598. var formatDates = calendar.formatDates;
  1599. function render(date, delta) {
  1600. if (delta) {
  1601. addDays(date, delta * 7);
  1602. }
  1603. var start = addDays(cloneDate(date), -((date.getDay() - opt('firstDay') + 7) % 7));
  1604. var end = addDays(cloneDate(start), 7);
  1605. var visStart = cloneDate(start);
  1606. skipHiddenDays(visStart);
  1607. var visEnd = cloneDate(end);
  1608. skipHiddenDays(visEnd, -1, true);
  1609. var colCnt = getCellsPerWeek();
  1610. t.start = start;
  1611. t.end = end;
  1612. t.visStart = visStart;
  1613. t.visEnd = visEnd;
  1614. t.title = formatDates(
  1615. visStart,
  1616. addDays(cloneDate(visEnd), -1),
  1617. opt('titleFormat')
  1618. );
  1619. renderBasic(1, colCnt, false);
  1620. }
  1621. }
  1622. ;;
  1623. fcViews.basicDay = BasicDayView;
  1624. function BasicDayView(element, calendar) {
  1625. var t = this;
  1626. // exports
  1627. t.render = render;
  1628. // imports
  1629. BasicView.call(t, element, calendar, 'basicDay');
  1630. var opt = t.opt;
  1631. var renderBasic = t.renderBasic;
  1632. var skipHiddenDays = t.skipHiddenDays;
  1633. var formatDate = calendar.formatDate;
  1634. function render(date, delta) {
  1635. if (delta) {
  1636. addDays(date, delta);
  1637. }
  1638. skipHiddenDays(date, delta < 0 ? -1 : 1);
  1639. var start = cloneDate(date, true);
  1640. var end = addDays(cloneDate(start), 1);
  1641. t.title = formatDate(date, opt('titleFormat'));
  1642. t.start = t.visStart = start;
  1643. t.end = t.visEnd = end;
  1644. renderBasic(1, 1, false);
  1645. }
  1646. }
  1647. ;;
  1648. setDefaults({
  1649. weekMode: 'fixed'
  1650. });
  1651. function BasicView(element, calendar, viewName) {
  1652. var t = this;
  1653. // exports
  1654. t.renderBasic = renderBasic;
  1655. t.setHeight = setHeight;
  1656. t.setWidth = setWidth;
  1657. t.renderDayOverlay = renderDayOverlay;
  1658. t.defaultSelectionEnd = defaultSelectionEnd;
  1659. t.renderSelection = renderSelection;
  1660. t.clearSelection = clearSelection;
  1661. t.reportDayClick = reportDayClick; // for selection (kinda hacky)
  1662. t.dragStart = dragStart;
  1663. t.dragStop = dragStop;
  1664. t.defaultEventEnd = defaultEventEnd;
  1665. t.getHoverListener = function() { return hoverListener };
  1666. t.colLeft = colLeft;
  1667. t.colRight = colRight;
  1668. t.colContentLeft = colContentLeft;
  1669. t.colContentRight = colContentRight;
  1670. t.getIsCellAllDay = function() { return true };
  1671. t.allDayRow = allDayRow;
  1672. t.getRowCnt = function() { return rowCnt };
  1673. t.getColCnt = function() { return colCnt };
  1674. t.getColWidth = function() { return colWidth };
  1675. t.getDaySegmentContainer = function() { return daySegmentContainer };
  1676. // imports
  1677. View.call(t, element, calendar, viewName);
  1678. OverlayManager.call(t);
  1679. SelectionManager.call(t);
  1680. BasicEventRenderer.call(t);
  1681. var opt = t.opt;
  1682. var trigger = t.trigger;
  1683. var renderOverlay = t.renderOverlay;
  1684. var clearOverlays = t.clearOverlays;
  1685. var daySelectionMousedown = t.daySelectionMousedown;
  1686. var cellToDate = t.cellToDate;
  1687. var dateToCell = t.dateToCell;
  1688. var rangeToSegments = t.rangeToSegments;
  1689. var formatDate = calendar.formatDate;
  1690. // locals
  1691. var table;
  1692. var head;
  1693. var headCells;
  1694. var body;
  1695. var bodyRows;
  1696. var bodyCells;
  1697. var bodyFirstCells;
  1698. var firstRowCellInners;
  1699. var firstRowCellContentInners;
  1700. var daySegmentContainer;
  1701. var viewWidth;
  1702. var viewHeight;
  1703. var colWidth;
  1704. var weekNumberWidth;
  1705. var rowCnt, colCnt;
  1706. var showNumbers;
  1707. var coordinateGrid;
  1708. var hoverListener;
  1709. var colPositions;
  1710. var colContentPositions;
  1711. var tm;
  1712. var colFormat;
  1713. var showWeekNumbers;
  1714. var weekNumberTitle;
  1715. var weekNumberFormat;
  1716. /* Rendering
  1717. ------------------------------------------------------------*/
  1718. disableTextSelection(element.addClass('fc-grid'));
  1719. function renderBasic(_rowCnt, _colCnt, _showNumbers) {
  1720. rowCnt = _rowCnt;
  1721. colCnt = _colCnt;
  1722. showNumbers = _showNumbers;
  1723. updateOptions();
  1724. if (!body) {
  1725. buildEventContainer();
  1726. }
  1727. buildTable();
  1728. }
  1729. function updateOptions() {
  1730. tm = opt('theme') ? 'ui' : 'fc';
  1731. colFormat = opt('columnFormat');
  1732. // week # options. (TODO: bad, logic also in other views)
  1733. showWeekNumbers = opt('weekNumbers');
  1734. weekNumberTitle = opt('weekNumberTitle');
  1735. if (opt('weekNumberCalculation') != 'iso') {
  1736. weekNumberFormat = "w";
  1737. }
  1738. else {
  1739. weekNumberFormat = "W";
  1740. }
  1741. }
  1742. function buildEventContainer() {
  1743. daySegmentContainer =
  1744. $("<div class='fc-event-container' style='position:absolute;z-index:8;top:0;left:0'/>")
  1745. .appendTo(element);
  1746. }
  1747. function buildTable() {
  1748. var html = buildTableHTML();
  1749. if (table) {
  1750. table.remove();
  1751. }
  1752. table = $(html).appendTo(element);
  1753. head = table.find('thead');
  1754. headCells = head.find('.fc-day-header');
  1755. body = table.find('tbody');
  1756. bodyRows = body.find('tr');
  1757. bodyCells = body.find('.fc-day');
  1758. bodyFirstCells = bodyRows.find('td:first-child');
  1759. firstRowCellInners = bodyRows.eq(0).find('.fc-day > div');
  1760. firstRowCellContentInners = bodyRows.eq(0).find('.fc-day-content > div');
  1761. markFirstLast(head.add(head.find('tr'))); // marks first+last tr/th's
  1762. markFirstLast(bodyRows); // marks first+last td's
  1763. bodyRows.eq(0).addClass('fc-first');
  1764. bodyRows.filter(':last').addClass('fc-last');
  1765. bodyCells.each(function(i, _cell) {
  1766. var date = cellToDate(
  1767. Math.floor(i / colCnt),
  1768. i % colCnt
  1769. );
  1770. trigger('dayRender', t, date, $(_cell));
  1771. });
  1772. dayBind(bodyCells);
  1773. }
  1774. /* HTML Building
  1775. -----------------------------------------------------------*/
  1776. function buildTableHTML() {
  1777. var html =
  1778. "<table class='fc-border-separate' style='width:100%' cellspacing='0'>" +
  1779. buildHeadHTML() +
  1780. buildBodyHTML() +
  1781. "</table>";
  1782. return html;
  1783. }
  1784. function buildHeadHTML() {
  1785. var headerClass = tm + "-widget-header";
  1786. var html = '';
  1787. var col;
  1788. var date;
  1789. html += "<thead><tr>";
  1790. if (showWeekNumbers) {
  1791. html +=
  1792. "<th class='fc-week-number " + headerClass + "'>" +
  1793. htmlEscape(weekNumberTitle) +
  1794. "</th>";
  1795. }
  1796. for (col=0; col<colCnt; col++) {
  1797. date = cellToDate(0, col);
  1798. html +=
  1799. "<th class='fc-day-header fc-" + dayIDs[date.getDay()] + " " + headerClass + "'>" +
  1800. htmlEscape(formatDate(date, colFormat)) +
  1801. "</th>";
  1802. }
  1803. html += "</tr></thead>";
  1804. return html;
  1805. }
  1806. function buildBodyHTML() {
  1807. var contentClass = tm + "-widget-content";
  1808. var html = '';
  1809. var row;
  1810. var col;
  1811. var date;
  1812. html += "<tbody>";
  1813. for (row=0; row<rowCnt; row++) {
  1814. html += "<tr class='fc-week'>";
  1815. if (showWeekNumbers) {
  1816. date = cellToDate(row, 0);
  1817. html +=
  1818. "<td class='fc-week-number " + contentClass + "'>" +
  1819. "<div>" +
  1820. htmlEscape(formatDate(date, weekNumberFormat)) +
  1821. "</div>" +
  1822. "</td>";
  1823. }
  1824. for (col=0; col<colCnt; col++) {
  1825. date = cellToDate(row, col);
  1826. html += buildCellHTML(date);
  1827. }
  1828. html += "</tr>";
  1829. }
  1830. html += "</tbody>";
  1831. return html;
  1832. }
  1833. function buildCellHTML(date) {
  1834. var contentClass = tm + "-widget-content";
  1835. var month = t.start.getMonth();
  1836. var today = clearTime(new Date());
  1837. var html = '';
  1838. var classNames = [
  1839. 'fc-day',
  1840. 'fc-' + dayIDs[date.getDay()],
  1841. contentClass
  1842. ];
  1843. if (date.getMonth() != month) {
  1844. classNames.push('fc-other-month');
  1845. }
  1846. if (+date == +today) {
  1847. classNames.push(
  1848. 'fc-today',
  1849. tm + '-state-highlight'
  1850. );
  1851. }
  1852. else if (date < today) {
  1853. classNames.push('fc-past');
  1854. }
  1855. else {
  1856. classNames.push('fc-future');
  1857. }
  1858. html +=
  1859. "<td" +
  1860. " class='" + classNames.join(' ') + "'" +
  1861. " data-date='" + formatDate(date, 'yyyy-MM-dd') + "'" +
  1862. ">" +
  1863. "<div>";
  1864. if (showNumbers) {
  1865. html += "<div class='fc-day-number'>" + date.getDate() + "</div>";
  1866. // modified by feifei.im 增加节气显示
  1867. var cTerm = lunar(date).term;
  1868. if(cTerm){
  1869. html += "<div class='fc-day-cnTerm'>"+cTerm+"</div>";
  1870. }
  1871. // modified by feifei.im 增加节日显示
  1872. var fes = lunar(date).festival();
  1873. if(fes && fes.length>0){
  1874. html += "<div class='fc-day-cnTerm'>"+$.trim(fes[0].desc)+"</div>";
  1875. }
  1876. // modified by feifei.im 无节日节气时,增加农历显示
  1877. if(!cTerm && (!fes || fes.length==0)){
  1878. html += "<div class='fc-day-cnDate'>"+lunar(date).lMonth + "月" + lunar(date).lDate +"</div>";
  1879. }
  1880. }
  1881. html +=
  1882. "<div class='fc-day-content'>" +
  1883. "<div style='position:relative'>&nbsp;</div>" +
  1884. "</div>" +
  1885. "</div>" +
  1886. "</td>";
  1887. return html;
  1888. }
  1889. /* Dimensions
  1890. -----------------------------------------------------------*/
  1891. function setHeight(height) {
  1892. viewHeight = height;
  1893. var bodyHeight = viewHeight - head.height();
  1894. var rowHeight;
  1895. var rowHeightLast;
  1896. var cell;
  1897. if (opt('weekMode') == 'variable') {
  1898. rowHeight = rowHeightLast = Math.floor(bodyHeight / (rowCnt==1 ? 2 : 6));
  1899. }else{
  1900. rowHeight = Math.floor(bodyHeight / rowCnt);
  1901. rowHeightLast = bodyHeight - rowHeight * (rowCnt-1);
  1902. }
  1903. bodyFirstCells.each(function(i, _cell) {
  1904. if (i < rowCnt) {
  1905. cell = $(_cell);
  1906. cell.find('> div').css(
  1907. 'min-height',
  1908. (i==rowCnt-1 ? rowHeightLast : rowHeight) - vsides(cell)
  1909. );
  1910. }
  1911. });
  1912. }
  1913. function setWidth(width) {
  1914. viewWidth = width;
  1915. colPositions.clear();
  1916. colContentPositions.clear();
  1917. weekNumberWidth = 0;
  1918. if (showWeekNumbers) {
  1919. weekNumberWidth = head.find('th.fc-week-number').outerWidth();
  1920. }
  1921. colWidth = Math.floor((viewWidth - weekNumberWidth) / colCnt);
  1922. setOuterWidth(headCells.slice(0, -1), colWidth);
  1923. }
  1924. /* Day clicking and binding
  1925. -----------------------------------------------------------*/
  1926. function dayBind(days) {
  1927. days.click(dayClick)
  1928. .mousedown(daySelectionMousedown);
  1929. }
  1930. function dayClick(ev) {
  1931. if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick
  1932. var date = parseISO8601($(this).data('date'));
  1933. trigger('dayClick', this, date, true, ev);
  1934. }
  1935. }
  1936. /* Semi-transparent Overlay Helpers
  1937. ------------------------------------------------------*/
  1938. // TODO: should be consolidated with AgendaView's methods
  1939. function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive
  1940. if (refreshCoordinateGrid) {
  1941. coordinateGrid.build();
  1942. }
  1943. var segments = rangeToSegments(overlayStart, overlayEnd);
  1944. for (var i=0; i<segments.length; i++) {
  1945. var segment = segments[i];
  1946. dayBind(
  1947. renderCellOverlay(
  1948. segment.row,
  1949. segment.leftCol,
  1950. segment.row,
  1951. segment.rightCol
  1952. )
  1953. );
  1954. }
  1955. }
  1956. function renderCellOverlay(row0, col0, row1, col1) { // row1,col1 is inclusive
  1957. var rect = coordinateGrid.rect(row0, col0, row1, col1, element);
  1958. return renderOverlay(rect, element);
  1959. }
  1960. /* Selection
  1961. -----------------------------------------------------------------------*/
  1962. function defaultSelectionEnd(startDate, allDay) {
  1963. return cloneDate(startDate);
  1964. }
  1965. function renderSelection(startDate, endDate, allDay) {
  1966. renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true); // rebuild every time???
  1967. }
  1968. function clearSelection() {
  1969. clearOverlays();
  1970. }
  1971. function reportDayClick(date, allDay, ev) {
  1972. var cell = dateToCell(date);
  1973. var _element = bodyCells[cell.row*colCnt + cell.col];
  1974. trigger('dayClick', _element, date, allDay, ev);
  1975. }
  1976. /* External Dragging
  1977. -----------------------------------------------------------------------*/
  1978. function dragStart(_dragElement, ev, ui) {
  1979. hoverListener.start(function(cell) {
  1980. clearOverlays();
  1981. if (cell) {
  1982. renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
  1983. }
  1984. }, ev);
  1985. }
  1986. function dragStop(_dragElement, ev, ui) {
  1987. var cell = hoverListener.stop();
  1988. clearOverlays();
  1989. if (cell) {
  1990. var d = cellToDate(cell);
  1991. trigger('drop', _dragElement, d, true, ev, ui);
  1992. }
  1993. }
  1994. /* Utilities
  1995. --------------------------------------------------------*/
  1996. function defaultEventEnd(event) {
  1997. return cloneDate(event.start);
  1998. }
  1999. coordinateGrid = new CoordinateGrid(function(rows, cols) {
  2000. var e, n, p;
  2001. headCells.each(function(i, _e) {
  2002. e = $(_e);
  2003. n = e.offset().left;
  2004. if (i) {
  2005. p[1] = n;
  2006. }
  2007. p = [n];
  2008. cols[i] = p;
  2009. });
  2010. p[1] = n + e.outerWidth();
  2011. bodyRows.each(function(i, _e) {
  2012. if (i < rowCnt) {
  2013. e = $(_e);
  2014. n = e.offset().top;
  2015. if (i) {
  2016. p[1] = n;
  2017. }
  2018. p = [n];
  2019. rows[i] = p;
  2020. }
  2021. });
  2022. p[1] = n + e.outerHeight();
  2023. });
  2024. hoverListener = new HoverListener(coordinateGrid);
  2025. colPositions = new HorizontalPositionCache(function(col) {
  2026. return firstRowCellInners.eq(col);
  2027. });
  2028. colContentPositions = new HorizontalPositionCache(function(col) {
  2029. return firstRowCellContentInners.eq(col);
  2030. });
  2031. function colLeft(col) {
  2032. return colPositions.left(col);
  2033. }
  2034. function colRight(col) {
  2035. return colPositions.right(col);
  2036. }
  2037. function colContentLeft(col) {
  2038. return colContentPositions.left(col);
  2039. }
  2040. function colContentRight(col) {
  2041. return colContentPositions.right(col);
  2042. }
  2043. function allDayRow(i) {
  2044. return bodyRows.eq(i);
  2045. }
  2046. }
  2047. ;;
  2048. function BasicEventRenderer() {
  2049. var t = this;
  2050. // exports
  2051. t.renderEvents = renderEvents;
  2052. t.clearEvents = clearEvents;
  2053. // imports
  2054. DayEventRenderer.call(t);
  2055. function renderEvents(events, modifiedEventId) {
  2056. t.renderDayEvents(events, modifiedEventId);
  2057. }
  2058. function clearEvents() {
  2059. t.getDaySegmentContainer().empty();
  2060. }
  2061. // TODO: have this class (and AgendaEventRenderer) be responsible for creating the event container div
  2062. }
  2063. ;;
  2064. fcViews.agendaWeek = AgendaWeekView;
  2065. function AgendaWeekView(element, calendar) {
  2066. var t = this;
  2067. // exports
  2068. t.render = render;
  2069. // imports
  2070. AgendaView.call(t, element, calendar, 'agendaWeek');
  2071. var opt = t.opt;
  2072. var renderAgenda = t.renderAgenda;
  2073. var skipHiddenDays = t.skipHiddenDays;
  2074. var getCellsPerWeek = t.getCellsPerWeek;
  2075. var formatDates = calendar.formatDates;
  2076. function render(date, delta) {
  2077. if (delta) {
  2078. addDays(date, delta * 7);
  2079. }
  2080. var start = addDays(cloneDate(date), -((date.getDay() - opt('firstDay') + 7) % 7));
  2081. var end = addDays(cloneDate(start), 7);
  2082. var visStart = cloneDate(start);
  2083. skipHiddenDays(visStart);
  2084. var visEnd = cloneDate(end);
  2085. skipHiddenDays(visEnd, -1, true);
  2086. var colCnt = getCellsPerWeek();
  2087. t.title = formatDates(
  2088. visStart,
  2089. addDays(cloneDate(visEnd), -1),
  2090. opt('titleFormat')
  2091. );
  2092. t.start = start;
  2093. t.end = end;
  2094. t.visStart = visStart;
  2095. t.visEnd = visEnd;
  2096. renderAgenda(colCnt);
  2097. }
  2098. }
  2099. ;;
  2100. fcViews.agendaDay = AgendaDayView;
  2101. function AgendaDayView(element, calendar) {
  2102. var t = this;
  2103. // exports
  2104. t.render = render;
  2105. // imports
  2106. AgendaView.call(t, element, calendar, 'agendaDay');
  2107. var opt = t.opt;
  2108. var renderAgenda = t.renderAgenda;
  2109. var skipHiddenDays = t.skipHiddenDays;
  2110. var formatDate = calendar.formatDate;
  2111. function render(date, delta) {
  2112. if (delta) {
  2113. addDays(date, delta);
  2114. }
  2115. skipHiddenDays(date, delta < 0 ? -1 : 1);
  2116. var start = cloneDate(date, true);
  2117. var end = addDays(cloneDate(start), 1);
  2118. t.title = formatDate(date, opt('titleFormat'));
  2119. t.start = t.visStart = start;
  2120. t.end = t.visEnd = end;
  2121. renderAgenda(1);
  2122. }
  2123. }
  2124. ;;
  2125. setDefaults({
  2126. allDaySlot: true,
  2127. allDayText: 'all-day',
  2128. firstHour: 6,
  2129. slotMinutes: 30,
  2130. defaultEventMinutes: 120,
  2131. axisFormat: 'h(:mm)tt',
  2132. timeFormat: {
  2133. agenda: 'h:mm{ - h:mm}'
  2134. },
  2135. dragOpacity: {
  2136. agenda: .5
  2137. },
  2138. minTime: 0,
  2139. maxTime: 24,
  2140. slotEventOverlap: true
  2141. });
  2142. // TODO: make it work in quirks mode (event corners, all-day height)
  2143. // TODO: test liquid width, especially in IE6
  2144. function AgendaView(element, calendar, viewName) {
  2145. var t = this;
  2146. // exports
  2147. t.renderAgenda = renderAgenda;
  2148. t.setWidth = setWidth;
  2149. t.setHeight = setHeight;
  2150. t.afterRender = afterRender;
  2151. t.defaultEventEnd = defaultEventEnd;
  2152. t.timePosition = timePosition;
  2153. t.getIsCellAllDay = getIsCellAllDay;
  2154. t.allDayRow = getAllDayRow;
  2155. t.getCoordinateGrid = function() { return coordinateGrid }; // specifically for AgendaEventRenderer
  2156. t.getHoverListener = function() { return hoverListener };
  2157. t.colLeft = colLeft;
  2158. t.colRight = colRight;
  2159. t.colContentLeft = colContentLeft;
  2160. t.colContentRight = colContentRight;
  2161. t.getDaySegmentContainer = function() { return daySegmentContainer };
  2162. t.getSlotSegmentContainer = function() { return slotSegmentContainer };
  2163. t.getMinMinute = function() { return minMinute };
  2164. t.getMaxMinute = function() { return maxMinute };
  2165. t.getSlotContainer = function() { return slotContainer };
  2166. t.getRowCnt = function() { return 1 };
  2167. t.getColCnt = function() { return colCnt };
  2168. t.getColWidth = function() { return colWidth };
  2169. t.getSnapHeight = function() { return snapHeight };
  2170. t.getSnapMinutes = function() { return snapMinutes };
  2171. t.defaultSelectionEnd = defaultSelectionEnd;
  2172. t.renderDayOverlay = renderDayOverlay;
  2173. t.renderSelection = renderSelection;
  2174. t.clearSelection = clearSelection;
  2175. t.reportDayClick = reportDayClick; // selection mousedown hack
  2176. t.dragStart = dragStart;
  2177. t.dragStop = dragStop;
  2178. // imports
  2179. View.call(t, element, calendar, viewName);
  2180. OverlayManager.call(t);
  2181. SelectionManager.call(t);
  2182. AgendaEventRenderer.call(t);
  2183. var opt = t.opt;
  2184. var trigger = t.trigger;
  2185. var renderOverlay = t.renderOverlay;
  2186. var clearOverlays = t.clearOverlays;
  2187. var reportSelection = t.reportSelection;
  2188. var unselect = t.unselect;
  2189. var daySelectionMousedown = t.daySelectionMousedown;
  2190. var slotSegHtml = t.slotSegHtml;
  2191. var cellToDate = t.cellToDate;
  2192. var dateToCell = t.dateToCell;
  2193. var rangeToSegments = t.rangeToSegments;
  2194. var formatDate = calendar.formatDate;
  2195. // locals
  2196. var dayTable;
  2197. var dayHead;
  2198. var dayHeadCells;
  2199. var dayBody;
  2200. var dayBodyCells;
  2201. var dayBodyCellInners;
  2202. var dayBodyCellContentInners;
  2203. var dayBodyFirstCell;
  2204. var dayBodyFirstCellStretcher;
  2205. var slotLayer;
  2206. var daySegmentContainer;
  2207. var allDayTable;
  2208. var allDayRow;
  2209. var slotScroller;
  2210. var slotContainer;
  2211. var slotSegmentContainer;
  2212. var slotTable;
  2213. var selectionHelper;
  2214. var viewWidth;
  2215. var viewHeight;
  2216. var axisWidth;
  2217. var colWidth;
  2218. var gutterWidth;
  2219. var slotHeight; // TODO: what if slotHeight changes? (see issue 650)
  2220. var snapMinutes;
  2221. var snapRatio; // ratio of number of "selection" slots to normal slots. (ex: 1, 2, 4)
  2222. var snapHeight; // holds the pixel hight of a "selection" slot
  2223. var colCnt;
  2224. var slotCnt;
  2225. var coordinateGrid;
  2226. var hoverListener;
  2227. var colPositions;
  2228. var colContentPositions;
  2229. var slotTopCache = {};
  2230. var tm;
  2231. var rtl;
  2232. var minMinute, maxMinute;
  2233. var colFormat;
  2234. var showWeekNumbers;
  2235. var weekNumberTitle;
  2236. var weekNumberFormat;
  2237. /* Rendering
  2238. -----------------------------------------------------------------------------*/
  2239. disableTextSelection(element.addClass('fc-agenda'));
  2240. function renderAgenda(c) {
  2241. colCnt = c;
  2242. updateOptions();
  2243. if (!dayTable) { // first time rendering?
  2244. buildSkeleton(); // builds day table, slot area, events containers
  2245. }
  2246. else {
  2247. buildDayTable(); // rebuilds day table
  2248. }
  2249. }
  2250. function updateOptions() {
  2251. tm = opt('theme') ? 'ui' : 'fc';
  2252. rtl = opt('isRTL')
  2253. minMinute = parseTime(opt('minTime'));
  2254. maxMinute = parseTime(opt('maxTime'));
  2255. colFormat = opt('columnFormat');
  2256. // week # options. (TODO: bad, logic also in other views)
  2257. showWeekNumbers = opt('weekNumbers');
  2258. weekNumberTitle = opt('weekNumberTitle');
  2259. if (opt('weekNumberCalculation') != 'iso') {
  2260. weekNumberFormat = "w";
  2261. }
  2262. else {
  2263. weekNumberFormat = "W";
  2264. }
  2265. snapMinutes = opt('snapMinutes') || opt('slotMinutes');
  2266. }
  2267. /* Build DOM
  2268. -----------------------------------------------------------------------*/
  2269. function buildSkeleton() {
  2270. var headerClass = tm + "-widget-header";
  2271. var contentClass = tm + "-widget-content";
  2272. var s;
  2273. var d;
  2274. var i;
  2275. var maxd;
  2276. var minutes;
  2277. var slotNormal = opt('slotMinutes') % 15 == 0;
  2278. buildDayTable();
  2279. slotLayer =
  2280. $("<div style='position:absolute;z-index:2;left:0;width:100%'/>")
  2281. .appendTo(element);
  2282. if (opt('allDaySlot')) {
  2283. daySegmentContainer =
  2284. $("<div class='fc-event-container' style='position:absolute;z-index:8;top:0;left:0'/>")
  2285. .appendTo(slotLayer);
  2286. s =
  2287. "<table style='width:100%' class='fc-agenda-allday' cellspacing='0'>" +
  2288. "<tr>" +
  2289. "<th class='" + headerClass + " fc-agenda-axis'>" + opt('allDayText') + "</th>" +
  2290. "<td>" +
  2291. "<div class='fc-day-content' ><div style='position:relative;display:none'/></div>" +
  2292. "</td>" +
  2293. "<th class='" + headerClass + " fc-agenda-gutter'>&nbsp;</th>" +
  2294. "</tr>" +
  2295. "</table>";
  2296. allDayTable = $(s).appendTo(slotLayer);
  2297. allDayRow = allDayTable.find('tr');
  2298. dayBind(allDayRow.find('td'));
  2299. slotLayer.append(
  2300. "<div class='fc-agenda-divider " + headerClass + "'>" +
  2301. "<div class='fc-agenda-divider-inner'/>" +
  2302. "</div>"
  2303. );
  2304. }else{
  2305. daySegmentContainer = $([]); // in jQuery 1.4, we can just do $()
  2306. }
  2307. slotScroller =
  2308. $("<div style='position:absolute;width:100%;overflow-x:hidden;overflow-y:auto'/>")
  2309. .appendTo(slotLayer);
  2310. slotContainer =
  2311. $("<div style='position:relative;width:100%;overflow:hidden'/>")
  2312. .appendTo(slotScroller);
  2313. slotSegmentContainer =
  2314. $("<div class='fc-event-container' style='position:absolute;z-index:8;top:0;left:0'/>")
  2315. .appendTo(slotContainer);
  2316. s =
  2317. "<table class='fc-agenda-slots' style='width:100%' cellspacing='0'>" +
  2318. "<tbody>";
  2319. d = zeroDate();
  2320. maxd = addMinutes(cloneDate(d), maxMinute);
  2321. addMinutes(d, minMinute);
  2322. slotCnt = 0;
  2323. for (i=0; d < maxd; i++) {
  2324. minutes = d.getMinutes();
  2325. s +=
  2326. "<tr class='fc-slot" + i + ' ' + (!minutes ? '' : 'fc-minor') + "'>" +
  2327. "<th class='fc-agenda-axis " + headerClass + "'>" +
  2328. ((!slotNormal || !minutes) ? formatDate(d, opt('axisFormat')) : '&nbsp;') +
  2329. "</th>" +
  2330. "<td class='" + contentClass + "'>" +
  2331. "<div style='position:relative'>&nbsp;</div>" +
  2332. "</td>" +
  2333. "</tr>";
  2334. addMinutes(d, opt('slotMinutes'));
  2335. slotCnt++;
  2336. }
  2337. s +=
  2338. "</tbody>" +
  2339. "</table>";
  2340. slotTable = $(s).appendTo(slotContainer);
  2341. slotBind(slotTable.find('td'));
  2342. }
  2343. /* Build Day Table
  2344. -----------------------------------------------------------------------*/
  2345. function buildDayTable() {
  2346. var html = buildDayTableHTML();
  2347. if (dayTable) {
  2348. dayTable.remove();
  2349. }
  2350. dayTable = $(html).appendTo(element);
  2351. dayHead = dayTable.find('thead');
  2352. dayHeadCells = dayHead.find('th').slice(1, -1); // exclude gutter
  2353. dayBody = dayTable.find('tbody');
  2354. dayBodyCells = dayBody.find('td').slice(0, -1); // exclude gutter
  2355. dayBodyCellInners = dayBodyCells.find('> div');
  2356. dayBodyCellContentInners = dayBodyCells.find('.fc-day-content > div');
  2357. dayBodyFirstCell = dayBodyCells.eq(0);
  2358. dayBodyFirstCellStretcher = dayBodyCellInners.eq(0);
  2359. markFirstLast(dayHead.add(dayHead.find('tr')));
  2360. markFirstLast(dayBody.add(dayBody.find('tr')));
  2361. // TODO: now that we rebuild the cells every time, we should call dayRender
  2362. }
  2363. function buildDayTableHTML() {
  2364. var html =
  2365. "<table style='width:100%' class='fc-agenda-days fc-border-separate' cellspacing='0'>" +
  2366. buildDayTableHeadHTML() +
  2367. buildDayTableBodyHTML() +
  2368. "</table>";
  2369. return html;
  2370. }
  2371. function buildDayTableHeadHTML() {
  2372. var headerClass = tm + "-widget-header";
  2373. var date;
  2374. var html = '';
  2375. var weekText;
  2376. var col;
  2377. html +=
  2378. "<thead>" +
  2379. "<tr>";
  2380. if (showWeekNumbers) {
  2381. date = cellToDate(0, 0);
  2382. weekText = formatDate(date, weekNumberFormat);
  2383. if (rtl) {
  2384. weekText += weekNumberTitle;
  2385. }
  2386. else {
  2387. weekText = weekNumberTitle + weekText;
  2388. }
  2389. html +=
  2390. "<th class='fc-agenda-axis fc-week-number " + headerClass + "'>" +
  2391. htmlEscape(weekText) +
  2392. "</th>";
  2393. }
  2394. else {
  2395. html += "<th class='fc-agenda-axis " + headerClass + "'>&nbsp;</th>";
  2396. }
  2397. for (col=0; col<colCnt; col++) {
  2398. date = cellToDate(0, col);
  2399. html +=
  2400. "<th class='fc-" + dayIDs[date.getDay()] + " fc-col" + col + ' ' + headerClass + "'>" +
  2401. htmlEscape(formatDate(date, colFormat)) +
  2402. "</th>";
  2403. }
  2404. html +=
  2405. "<th class='fc-agenda-gutter " + headerClass + "'>&nbsp;</th>" +
  2406. "</tr>" +
  2407. "</thead>";
  2408. return html;
  2409. }
  2410. function buildDayTableBodyHTML() {
  2411. var headerClass = tm + "-widget-header"; // TODO: make these when updateOptions() called
  2412. var contentClass = tm + "-widget-content";
  2413. var date;
  2414. var today = clearTime(new Date());
  2415. var col;
  2416. var cellsHTML;
  2417. var cellHTML;
  2418. var classNames;
  2419. var html = '';
  2420. html +=
  2421. "<tbody>" +
  2422. "<tr>" +
  2423. "<th class='fc-agenda-axis " + headerClass + "'>&nbsp;</th>";
  2424. cellsHTML = '';
  2425. for (col=0; col<colCnt; col++) {
  2426. date = cellToDate(0, col);
  2427. classNames = [
  2428. 'fc-col' + col,
  2429. 'fc-' + dayIDs[date.getDay()],
  2430. contentClass
  2431. ];
  2432. if (+date == +today) {
  2433. classNames.push(
  2434. tm + '-state-highlight',
  2435. 'fc-today'
  2436. );
  2437. }
  2438. else if (date < today) {
  2439. classNames.push('fc-past');
  2440. }
  2441. else {
  2442. classNames.push('fc-future');
  2443. }
  2444. cellHTML =
  2445. "<td class='" + classNames.join(' ') + "'>" +
  2446. "<div>" +
  2447. "<div class='fc-day-content'>" +
  2448. "<div style='position:relative'>&nbsp;</div>" +
  2449. "</div>" +
  2450. "</div>" +
  2451. "</td>";
  2452. cellsHTML += cellHTML;
  2453. }
  2454. html += cellsHTML;
  2455. html +=
  2456. "<td class='fc-agenda-gutter " + contentClass + "'>&nbsp;</td>" +
  2457. "</tr>" +
  2458. "</tbody>";
  2459. return html;
  2460. }
  2461. // TODO: data-date on the cells
  2462. /* Dimensions
  2463. -----------------------------------------------------------------------*/
  2464. function setHeight(height) {
  2465. if (height === undefined) {
  2466. height = viewHeight;
  2467. }
  2468. viewHeight = height;
  2469. slotTopCache = {};
  2470. var headHeight = dayBody.position().top;
  2471. var allDayHeight = slotScroller.position().top; // including divider
  2472. var bodyHeight = Math.min( // total body height, including borders
  2473. height - headHeight, // when scrollbars
  2474. slotTable.height() + allDayHeight + 1 // when no scrollbars. +1 for bottom border
  2475. );
  2476. dayBodyFirstCellStretcher
  2477. .height(bodyHeight - vsides(dayBodyFirstCell));
  2478. slotLayer.css('top', headHeight);
  2479. slotScroller.height(bodyHeight - allDayHeight - 1);
  2480. // the stylesheet guarantees that the first row has no border.
  2481. // this allows .height() to work well cross-browser.
  2482. slotHeight = slotTable.find('tr:first').height() + 1; // +1 for bottom border
  2483. snapRatio = opt('slotMinutes') / snapMinutes;
  2484. snapHeight = slotHeight / snapRatio;
  2485. }
  2486. function setWidth(width) {
  2487. viewWidth = width;
  2488. colPositions.clear();
  2489. colContentPositions.clear();
  2490. var axisFirstCells = dayHead.find('th:first');
  2491. if (allDayTable) {
  2492. axisFirstCells = axisFirstCells.add(allDayTable.find('th:first'));
  2493. }
  2494. axisFirstCells = axisFirstCells.add(slotTable.find('th:first'));
  2495. axisWidth = 0;
  2496. setOuterWidth(
  2497. axisFirstCells
  2498. .width('')
  2499. .each(function(i, _cell) {
  2500. axisWidth = Math.max(axisWidth, $(_cell).outerWidth());
  2501. }),
  2502. axisWidth
  2503. );
  2504. var gutterCells = dayTable.find('.fc-agenda-gutter');
  2505. if (allDayTable) {
  2506. gutterCells = gutterCells.add(allDayTable.find('th.fc-agenda-gutter'));
  2507. }
  2508. var slotTableWidth = slotScroller[0].clientWidth; // needs to be done after axisWidth (for IE7)
  2509. gutterWidth = slotScroller.width() - slotTableWidth;
  2510. if (gutterWidth) {
  2511. setOuterWidth(gutterCells, gutterWidth);
  2512. gutterCells
  2513. .show()
  2514. .prev()
  2515. .removeClass('fc-last');
  2516. }else{
  2517. gutterCells
  2518. .hide()
  2519. .prev()
  2520. .addClass('fc-last');
  2521. }
  2522. colWidth = Math.floor((slotTableWidth - axisWidth) / colCnt);
  2523. setOuterWidth(dayHeadCells.slice(0, -1), colWidth);
  2524. }
  2525. /* Scrolling
  2526. -----------------------------------------------------------------------*/
  2527. function resetScroll() {
  2528. var d0 = zeroDate();
  2529. var scrollDate = cloneDate(d0);
  2530. scrollDate.setHours(opt('firstHour'));
  2531. var top = timePosition(d0, scrollDate) + 1; // +1 for the border
  2532. function scroll() {
  2533. slotScroller.scrollTop(top);
  2534. }
  2535. scroll();
  2536. setTimeout(scroll, 0); // overrides any previous scroll state made by the browser
  2537. }
  2538. function afterRender() { // after the view has been freshly rendered and sized
  2539. resetScroll();
  2540. }
  2541. /* Slot/Day clicking and binding
  2542. -----------------------------------------------------------------------*/
  2543. function dayBind(cells) {
  2544. cells.click(slotClick)
  2545. .mousedown(daySelectionMousedown);
  2546. }
  2547. function slotBind(cells) {
  2548. cells.click(slotClick)
  2549. .mousedown(slotSelectionMousedown);
  2550. }
  2551. function slotClick(ev) {
  2552. if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick
  2553. var col = Math.min(colCnt-1, Math.floor((ev.pageX - dayTable.offset().left - axisWidth) / colWidth));
  2554. var date = cellToDate(0, col);
  2555. var rowMatch = this.parentNode.className.match(/fc-slot(\d+)/); // TODO: maybe use data
  2556. if (rowMatch) {
  2557. var mins = parseInt(rowMatch[1]) * opt('slotMinutes');
  2558. var hours = Math.floor(mins/60);
  2559. date.setHours(hours);
  2560. date.setMinutes(mins%60 + minMinute);
  2561. trigger('dayClick', dayBodyCells[col], date, false, ev);
  2562. }else{
  2563. trigger('dayClick', dayBodyCells[col], date, true, ev);
  2564. }
  2565. }
  2566. }
  2567. /* Semi-transparent Overlay Helpers
  2568. -----------------------------------------------------*/
  2569. // TODO: should be consolidated with BasicView's methods
  2570. function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive
  2571. if (refreshCoordinateGrid) {
  2572. coordinateGrid.build();
  2573. }
  2574. var segments = rangeToSegments(overlayStart, overlayEnd);
  2575. for (var i=0; i<segments.length; i++) {
  2576. var segment = segments[i];
  2577. dayBind(
  2578. renderCellOverlay(
  2579. segment.row,
  2580. segment.leftCol,
  2581. segment.row,
  2582. segment.rightCol
  2583. )
  2584. );
  2585. }
  2586. }
  2587. function renderCellOverlay(row0, col0, row1, col1) { // only for all-day?
  2588. var rect = coordinateGrid.rect(row0, col0, row1, col1, slotLayer);
  2589. return renderOverlay(rect, slotLayer);
  2590. }
  2591. function renderSlotOverlay(overlayStart, overlayEnd) {
  2592. for (var i=0; i<colCnt; i++) {
  2593. var dayStart = cellToDate(0, i);
  2594. var dayEnd = addDays(cloneDate(dayStart), 1);
  2595. var stretchStart = new Date(Math.max(dayStart, overlayStart));
  2596. var stretchEnd = new Date(Math.min(dayEnd, overlayEnd));
  2597. if (stretchStart < stretchEnd) {
  2598. var rect = coordinateGrid.rect(0, i, 0, i, slotContainer); // only use it for horizontal coords
  2599. var top = timePosition(dayStart, stretchStart);
  2600. var bottom = timePosition(dayStart, stretchEnd);
  2601. rect.top = top;
  2602. rect.height = bottom - top;
  2603. slotBind(
  2604. renderOverlay(rect, slotContainer)
  2605. );
  2606. }
  2607. }
  2608. }
  2609. /* Coordinate Utilities
  2610. -----------------------------------------------------------------------------*/
  2611. coordinateGrid = new CoordinateGrid(function(rows, cols) {
  2612. var e, n, p;
  2613. dayHeadCells.each(function(i, _e) {
  2614. e = $(_e);
  2615. n = e.offset().left;
  2616. if (i) {
  2617. p[1] = n;
  2618. }
  2619. p = [n];
  2620. cols[i] = p;
  2621. });
  2622. p[1] = n + e.outerWidth();
  2623. if (opt('allDaySlot')) {
  2624. e = allDayRow;
  2625. n = e.offset().top;
  2626. rows[0] = [n, n+e.outerHeight()];
  2627. }
  2628. var slotTableTop = slotContainer.offset().top;
  2629. var slotScrollerTop = slotScroller.offset().top;
  2630. var slotScrollerBottom = slotScrollerTop + slotScroller.outerHeight();
  2631. function constrain(n) {
  2632. return Math.max(slotScrollerTop, Math.min(slotScrollerBottom, n));
  2633. }
  2634. for (var i=0; i<slotCnt*snapRatio; i++) { // adapt slot count to increased/decreased selection slot count
  2635. rows.push([
  2636. constrain(slotTableTop + snapHeight*i),
  2637. constrain(slotTableTop + snapHeight*(i+1))
  2638. ]);
  2639. }
  2640. });
  2641. hoverListener = new HoverListener(coordinateGrid);
  2642. colPositions = new HorizontalPositionCache(function(col) {
  2643. return dayBodyCellInners.eq(col);
  2644. });
  2645. colContentPositions = new HorizontalPositionCache(function(col) {
  2646. return dayBodyCellContentInners.eq(col);
  2647. });
  2648. function colLeft(col) {
  2649. return colPositions.left(col);
  2650. }
  2651. function colContentLeft(col) {
  2652. return colContentPositions.left(col);
  2653. }
  2654. function colRight(col) {
  2655. return colPositions.right(col);
  2656. }
  2657. function colContentRight(col) {
  2658. return colContentPositions.right(col);
  2659. }
  2660. function getIsCellAllDay(cell) {
  2661. return opt('allDaySlot') && !cell.row;
  2662. }
  2663. function realCellToDate(cell) { // ugh "real" ... but blame it on our abuse of the "cell" system
  2664. var d = cellToDate(0, cell.col);
  2665. var slotIndex = cell.row;
  2666. if (opt('allDaySlot')) {
  2667. slotIndex--;
  2668. }
  2669. if (slotIndex >= 0) {
  2670. addMinutes(d, minMinute + slotIndex * snapMinutes);
  2671. }
  2672. return d;
  2673. }
  2674. // get the Y coordinate of the given time on the given day (both Date objects)
  2675. function timePosition(day, time) { // both date objects. day holds 00:00 of current day
  2676. day = cloneDate(day, true);
  2677. if (time < addMinutes(cloneDate(day), minMinute)) {
  2678. return 0;
  2679. }
  2680. if (time >= addMinutes(cloneDate(day), maxMinute)) {
  2681. return slotTable.height();
  2682. }
  2683. var slotMinutes = opt('slotMinutes'),
  2684. minutes = time.getHours()*60 + time.getMinutes() - minMinute,
  2685. slotI = Math.floor(minutes / slotMinutes),
  2686. slotTop = slotTopCache[slotI];
  2687. if (slotTop === undefined) {
  2688. slotTop = slotTopCache[slotI] =
  2689. slotTable.find('tr').eq(slotI).find('td div')[0].offsetTop;
  2690. // .eq() is faster than ":eq()" selector
  2691. // [0].offsetTop is faster than .position().top (do we really need this optimization?)
  2692. // a better optimization would be to cache all these divs
  2693. }
  2694. return Math.max(0, Math.round(
  2695. slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes)
  2696. ));
  2697. }
  2698. function getAllDayRow(index) {
  2699. return allDayRow;
  2700. }
  2701. function defaultEventEnd(event) {
  2702. var start = cloneDate(event.start);
  2703. if (event.allDay) {
  2704. return start;
  2705. }
  2706. return addMinutes(start, opt('defaultEventMinutes'));
  2707. }
  2708. /* Selection
  2709. ---------------------------------------------------------------------------------*/
  2710. function defaultSelectionEnd(startDate, allDay) {
  2711. if (allDay) {
  2712. return cloneDate(startDate);
  2713. }
  2714. return addMinutes(cloneDate(startDate), opt('slotMinutes'));
  2715. }
  2716. function renderSelection(startDate, endDate, allDay) { // only for all-day
  2717. if (allDay) {
  2718. if (opt('allDaySlot')) {
  2719. renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true);
  2720. }
  2721. }else{
  2722. renderSlotSelection(startDate, endDate);
  2723. }
  2724. }
  2725. function renderSlotSelection(startDate, endDate) {
  2726. var helperOption = opt('selectHelper');
  2727. coordinateGrid.build();
  2728. if (helperOption) {
  2729. var col = dateToCell(startDate).col;
  2730. if (col >= 0 && col < colCnt) { // only works when times are on same day
  2731. var rect = coordinateGrid.rect(0, col, 0, col, slotContainer); // only for horizontal coords
  2732. var top = timePosition(startDate, startDate);
  2733. var bottom = timePosition(startDate, endDate);
  2734. if (bottom > top) { // protect against selections that are entirely before or after visible range
  2735. rect.top = top;
  2736. rect.height = bottom - top;
  2737. rect.left += 2;
  2738. rect.width -= 5;
  2739. if ($.isFunction(helperOption)) {
  2740. var helperRes = helperOption(startDate, endDate);
  2741. if (helperRes) {
  2742. rect.position = 'absolute';
  2743. selectionHelper = $(helperRes)
  2744. .css(rect)
  2745. .appendTo(slotContainer);
  2746. }
  2747. }else{
  2748. rect.isStart = true; // conside rect a "seg" now
  2749. rect.isEnd = true; //
  2750. selectionHelper = $(slotSegHtml(
  2751. {
  2752. title: '',
  2753. start: startDate,
  2754. end: endDate,
  2755. className: ['fc-select-helper'],
  2756. editable: false
  2757. },
  2758. rect
  2759. ));
  2760. selectionHelper.css('opacity', opt('dragOpacity'));
  2761. }
  2762. if (selectionHelper) {
  2763. slotBind(selectionHelper);
  2764. slotContainer.append(selectionHelper);
  2765. setOuterWidth(selectionHelper, rect.width, true); // needs to be after appended
  2766. setOuterHeight(selectionHelper, rect.height, true);
  2767. }
  2768. }
  2769. }
  2770. }else{
  2771. renderSlotOverlay(startDate, endDate);
  2772. }
  2773. }
  2774. function clearSelection() {
  2775. clearOverlays();
  2776. if (selectionHelper) {
  2777. selectionHelper.remove();
  2778. selectionHelper = null;
  2779. }
  2780. }
  2781. function slotSelectionMousedown(ev) {
  2782. if (ev.which == 1 && opt('selectable')) { // ev.which==1 means left mouse button
  2783. unselect(ev);
  2784. var dates;
  2785. hoverListener.start(function(cell, origCell) {
  2786. clearSelection();
  2787. if (cell && cell.col == origCell.col && !getIsCellAllDay(cell)) {
  2788. var d1 = realCellToDate(origCell);
  2789. var d2 = realCellToDate(cell);
  2790. dates = [
  2791. d1,
  2792. addMinutes(cloneDate(d1), snapMinutes), // calculate minutes depending on selection slot minutes
  2793. d2,
  2794. addMinutes(cloneDate(d2), snapMinutes)
  2795. ].sort(dateCompare);
  2796. renderSlotSelection(dates[0], dates[3]);
  2797. }else{
  2798. dates = null;
  2799. }
  2800. }, ev);
  2801. $(document).one('mouseup', function(ev) {
  2802. hoverListener.stop();
  2803. if (dates) {
  2804. if (+dates[0] == +dates[1]) {
  2805. reportDayClick(dates[0], false, ev);
  2806. }
  2807. reportSelection(dates[0], dates[3], false, ev);
  2808. }
  2809. });
  2810. }
  2811. }
  2812. function reportDayClick(date, allDay, ev) {
  2813. trigger('dayClick', dayBodyCells[dateToCell(date).col], date, allDay, ev);
  2814. }
  2815. /* External Dragging
  2816. --------------------------------------------------------------------------------*/
  2817. function dragStart(_dragElement, ev, ui) {
  2818. hoverListener.start(function(cell) {
  2819. clearOverlays();
  2820. if (cell) {
  2821. if (getIsCellAllDay(cell)) {
  2822. renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
  2823. }else{
  2824. var d1 = realCellToDate(cell);
  2825. var d2 = addMinutes(cloneDate(d1), opt('defaultEventMinutes'));
  2826. renderSlotOverlay(d1, d2);
  2827. }
  2828. }
  2829. }, ev);
  2830. }
  2831. function dragStop(_dragElement, ev, ui) {
  2832. var cell = hoverListener.stop();
  2833. clearOverlays();
  2834. if (cell) {
  2835. trigger('drop', _dragElement, realCellToDate(cell), getIsCellAllDay(cell), ev, ui);
  2836. }
  2837. }
  2838. }
  2839. ;;
  2840. function AgendaEventRenderer() {
  2841. var t = this;
  2842. // exports
  2843. t.renderEvents = renderEvents;
  2844. t.clearEvents = clearEvents;
  2845. t.slotSegHtml = slotSegHtml;
  2846. // imports
  2847. DayEventRenderer.call(t);
  2848. var opt = t.opt;
  2849. var trigger = t.trigger;
  2850. var isEventDraggable = t.isEventDraggable;
  2851. var isEventResizable = t.isEventResizable;
  2852. var eventEnd = t.eventEnd;
  2853. var eventElementHandlers = t.eventElementHandlers;
  2854. var setHeight = t.setHeight;
  2855. var getDaySegmentContainer = t.getDaySegmentContainer;
  2856. var getSlotSegmentContainer = t.getSlotSegmentContainer;
  2857. var getHoverListener = t.getHoverListener;
  2858. var getMaxMinute = t.getMaxMinute;
  2859. var getMinMinute = t.getMinMinute;
  2860. var timePosition = t.timePosition;
  2861. var getIsCellAllDay = t.getIsCellAllDay;
  2862. var colContentLeft = t.colContentLeft;
  2863. var colContentRight = t.colContentRight;
  2864. var cellToDate = t.cellToDate;
  2865. var getColCnt = t.getColCnt;
  2866. var getColWidth = t.getColWidth;
  2867. var getSnapHeight = t.getSnapHeight;
  2868. var getSnapMinutes = t.getSnapMinutes;
  2869. var getSlotContainer = t.getSlotContainer;
  2870. var reportEventElement = t.reportEventElement;
  2871. var showEvents = t.showEvents;
  2872. var hideEvents = t.hideEvents;
  2873. var eventDrop = t.eventDrop;
  2874. var eventResize = t.eventResize;
  2875. var renderDayOverlay = t.renderDayOverlay;
  2876. var clearOverlays = t.clearOverlays;
  2877. var renderDayEvents = t.renderDayEvents;
  2878. var calendar = t.calendar;
  2879. var formatDate = calendar.formatDate;
  2880. var formatDates = calendar.formatDates;
  2881. // overrides
  2882. t.draggableDayEvent = draggableDayEvent;
  2883. /* Rendering
  2884. ----------------------------------------------------------------------------*/
  2885. function renderEvents(events, modifiedEventId) {
  2886. var i, len=events.length,
  2887. dayEvents=[],
  2888. slotEvents=[];
  2889. for (i=0; i<len; i++) {
  2890. if (events[i].allDay) {
  2891. dayEvents.push(events[i]);
  2892. }else{
  2893. slotEvents.push(events[i]);
  2894. }
  2895. }
  2896. if (opt('allDaySlot')) {
  2897. renderDayEvents(dayEvents, modifiedEventId);
  2898. setHeight(); // no params means set to viewHeight
  2899. }
  2900. renderSlotSegs(compileSlotSegs(slotEvents), modifiedEventId);
  2901. }
  2902. function clearEvents() {
  2903. getDaySegmentContainer().empty();
  2904. getSlotSegmentContainer().empty();
  2905. }
  2906. function compileSlotSegs(events) {
  2907. var colCnt = getColCnt(),
  2908. minMinute = getMinMinute(),
  2909. maxMinute = getMaxMinute(),
  2910. d,
  2911. visEventEnds = $.map(events, slotEventEnd),
  2912. i,
  2913. j, seg,
  2914. colSegs,
  2915. segs = [];
  2916. for (i=0; i<colCnt; i++) {
  2917. d = cellToDate(0, i);
  2918. addMinutes(d, minMinute);
  2919. colSegs = sliceSegs(
  2920. events,
  2921. visEventEnds,
  2922. d,
  2923. addMinutes(cloneDate(d), maxMinute-minMinute)
  2924. );
  2925. colSegs = placeSlotSegs(colSegs); // returns a new order
  2926. for (j=0; j<colSegs.length; j++) {
  2927. seg = colSegs[j];
  2928. seg.col = i;
  2929. segs.push(seg);
  2930. }
  2931. }
  2932. return segs;
  2933. }
  2934. function sliceSegs(events, visEventEnds, start, end) {
  2935. var segs = [],
  2936. i, len=events.length, event,
  2937. eventStart, eventEnd,
  2938. segStart, segEnd,
  2939. isStart, isEnd;
  2940. for (i=0; i<len; i++) {
  2941. event = events[i];
  2942. eventStart = event.start;
  2943. eventEnd = visEventEnds[i];
  2944. if (eventEnd > start && eventStart < end) {
  2945. if (eventStart < start) {
  2946. segStart = cloneDate(start);
  2947. isStart = false;
  2948. }else{
  2949. segStart = eventStart;
  2950. isStart = true;
  2951. }
  2952. if (eventEnd > end) {
  2953. segEnd = cloneDate(end);
  2954. isEnd = false;
  2955. }else{
  2956. segEnd = eventEnd;
  2957. isEnd = true;
  2958. }
  2959. segs.push({
  2960. event: event,
  2961. start: segStart,
  2962. end: segEnd,
  2963. isStart: isStart,
  2964. isEnd: isEnd
  2965. });
  2966. }
  2967. }
  2968. return segs.sort(compareSlotSegs);
  2969. }
  2970. function slotEventEnd(event) {
  2971. if (event.end) {
  2972. return cloneDate(event.end);
  2973. }else{
  2974. return addMinutes(cloneDate(event.start), opt('defaultEventMinutes'));
  2975. }
  2976. }
  2977. // renders events in the 'time slots' at the bottom
  2978. // TODO: when we refactor this, when user returns `false` eventRender, don't have empty space
  2979. // TODO: refactor will include using pixels to detect collisions instead of dates (handy for seg cmp)
  2980. function renderSlotSegs(segs, modifiedEventId) {
  2981. var i, segCnt=segs.length, seg,
  2982. event,
  2983. top,
  2984. bottom,
  2985. columnLeft,
  2986. columnRight,
  2987. columnWidth,
  2988. width,
  2989. left,
  2990. right,
  2991. html = '',
  2992. eventElements,
  2993. eventElement,
  2994. triggerRes,
  2995. titleElement,
  2996. height,
  2997. slotSegmentContainer = getSlotSegmentContainer(),
  2998. isRTL = opt('isRTL');
  2999. // calculate position/dimensions, create html
  3000. for (i=0; i<segCnt; i++) {
  3001. seg = segs[i];
  3002. event = seg.event;
  3003. top = timePosition(seg.start, seg.start);
  3004. bottom = timePosition(seg.start, seg.end);
  3005. columnLeft = colContentLeft(seg.col);
  3006. columnRight = colContentRight(seg.col);
  3007. columnWidth = columnRight - columnLeft;
  3008. // shave off space on right near scrollbars (2.5%)
  3009. // TODO: move this to CSS somehow
  3010. columnRight -= columnWidth * .025;
  3011. columnWidth = columnRight - columnLeft;
  3012. width = columnWidth * (seg.forwardCoord - seg.backwardCoord);
  3013. if (opt('slotEventOverlap')) {
  3014. // double the width while making sure resize handle is visible
  3015. // (assumed to be 20px wide)
  3016. width = Math.max(
  3017. (width - (20/2)) * 2,
  3018. width // narrow columns will want to make the segment smaller than
  3019. // the natural width. don't allow it
  3020. );
  3021. }
  3022. if (isRTL) {
  3023. right = columnRight - seg.backwardCoord * columnWidth;
  3024. left = right - width;
  3025. }
  3026. else {
  3027. left = columnLeft + seg.backwardCoord * columnWidth;
  3028. right = left + width;
  3029. }
  3030. // make sure horizontal coordinates are in bounds
  3031. left = Math.max(left, columnLeft);
  3032. right = Math.min(right, columnRight);
  3033. width = right - left;
  3034. seg.top = top;
  3035. seg.left = left;
  3036. seg.outerWidth = width;
  3037. seg.outerHeight = bottom - top;
  3038. html += slotSegHtml(event, seg);
  3039. }
  3040. slotSegmentContainer[0].innerHTML = html; // faster than html()
  3041. eventElements = slotSegmentContainer.children();
  3042. // retrieve elements, run through eventRender callback, bind event handlers
  3043. for (i=0; i<segCnt; i++) {
  3044. seg = segs[i];
  3045. event = seg.event;
  3046. eventElement = $(eventElements[i]); // faster than eq()
  3047. triggerRes = trigger('eventRender', event, event, eventElement);
  3048. if (triggerRes === false) {
  3049. eventElement.remove();
  3050. }else{
  3051. if (triggerRes && triggerRes !== true) {
  3052. eventElement.remove();
  3053. eventElement = $(triggerRes)
  3054. .css({
  3055. position: 'absolute',
  3056. top: seg.top,
  3057. left: seg.left
  3058. })
  3059. .appendTo(slotSegmentContainer);
  3060. }
  3061. seg.element = eventElement;
  3062. if (event._id === modifiedEventId) {
  3063. bindSlotSeg(event, eventElement, seg);
  3064. }else{
  3065. eventElement[0]._fci = i; // for lazySegBind
  3066. }
  3067. reportEventElement(event, eventElement);
  3068. }
  3069. }
  3070. lazySegBind(slotSegmentContainer, segs, bindSlotSeg);
  3071. // record event sides and title positions
  3072. for (i=0; i<segCnt; i++) {
  3073. seg = segs[i];
  3074. if (eventElement = seg.element) {
  3075. seg.vsides = vsides(eventElement, true);
  3076. seg.hsides = hsides(eventElement, true);
  3077. titleElement = eventElement.find('.fc-event-title');
  3078. if (titleElement.length) {
  3079. seg.contentTop = titleElement[0].offsetTop;
  3080. }
  3081. }
  3082. }
  3083. // set all positions/dimensions at once
  3084. for (i=0; i<segCnt; i++) {
  3085. seg = segs[i];
  3086. if (eventElement = seg.element) {
  3087. eventElement[0].style.width = Math.max(0, seg.outerWidth - seg.hsides) + 'px';
  3088. height = Math.max(0, seg.outerHeight - seg.vsides);
  3089. eventElement[0].style.height = height + 'px';
  3090. event = seg.event;
  3091. if (seg.contentTop !== undefined && height - seg.contentTop < 10) {
  3092. // not enough room for title, put it in the time (TODO: maybe make both display:inline instead)
  3093. eventElement.find('div.fc-event-time')
  3094. .text(formatDate(event.start, opt('timeFormat')) + ' - ' + event.title);
  3095. eventElement.find('div.fc-event-title')
  3096. .remove();
  3097. }
  3098. trigger('eventAfterRender', event, event, eventElement);
  3099. }
  3100. }
  3101. }
  3102. function slotSegHtml(event, seg) {
  3103. var html = "<";
  3104. var url = event.url;
  3105. var skinCss = getSkinCss(event, opt);
  3106. var classes = ['fc-event', 'fc-event-vert'];
  3107. if (isEventDraggable(event)) {
  3108. classes.push('fc-event-draggable');
  3109. }
  3110. if (seg.isStart) {
  3111. classes.push('fc-event-start');
  3112. }
  3113. if (seg.isEnd) {
  3114. classes.push('fc-event-end');
  3115. }
  3116. classes = classes.concat(event.className);
  3117. if (event.source) {
  3118. classes = classes.concat(event.source.className || []);
  3119. }
  3120. if (url) {
  3121. html += "a href='" + htmlEscape(event.url) + "'";
  3122. }else{
  3123. html += "div";
  3124. }
  3125. html +=
  3126. " class='" + classes.join(' ') + "'" +
  3127. " style=" +
  3128. "'" +
  3129. "position:absolute;" +
  3130. "top:" + seg.top + "px;" +
  3131. "left:" + seg.left + "px;" +
  3132. skinCss +
  3133. "'" +
  3134. ">" +
  3135. "<div class='fc-event-inner'>" +
  3136. "<div class='fc-event-time'>" +
  3137. htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) +
  3138. "</div>" +
  3139. "<div class='fc-event-title'>" +
  3140. htmlEscape(event.title || '') +
  3141. "</div>" +
  3142. "</div>" +
  3143. "<div class='fc-event-bg'></div>";
  3144. if (seg.isEnd && isEventResizable(event)) {
  3145. html +=
  3146. "<div class='ui-resizable-handle ui-resizable-s'>=</div>";
  3147. }
  3148. html +=
  3149. "</" + (url ? "a" : "div") + ">";
  3150. return html;
  3151. }
  3152. function bindSlotSeg(event, eventElement, seg) {
  3153. var timeElement = eventElement.find('div.fc-event-time');
  3154. if (isEventDraggable(event)) {
  3155. draggableSlotEvent(event, eventElement, timeElement);
  3156. }
  3157. if (seg.isEnd && isEventResizable(event)) {
  3158. resizableSlotEvent(event, eventElement, timeElement);
  3159. }
  3160. eventElementHandlers(event, eventElement);
  3161. }
  3162. /* Dragging
  3163. -----------------------------------------------------------------------------------*/
  3164. // when event starts out FULL-DAY
  3165. // overrides DayEventRenderer's version because it needs to account for dragging elements
  3166. // to and from the slot area.
  3167. function draggableDayEvent(event, eventElement, seg) {
  3168. var isStart = seg.isStart;
  3169. var origWidth;
  3170. var revert;
  3171. var allDay = true;
  3172. var dayDelta;
  3173. var hoverListener = getHoverListener();
  3174. var colWidth = getColWidth();
  3175. var snapHeight = getSnapHeight();
  3176. var snapMinutes = getSnapMinutes();
  3177. var minMinute = getMinMinute();
  3178. eventElement.draggable({
  3179. opacity: opt('dragOpacity', 'month'), // use whatever the month view was using
  3180. revertDuration: opt('dragRevertDuration'),
  3181. start: function(ev, ui) {
  3182. trigger('eventDragStart', eventElement, event, ev, ui);
  3183. hideEvents(event, eventElement);
  3184. origWidth = eventElement.width();
  3185. hoverListener.start(function(cell, origCell) {
  3186. clearOverlays();
  3187. if (cell) {
  3188. revert = false;
  3189. var origDate = cellToDate(0, origCell.col);
  3190. var date = cellToDate(0, cell.col);
  3191. dayDelta = dayDiff(date, origDate);
  3192. if (!cell.row) {
  3193. // on full-days
  3194. renderDayOverlay(
  3195. addDays(cloneDate(event.start), dayDelta),
  3196. addDays(exclEndDay(event), dayDelta)
  3197. );
  3198. resetElement();
  3199. }else{
  3200. // mouse is over bottom slots
  3201. if (isStart) {
  3202. if (allDay) {
  3203. // convert event to temporary slot-event
  3204. eventElement.width(colWidth - 10); // don't use entire width
  3205. setOuterHeight(
  3206. eventElement,
  3207. snapHeight * Math.round(
  3208. (event.end ? ((event.end - event.start) / MINUTE_MS) : opt('defaultEventMinutes')) /
  3209. snapMinutes
  3210. )
  3211. );
  3212. eventElement.draggable('option', 'grid', [colWidth, 1]);
  3213. allDay = false;
  3214. }
  3215. }else{
  3216. revert = true;
  3217. }
  3218. }
  3219. revert = revert || (allDay && !dayDelta);
  3220. }else{
  3221. resetElement();
  3222. revert = true;
  3223. }
  3224. eventElement.draggable('option', 'revert', revert);
  3225. }, ev, 'drag');
  3226. },
  3227. stop: function(ev, ui) {
  3228. hoverListener.stop();
  3229. clearOverlays();
  3230. trigger('eventDragStop', eventElement, event, ev, ui);
  3231. if (revert) {
  3232. // hasn't moved or is out of bounds (draggable has already reverted)
  3233. resetElement();
  3234. eventElement.css('filter', ''); // clear IE opacity side-effects
  3235. showEvents(event, eventElement);
  3236. }else{
  3237. // changed!
  3238. var minuteDelta = 0;
  3239. if (!allDay) {
  3240. minuteDelta = Math.round((eventElement.offset().top - getSlotContainer().offset().top) / snapHeight)
  3241. * snapMinutes
  3242. + minMinute
  3243. - (event.start.getHours() * 60 + event.start.getMinutes());
  3244. }
  3245. eventDrop(this, event, dayDelta, minuteDelta, allDay, ev, ui);
  3246. }
  3247. }
  3248. });
  3249. function resetElement() {
  3250. if (!allDay) {
  3251. eventElement
  3252. .width(origWidth)
  3253. .height('')
  3254. .draggable('option', 'grid', null);
  3255. allDay = true;
  3256. }
  3257. }
  3258. }
  3259. // when event starts out IN TIMESLOTS
  3260. function draggableSlotEvent(event, eventElement, timeElement) {
  3261. var coordinateGrid = t.getCoordinateGrid();
  3262. var colCnt = getColCnt();
  3263. var colWidth = getColWidth();
  3264. var snapHeight = getSnapHeight();
  3265. var snapMinutes = getSnapMinutes();
  3266. // states
  3267. var origPosition; // original position of the element, not the mouse
  3268. var origCell;
  3269. var isInBounds, prevIsInBounds;
  3270. var isAllDay, prevIsAllDay;
  3271. var colDelta, prevColDelta;
  3272. var dayDelta; // derived from colDelta
  3273. var minuteDelta, prevMinuteDelta;
  3274. eventElement.draggable({
  3275. scroll: false,
  3276. grid: [ colWidth, snapHeight ],
  3277. axis: colCnt==1 ? 'y' : false,
  3278. opacity: opt('dragOpacity'),
  3279. revertDuration: opt('dragRevertDuration'),
  3280. start: function(ev, ui) {
  3281. trigger('eventDragStart', eventElement, event, ev, ui);
  3282. hideEvents(event, eventElement);
  3283. coordinateGrid.build();
  3284. // initialize states
  3285. origPosition = eventElement.position();
  3286. origCell = coordinateGrid.cell(ev.pageX, ev.pageY);
  3287. isInBounds = prevIsInBounds = true;
  3288. isAllDay = prevIsAllDay = getIsCellAllDay(origCell);
  3289. colDelta = prevColDelta = 0;
  3290. dayDelta = 0;
  3291. minuteDelta = prevMinuteDelta = 0;
  3292. },
  3293. drag: function(ev, ui) {
  3294. // NOTE: this `cell` value is only useful for determining in-bounds and all-day.
  3295. // Bad for anything else due to the discrepancy between the mouse position and the
  3296. // element position while snapping. (problem revealed in PR #55)
  3297. //
  3298. // PS- the problem exists for draggableDayEvent() when dragging an all-day event to a slot event.
  3299. // We should overhaul the dragging system and stop relying on jQuery UI.
  3300. var cell = coordinateGrid.cell(ev.pageX, ev.pageY);
  3301. // update states
  3302. isInBounds = !!cell;
  3303. if (isInBounds) {
  3304. isAllDay = getIsCellAllDay(cell);
  3305. // calculate column delta
  3306. colDelta = Math.round((ui.position.left - origPosition.left) / colWidth);
  3307. if (colDelta != prevColDelta) {
  3308. // calculate the day delta based off of the original clicked column and the column delta
  3309. var origDate = cellToDate(0, origCell.col);
  3310. var col = origCell.col + colDelta;
  3311. col = Math.max(0, col);
  3312. col = Math.min(colCnt-1, col);
  3313. var date = cellToDate(0, col);
  3314. dayDelta = dayDiff(date, origDate);
  3315. }
  3316. // calculate minute delta (only if over slots)
  3317. if (!isAllDay) {
  3318. minuteDelta = Math.round((ui.position.top - origPosition.top) / snapHeight) * snapMinutes;
  3319. }
  3320. }
  3321. // any state changes?
  3322. if (
  3323. isInBounds != prevIsInBounds ||
  3324. isAllDay != prevIsAllDay ||
  3325. colDelta != prevColDelta ||
  3326. minuteDelta != prevMinuteDelta
  3327. ) {
  3328. updateUI();
  3329. // update previous states for next time
  3330. prevIsInBounds = isInBounds;
  3331. prevIsAllDay = isAllDay;
  3332. prevColDelta = colDelta;
  3333. prevMinuteDelta = minuteDelta;
  3334. }
  3335. // if out-of-bounds, revert when done, and vice versa.
  3336. eventElement.draggable('option', 'revert', !isInBounds);
  3337. },
  3338. stop: function(ev, ui) {
  3339. clearOverlays();
  3340. trigger('eventDragStop', eventElement, event, ev, ui);
  3341. if (isInBounds && (isAllDay || dayDelta || minuteDelta)) { // changed!
  3342. eventDrop(this, event, dayDelta, isAllDay ? 0 : minuteDelta, isAllDay, ev, ui);
  3343. }
  3344. else { // either no change or out-of-bounds (draggable has already reverted)
  3345. // reset states for next time, and for updateUI()
  3346. isInBounds = true;
  3347. isAllDay = false;
  3348. colDelta = 0;
  3349. dayDelta = 0;
  3350. minuteDelta = 0;
  3351. updateUI();
  3352. eventElement.css('filter', ''); // clear IE opacity side-effects
  3353. // sometimes fast drags make event revert to wrong position, so reset.
  3354. // also, if we dragged the element out of the area because of snapping,
  3355. // but the *mouse* is still in bounds, we need to reset the position.
  3356. eventElement.css(origPosition);
  3357. showEvents(event, eventElement);
  3358. }
  3359. }
  3360. });
  3361. function updateUI() {
  3362. clearOverlays();
  3363. if (isInBounds) {
  3364. if (isAllDay) {
  3365. timeElement.hide();
  3366. eventElement.draggable('option', 'grid', null); // disable grid snapping
  3367. renderDayOverlay(
  3368. addDays(cloneDate(event.start), dayDelta),
  3369. addDays(exclEndDay(event), dayDelta)
  3370. );
  3371. }
  3372. else {
  3373. updateTimeText(minuteDelta);
  3374. timeElement.css('display', ''); // show() was causing display=inline
  3375. eventElement.draggable('option', 'grid', [colWidth, snapHeight]); // re-enable grid snapping
  3376. }
  3377. }
  3378. }
  3379. function updateTimeText(minuteDelta) {
  3380. var newStart = addMinutes(cloneDate(event.start), minuteDelta);
  3381. var newEnd;
  3382. if (event.end) {
  3383. newEnd = addMinutes(cloneDate(event.end), minuteDelta);
  3384. }
  3385. timeElement.text(formatDates(newStart, newEnd, opt('timeFormat')));
  3386. }
  3387. }
  3388. /* Resizing
  3389. --------------------------------------------------------------------------------------*/
  3390. function resizableSlotEvent(event, eventElement, timeElement) {
  3391. var snapDelta, prevSnapDelta;
  3392. var snapHeight = getSnapHeight();
  3393. var snapMinutes = getSnapMinutes();
  3394. eventElement.resizable({
  3395. handles: {
  3396. s: '.ui-resizable-handle'
  3397. },
  3398. grid: snapHeight,
  3399. start: function(ev, ui) {
  3400. snapDelta = prevSnapDelta = 0;
  3401. hideEvents(event, eventElement);
  3402. trigger('eventResizeStart', this, event, ev, ui);
  3403. },
  3404. resize: function(ev, ui) {
  3405. // don't rely on ui.size.height, doesn't take grid into account
  3406. snapDelta = Math.round((Math.max(snapHeight, eventElement.height()) - ui.originalSize.height) / snapHeight);
  3407. if (snapDelta != prevSnapDelta) {
  3408. timeElement.text(
  3409. formatDates(
  3410. event.start,
  3411. (!snapDelta && !event.end) ? null : // no change, so don't display time range
  3412. addMinutes(eventEnd(event), snapMinutes*snapDelta),
  3413. opt('timeFormat')
  3414. )
  3415. );
  3416. prevSnapDelta = snapDelta;
  3417. }
  3418. },
  3419. stop: function(ev, ui) {
  3420. trigger('eventResizeStop', this, event, ev, ui);
  3421. if (snapDelta) {
  3422. eventResize(this, event, 0, snapMinutes*snapDelta, ev, ui);
  3423. }else{
  3424. showEvents(event, eventElement);
  3425. // BUG: if event was really short, need to put title back in span
  3426. }
  3427. }
  3428. });
  3429. }
  3430. }
  3431. /* Agenda Event Segment Utilities
  3432. -----------------------------------------------------------------------------*/
  3433. // Sets the seg.backwardCoord and seg.forwardCoord on each segment and returns a new
  3434. // list in the order they should be placed into the DOM (an implicit z-index).
  3435. function placeSlotSegs(segs) {
  3436. var levels = buildSlotSegLevels(segs);
  3437. var level0 = levels[0];
  3438. var i;
  3439. computeForwardSlotSegs(levels);
  3440. if (level0) {
  3441. for (i=0; i<level0.length; i++) {
  3442. computeSlotSegPressures(level0[i]);
  3443. }
  3444. for (i=0; i<level0.length; i++) {
  3445. computeSlotSegCoords(level0[i], 0, 0);
  3446. }
  3447. }
  3448. return flattenSlotSegLevels(levels);
  3449. }
  3450. // Builds an array of segments "levels". The first level will be the leftmost tier of segments
  3451. // if the calendar is left-to-right, or the rightmost if the calendar is right-to-left.
  3452. function buildSlotSegLevels(segs) {
  3453. var levels = [];
  3454. var i, seg;
  3455. var j;
  3456. for (i=0; i<segs.length; i++) {
  3457. seg = segs[i];
  3458. // go through all the levels and stop on the first level where there are no collisions
  3459. for (j=0; j<levels.length; j++) {
  3460. if (!computeSlotSegCollisions(seg, levels[j]).length) {
  3461. break;
  3462. }
  3463. }
  3464. (levels[j] || (levels[j] = [])).push(seg);
  3465. }
  3466. return levels;
  3467. }
  3468. // For every segment, figure out the other segments that are in subsequent
  3469. // levels that also occupy the same vertical space. Accumulate in seg.forwardSegs
  3470. function computeForwardSlotSegs(levels) {
  3471. var i, level;
  3472. var j, seg;
  3473. var k;
  3474. for (i=0; i<levels.length; i++) {
  3475. level = levels[i];
  3476. for (j=0; j<level.length; j++) {
  3477. seg = level[j];
  3478. seg.forwardSegs = [];
  3479. for (k=i+1; k<levels.length; k++) {
  3480. computeSlotSegCollisions(seg, levels[k], seg.forwardSegs);
  3481. }
  3482. }
  3483. }
  3484. }
  3485. // Figure out which path forward (via seg.forwardSegs) results in the longest path until
  3486. // the furthest edge is reached. The number of segments in this path will be seg.forwardPressure
  3487. function computeSlotSegPressures(seg) {
  3488. var forwardSegs = seg.forwardSegs;
  3489. var forwardPressure = 0;
  3490. var i, forwardSeg;
  3491. if (seg.forwardPressure === undefined) { // not already computed
  3492. for (i=0; i<forwardSegs.length; i++) {
  3493. forwardSeg = forwardSegs[i];
  3494. // figure out the child's maximum forward path
  3495. computeSlotSegPressures(forwardSeg);
  3496. // either use the existing maximum, or use the child's forward pressure
  3497. // plus one (for the forwardSeg itself)
  3498. forwardPressure = Math.max(
  3499. forwardPressure,
  3500. 1 + forwardSeg.forwardPressure
  3501. );
  3502. }
  3503. seg.forwardPressure = forwardPressure;
  3504. }
  3505. }
  3506. // Calculate seg.forwardCoord and seg.backwardCoord for the segment, where both values range
  3507. // from 0 to 1. If the calendar is left-to-right, the seg.backwardCoord maps to "left" and
  3508. // seg.forwardCoord maps to "right" (via percentage). Vice-versa if the calendar is right-to-left.
  3509. //
  3510. // The segment might be part of a "series", which means consecutive segments with the same pressure
  3511. // who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of
  3512. // segments behind this one in the current series, and `seriesBackwardCoord` is the starting
  3513. // coordinate of the first segment in the series.
  3514. function computeSlotSegCoords(seg, seriesBackwardPressure, seriesBackwardCoord) {
  3515. var forwardSegs = seg.forwardSegs;
  3516. var i;
  3517. if (seg.forwardCoord === undefined) { // not already computed
  3518. if (!forwardSegs.length) {
  3519. // if there are no forward segments, this segment should butt up against the edge
  3520. seg.forwardCoord = 1;
  3521. }
  3522. else {
  3523. // sort highest pressure first
  3524. forwardSegs.sort(compareForwardSlotSegs);
  3525. // this segment's forwardCoord will be calculated from the backwardCoord of the
  3526. // highest-pressure forward segment.
  3527. computeSlotSegCoords(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord);
  3528. seg.forwardCoord = forwardSegs[0].backwardCoord;
  3529. }
  3530. // calculate the backwardCoord from the forwardCoord. consider the series
  3531. seg.backwardCoord = seg.forwardCoord -
  3532. (seg.forwardCoord - seriesBackwardCoord) / // available width for series
  3533. (seriesBackwardPressure + 1); // # of segments in the series
  3534. // use this segment's coordinates to computed the coordinates of the less-pressurized
  3535. // forward segments
  3536. for (i=0; i<forwardSegs.length; i++) {
  3537. computeSlotSegCoords(forwardSegs[i], 0, seg.forwardCoord);
  3538. }
  3539. }
  3540. }
  3541. // Outputs a flat array of segments, from lowest to highest level
  3542. function flattenSlotSegLevels(levels) {
  3543. var segs = [];
  3544. var i, level;
  3545. var j;
  3546. for (i=0; i<levels.length; i++) {
  3547. level = levels[i];
  3548. for (j=0; j<level.length; j++) {
  3549. segs.push(level[j]);
  3550. }
  3551. }
  3552. return segs;
  3553. }
  3554. // Find all the segments in `otherSegs` that vertically collide with `seg`.
  3555. // Append into an optionally-supplied `results` array and return.
  3556. function computeSlotSegCollisions(seg, otherSegs, results) {
  3557. results = results || [];
  3558. for (var i=0; i<otherSegs.length; i++) {
  3559. if (isSlotSegCollision(seg, otherSegs[i])) {
  3560. results.push(otherSegs[i]);
  3561. }
  3562. }
  3563. return results;
  3564. }
  3565. // Do these segments occupy the same vertical space?
  3566. function isSlotSegCollision(seg1, seg2) {
  3567. return seg1.end > seg2.start && seg1.start < seg2.end;
  3568. }
  3569. // A cmp function for determining which forward segment to rely on more when computing coordinates.
  3570. function compareForwardSlotSegs(seg1, seg2) {
  3571. // put higher-pressure first
  3572. return seg2.forwardPressure - seg1.forwardPressure ||
  3573. // put segments that are closer to initial edge first (and favor ones with no coords yet)
  3574. (seg1.backwardCoord || 0) - (seg2.backwardCoord || 0) ||
  3575. // do normal sorting...
  3576. compareSlotSegs(seg1, seg2);
  3577. }
  3578. // A cmp function for determining which segment should be closer to the initial edge
  3579. // (the left edge on a left-to-right calendar).
  3580. function compareSlotSegs(seg1, seg2) {
  3581. return seg1.start - seg2.start || // earlier start time goes first
  3582. (seg2.end - seg2.start) - (seg1.end - seg1.start) || // tie? longer-duration goes first
  3583. (seg1.event.title || '').localeCompare(seg2.event.title); // tie? alphabetically by title
  3584. }
  3585. ;;
  3586. function View(element, calendar, viewName) {
  3587. var t = this;
  3588. // exports
  3589. t.element = element;
  3590. t.calendar = calendar;
  3591. t.name = viewName;
  3592. t.opt = opt;
  3593. t.trigger = trigger;
  3594. t.isEventDraggable = isEventDraggable;
  3595. t.isEventResizable = isEventResizable;
  3596. t.setEventData = setEventData;
  3597. t.clearEventData = clearEventData;
  3598. t.eventEnd = eventEnd;
  3599. t.reportEventElement = reportEventElement;
  3600. t.triggerEventDestroy = triggerEventDestroy;
  3601. t.eventElementHandlers = eventElementHandlers;
  3602. t.showEvents = showEvents;
  3603. t.hideEvents = hideEvents;
  3604. t.eventDrop = eventDrop;
  3605. t.eventResize = eventResize;
  3606. // t.title
  3607. // t.start, t.end
  3608. // t.visStart, t.visEnd
  3609. // imports
  3610. var defaultEventEnd = t.defaultEventEnd;
  3611. var normalizeEvent = calendar.normalizeEvent; // in EventManager
  3612. var reportEventChange = calendar.reportEventChange;
  3613. // locals
  3614. var eventsByID = {}; // eventID mapped to array of events (there can be multiple b/c of repeating events)
  3615. var eventElementsByID = {}; // eventID mapped to array of jQuery elements
  3616. var eventElementCouples = []; // array of objects, { event, element } // TODO: unify with segment system
  3617. var options = calendar.options;
  3618. function opt(name, viewNameOverride) {
  3619. var v = options[name];
  3620. if ($.isPlainObject(v)) {
  3621. return smartProperty(v, viewNameOverride || viewName);
  3622. }
  3623. return v;
  3624. }
  3625. function trigger(name, thisObj) {
  3626. return calendar.trigger.apply(
  3627. calendar,
  3628. [name, thisObj || t].concat(Array.prototype.slice.call(arguments, 2), [t])
  3629. );
  3630. }
  3631. /* Event Editable Boolean Calculations
  3632. ------------------------------------------------------------------------------*/
  3633. function isEventDraggable(event) {
  3634. var source = event.source || {};
  3635. return firstDefined(
  3636. event.startEditable,
  3637. source.startEditable,
  3638. opt('eventStartEditable'),
  3639. event.editable,
  3640. source.editable,
  3641. opt('editable')
  3642. )
  3643. && !opt('disableDragging'); // deprecated
  3644. }
  3645. function isEventResizable(event) { // but also need to make sure the seg.isEnd == true
  3646. var source = event.source || {};
  3647. return firstDefined(
  3648. event.durationEditable,
  3649. source.durationEditable,
  3650. opt('eventDurationEditable'),
  3651. event.editable,
  3652. source.editable,
  3653. opt('editable')
  3654. )
  3655. && !opt('disableResizing'); // deprecated
  3656. }
  3657. /* Event Data
  3658. ------------------------------------------------------------------------------*/
  3659. function setEventData(events) { // events are already normalized at this point
  3660. eventsByID = {};
  3661. var i, len=events.length, event;
  3662. for (i=0; i<len; i++) {
  3663. event = events[i];
  3664. if (eventsByID[event._id]) {
  3665. eventsByID[event._id].push(event);
  3666. }else{
  3667. eventsByID[event._id] = [event];
  3668. }
  3669. }
  3670. }
  3671. function clearEventData() {
  3672. eventsByID = {};
  3673. eventElementsByID = {};
  3674. eventElementCouples = [];
  3675. }
  3676. // returns a Date object for an event's end
  3677. function eventEnd(event) {
  3678. return event.end ? cloneDate(event.end) : defaultEventEnd(event);
  3679. }
  3680. /* Event Elements
  3681. ------------------------------------------------------------------------------*/
  3682. // report when view creates an element for an event
  3683. function reportEventElement(event, element) {
  3684. eventElementCouples.push({ event: event, element: element });
  3685. if (eventElementsByID[event._id]) {
  3686. eventElementsByID[event._id].push(element);
  3687. }else{
  3688. eventElementsByID[event._id] = [element];
  3689. }
  3690. }
  3691. function triggerEventDestroy() {
  3692. $.each(eventElementCouples, function(i, couple) {
  3693. t.trigger('eventDestroy', couple.event, couple.event, couple.element);
  3694. });
  3695. }
  3696. // attaches eventClick, eventMouseover, eventMouseout
  3697. function eventElementHandlers(event, eventElement) {
  3698. eventElement
  3699. .click(function(ev) {
  3700. if (!eventElement.hasClass('ui-draggable-dragging') &&
  3701. !eventElement.hasClass('ui-resizable-resizing')) {
  3702. return trigger('eventClick', this, event, ev);
  3703. }
  3704. })
  3705. .hover(
  3706. function(ev) {
  3707. trigger('eventMouseover', this, event, ev);
  3708. },
  3709. function(ev) {
  3710. trigger('eventMouseout', this, event, ev);
  3711. }
  3712. );
  3713. // TODO: don't fire eventMouseover/eventMouseout *while* dragging is occuring (on subject element)
  3714. // TODO: same for resizing
  3715. }
  3716. function showEvents(event, exceptElement) {
  3717. eachEventElement(event, exceptElement, 'show');
  3718. }
  3719. function hideEvents(event, exceptElement) {
  3720. eachEventElement(event, exceptElement, 'hide');
  3721. }
  3722. function eachEventElement(event, exceptElement, funcName) {
  3723. // NOTE: there may be multiple events per ID (repeating events)
  3724. // and multiple segments per event
  3725. var elements = eventElementsByID[event._id],
  3726. i, len = elements.length;
  3727. for (i=0; i<len; i++) {
  3728. if (!exceptElement || elements[i][0] != exceptElement[0]) {
  3729. elements[i][funcName]();
  3730. }
  3731. }
  3732. }
  3733. /* Event Modification Reporting
  3734. ---------------------------------------------------------------------------------*/
  3735. function eventDrop(e, event, dayDelta, minuteDelta, allDay, ev, ui) {
  3736. var oldAllDay = event.allDay;
  3737. var eventId = event._id;
  3738. moveEvents(eventsByID[eventId], dayDelta, minuteDelta, allDay);
  3739. trigger(
  3740. 'eventDrop',
  3741. e,
  3742. event,
  3743. dayDelta,
  3744. minuteDelta,
  3745. allDay,
  3746. function() {
  3747. // TODO: investigate cases where this inverse technique might not work
  3748. moveEvents(eventsByID[eventId], -dayDelta, -minuteDelta, oldAllDay);
  3749. reportEventChange(eventId);
  3750. },
  3751. ev,
  3752. ui
  3753. );
  3754. reportEventChange(eventId);
  3755. }
  3756. function eventResize(e, event, dayDelta, minuteDelta, ev, ui) {
  3757. var eventId = event._id;
  3758. elongateEvents(eventsByID[eventId], dayDelta, minuteDelta);
  3759. trigger(
  3760. 'eventResize',
  3761. e,
  3762. event,
  3763. dayDelta,
  3764. minuteDelta,
  3765. function() {
  3766. // TODO: investigate cases where this inverse technique might not work
  3767. elongateEvents(eventsByID[eventId], -dayDelta, -minuteDelta);
  3768. reportEventChange(eventId);
  3769. },
  3770. ev,
  3771. ui
  3772. );
  3773. reportEventChange(eventId);
  3774. }
  3775. /* Event Modification Math
  3776. ---------------------------------------------------------------------------------*/
  3777. function moveEvents(events, dayDelta, minuteDelta, allDay) {
  3778. minuteDelta = minuteDelta || 0;
  3779. for (var e, len=events.length, i=0; i<len; i++) {
  3780. e = events[i];
  3781. if (allDay !== undefined) {
  3782. e.allDay = allDay;
  3783. }
  3784. addMinutes(addDays(e.start, dayDelta, true), minuteDelta);
  3785. if (e.end) {
  3786. e.end = addMinutes(addDays(e.end, dayDelta, true), minuteDelta);
  3787. }
  3788. normalizeEvent(e, options);
  3789. }
  3790. }
  3791. function elongateEvents(events, dayDelta, minuteDelta) {
  3792. minuteDelta = minuteDelta || 0;
  3793. for (var e, len=events.length, i=0; i<len; i++) {
  3794. e = events[i];
  3795. e.end = addMinutes(addDays(eventEnd(e), dayDelta, true), minuteDelta);
  3796. normalizeEvent(e, options);
  3797. }
  3798. }
  3799. // ====================================================================================================
  3800. // Utilities for day "cells"
  3801. // ====================================================================================================
  3802. // The "basic" views are completely made up of day cells.
  3803. // The "agenda" views have day cells at the top "all day" slot.
  3804. // This was the obvious common place to put these utilities, but they should be abstracted out into
  3805. // a more meaningful class (like DayEventRenderer).
  3806. // ====================================================================================================
  3807. // For determining how a given "cell" translates into a "date":
  3808. //
  3809. // 1. Convert the "cell" (row and column) into a "cell offset" (the # of the cell, cronologically from the first).
  3810. // Keep in mind that column indices are inverted with isRTL. This is taken into account.
  3811. //
  3812. // 2. Convert the "cell offset" to a "day offset" (the # of days since the first visible day in the view).
  3813. //
  3814. // 3. Convert the "day offset" into a "date" (a JavaScript Date object).
  3815. //
  3816. // The reverse transformation happens when transforming a date into a cell.
  3817. // exports
  3818. t.isHiddenDay = isHiddenDay;
  3819. t.skipHiddenDays = skipHiddenDays;
  3820. t.getCellsPerWeek = getCellsPerWeek;
  3821. t.dateToCell = dateToCell;
  3822. t.dateToDayOffset = dateToDayOffset;
  3823. t.dayOffsetToCellOffset = dayOffsetToCellOffset;
  3824. t.cellOffsetToCell = cellOffsetToCell;
  3825. t.cellToDate = cellToDate;
  3826. t.cellToCellOffset = cellToCellOffset;
  3827. t.cellOffsetToDayOffset = cellOffsetToDayOffset;
  3828. t.dayOffsetToDate = dayOffsetToDate;
  3829. t.rangeToSegments = rangeToSegments;
  3830. // internals
  3831. var hiddenDays = opt('hiddenDays') || []; // array of day-of-week indices that are hidden
  3832. var isHiddenDayHash = []; // is the day-of-week hidden? (hash with day-of-week-index -> bool)
  3833. var cellsPerWeek;
  3834. var dayToCellMap = []; // hash from dayIndex -> cellIndex, for one week
  3835. var cellToDayMap = []; // hash from cellIndex -> dayIndex, for one week
  3836. var isRTL = opt('isRTL');
  3837. // initialize important internal variables
  3838. (function() {
  3839. if (opt('weekends') === false) {
  3840. hiddenDays.push(0, 6); // 0=sunday, 6=saturday
  3841. }
  3842. // Loop through a hypothetical week and determine which
  3843. // days-of-week are hidden. Record in both hashes (one is the reverse of the other).
  3844. for (var dayIndex=0, cellIndex=0; dayIndex<7; dayIndex++) {
  3845. dayToCellMap[dayIndex] = cellIndex;
  3846. isHiddenDayHash[dayIndex] = $.inArray(dayIndex, hiddenDays) != -1;
  3847. if (!isHiddenDayHash[dayIndex]) {
  3848. cellToDayMap[cellIndex] = dayIndex;
  3849. cellIndex++;
  3850. }
  3851. }
  3852. cellsPerWeek = cellIndex;
  3853. if (!cellsPerWeek) {
  3854. throw 'invalid hiddenDays'; // all days were hidden? bad.
  3855. }
  3856. })();
  3857. // Is the current day hidden?
  3858. // `day` is a day-of-week index (0-6), or a Date object
  3859. function isHiddenDay(day) {
  3860. if (typeof day == 'object') {
  3861. day = day.getDay();
  3862. }
  3863. return isHiddenDayHash[day];
  3864. }
  3865. function getCellsPerWeek() {
  3866. return cellsPerWeek;
  3867. }
  3868. // Keep incrementing the current day until it is no longer a hidden day.
  3869. // If the initial value of `date` is not a hidden day, don't do anything.
  3870. // Pass `isExclusive` as `true` if you are dealing with an end date.
  3871. // `inc` defaults to `1` (increment one day forward each time)
  3872. function skipHiddenDays(date, inc, isExclusive) {
  3873. inc = inc || 1;
  3874. while (
  3875. isHiddenDayHash[ ( date.getDay() + (isExclusive ? inc : 0) + 7 ) % 7 ]
  3876. ) {
  3877. addDays(date, inc);
  3878. }
  3879. }
  3880. //
  3881. // TRANSFORMATIONS: cell -> cell offset -> day offset -> date
  3882. //
  3883. // cell -> date (combines all transformations)
  3884. // Possible arguments:
  3885. // - row, col
  3886. // - { row:#, col: # }
  3887. function cellToDate() {
  3888. var cellOffset = cellToCellOffset.apply(null, arguments);
  3889. var dayOffset = cellOffsetToDayOffset(cellOffset);
  3890. var date = dayOffsetToDate(dayOffset);
  3891. return date;
  3892. }
  3893. // cell -> cell offset
  3894. // Possible arguments:
  3895. // - row, col
  3896. // - { row:#, col:# }
  3897. function cellToCellOffset(row, col) {
  3898. var colCnt = t.getColCnt();
  3899. // rtl variables. wish we could pre-populate these. but where?
  3900. var dis = isRTL ? -1 : 1;
  3901. var dit = isRTL ? colCnt - 1 : 0;
  3902. if (typeof row == 'object') {
  3903. col = row.col;
  3904. row = row.row;
  3905. }
  3906. var cellOffset = row * colCnt + (col * dis + dit); // column, adjusted for RTL (dis & dit)
  3907. return cellOffset;
  3908. }
  3909. // cell offset -> day offset
  3910. function cellOffsetToDayOffset(cellOffset) {
  3911. var day0 = t.visStart.getDay(); // first date's day of week
  3912. cellOffset += dayToCellMap[day0]; // normlize cellOffset to beginning-of-week
  3913. return Math.floor(cellOffset / cellsPerWeek) * 7 // # of days from full weeks
  3914. + cellToDayMap[ // # of days from partial last week
  3915. (cellOffset % cellsPerWeek + cellsPerWeek) % cellsPerWeek // crazy math to handle negative cellOffsets
  3916. ]
  3917. - day0; // adjustment for beginning-of-week normalization
  3918. }
  3919. // day offset -> date (JavaScript Date object)
  3920. function dayOffsetToDate(dayOffset) {
  3921. var date = cloneDate(t.visStart);
  3922. addDays(date, dayOffset);
  3923. return date;
  3924. }
  3925. //
  3926. // TRANSFORMATIONS: date -> day offset -> cell offset -> cell
  3927. //
  3928. // date -> cell (combines all transformations)
  3929. function dateToCell(date) {
  3930. var dayOffset = dateToDayOffset(date);
  3931. var cellOffset = dayOffsetToCellOffset(dayOffset);
  3932. var cell = cellOffsetToCell(cellOffset);
  3933. return cell;
  3934. }
  3935. // date -> day offset
  3936. function dateToDayOffset(date) {
  3937. return dayDiff(date, t.visStart);
  3938. }
  3939. // day offset -> cell offset
  3940. function dayOffsetToCellOffset(dayOffset) {
  3941. var day0 = t.visStart.getDay(); // first date's day of week
  3942. dayOffset += day0; // normalize dayOffset to beginning-of-week
  3943. return Math.floor(dayOffset / 7) * cellsPerWeek // # of cells from full weeks
  3944. + dayToCellMap[ // # of cells from partial last week
  3945. (dayOffset % 7 + 7) % 7 // crazy math to handle negative dayOffsets
  3946. ]
  3947. - dayToCellMap[day0]; // adjustment for beginning-of-week normalization
  3948. }
  3949. // cell offset -> cell (object with row & col keys)
  3950. function cellOffsetToCell(cellOffset) {
  3951. var colCnt = t.getColCnt();
  3952. // rtl variables. wish we could pre-populate these. but where?
  3953. var dis = isRTL ? -1 : 1;
  3954. var dit = isRTL ? colCnt - 1 : 0;
  3955. var row = Math.floor(cellOffset / colCnt);
  3956. var col = ((cellOffset % colCnt + colCnt) % colCnt) * dis + dit; // column, adjusted for RTL (dis & dit)
  3957. return {
  3958. row: row,
  3959. col: col
  3960. };
  3961. }
  3962. //
  3963. // Converts a date range into an array of segment objects.
  3964. // "Segments" are horizontal stretches of time, sliced up by row.
  3965. // A segment object has the following properties:
  3966. // - row
  3967. // - cols
  3968. // - isStart
  3969. // - isEnd
  3970. //
  3971. function rangeToSegments(startDate, endDate) {
  3972. var rowCnt = t.getRowCnt();
  3973. var colCnt = t.getColCnt();
  3974. var segments = []; // array of segments to return
  3975. // day offset for given date range
  3976. var rangeDayOffsetStart = dateToDayOffset(startDate);
  3977. var rangeDayOffsetEnd = dateToDayOffset(endDate); // exclusive
  3978. // first and last cell offset for the given date range
  3979. // "last" implies inclusivity
  3980. var rangeCellOffsetFirst = dayOffsetToCellOffset(rangeDayOffsetStart);
  3981. var rangeCellOffsetLast = dayOffsetToCellOffset(rangeDayOffsetEnd) - 1;
  3982. // loop through all the rows in the view
  3983. for (var row=0; row<rowCnt; row++) {
  3984. // first and last cell offset for the row
  3985. var rowCellOffsetFirst = row * colCnt;
  3986. var rowCellOffsetLast = rowCellOffsetFirst + colCnt - 1;
  3987. // get the segment's cell offsets by constraining the range's cell offsets to the bounds of the row
  3988. var segmentCellOffsetFirst = Math.max(rangeCellOffsetFirst, rowCellOffsetFirst);
  3989. var segmentCellOffsetLast = Math.min(rangeCellOffsetLast, rowCellOffsetLast);
  3990. // make sure segment's offsets are valid and in view
  3991. if (segmentCellOffsetFirst <= segmentCellOffsetLast) {
  3992. // translate to cells
  3993. var segmentCellFirst = cellOffsetToCell(segmentCellOffsetFirst);
  3994. var segmentCellLast = cellOffsetToCell(segmentCellOffsetLast);
  3995. // view might be RTL, so order by leftmost column
  3996. var cols = [ segmentCellFirst.col, segmentCellLast.col ].sort();
  3997. // Determine if segment's first/last cell is the beginning/end of the date range.
  3998. // We need to compare "day offset" because "cell offsets" are often ambiguous and
  3999. // can translate to multiple days, and an edge case reveals itself when we the
  4000. // range's first cell is hidden (we don't want isStart to be true).
  4001. var isStart = cellOffsetToDayOffset(segmentCellOffsetFirst) == rangeDayOffsetStart;
  4002. var isEnd = cellOffsetToDayOffset(segmentCellOffsetLast) + 1 == rangeDayOffsetEnd; // +1 for comparing exclusively
  4003. segments.push({
  4004. row: row,
  4005. leftCol: cols[0],
  4006. rightCol: cols[1],
  4007. isStart: isStart,
  4008. isEnd: isEnd
  4009. });
  4010. }
  4011. }
  4012. return segments;
  4013. }
  4014. }
  4015. ;;
  4016. function DayEventRenderer() {
  4017. var t = this;
  4018. // exports
  4019. t.renderDayEvents = renderDayEvents;
  4020. t.draggableDayEvent = draggableDayEvent; // made public so that subclasses can override
  4021. t.resizableDayEvent = resizableDayEvent; // "
  4022. // imports
  4023. var opt = t.opt;
  4024. var trigger = t.trigger;
  4025. var isEventDraggable = t.isEventDraggable;
  4026. var isEventResizable = t.isEventResizable;
  4027. var eventEnd = t.eventEnd;
  4028. var reportEventElement = t.reportEventElement;
  4029. var eventElementHandlers = t.eventElementHandlers;
  4030. var showEvents = t.showEvents;
  4031. var hideEvents = t.hideEvents;
  4032. var eventDrop = t.eventDrop;
  4033. var eventResize = t.eventResize;
  4034. var getRowCnt = t.getRowCnt;
  4035. var getColCnt = t.getColCnt;
  4036. var getColWidth = t.getColWidth;
  4037. var allDayRow = t.allDayRow; // TODO: rename
  4038. var colLeft = t.colLeft;
  4039. var colRight = t.colRight;
  4040. var colContentLeft = t.colContentLeft;
  4041. var colContentRight = t.colContentRight;
  4042. var dateToCell = t.dateToCell;
  4043. var getDaySegmentContainer = t.getDaySegmentContainer;
  4044. var formatDates = t.calendar.formatDates;
  4045. var renderDayOverlay = t.renderDayOverlay;
  4046. var clearOverlays = t.clearOverlays;
  4047. var clearSelection = t.clearSelection;
  4048. var getHoverListener = t.getHoverListener;
  4049. var rangeToSegments = t.rangeToSegments;
  4050. var cellToDate = t.cellToDate;
  4051. var cellToCellOffset = t.cellToCellOffset;
  4052. var cellOffsetToDayOffset = t.cellOffsetToDayOffset;
  4053. var dateToDayOffset = t.dateToDayOffset;
  4054. var dayOffsetToCellOffset = t.dayOffsetToCellOffset;
  4055. // Render `events` onto the calendar, attach mouse event handlers, and call the `eventAfterRender` callback for each.
  4056. // Mouse event will be lazily applied, except if the event has an ID of `modifiedEventId`.
  4057. // Can only be called when the event container is empty (because it wipes out all innerHTML).
  4058. function renderDayEvents(events, modifiedEventId) {
  4059. // do the actual rendering. Receive the intermediate "segment" data structures.
  4060. var segments = _renderDayEvents(
  4061. events,
  4062. false, // don't append event elements
  4063. true // set the heights of the rows
  4064. );
  4065. // report the elements to the View, for general drag/resize utilities
  4066. segmentElementEach(segments, function(segment, element) {
  4067. reportEventElement(segment.event, element);
  4068. });
  4069. // attach mouse handlers
  4070. attachHandlers(segments, modifiedEventId);
  4071. // call `eventAfterRender` callback for each event
  4072. segmentElementEach(segments, function(segment, element) {
  4073. trigger('eventAfterRender', segment.event, segment.event, element);
  4074. });
  4075. }
  4076. // Render an event on the calendar, but don't report them anywhere, and don't attach mouse handlers.
  4077. // Append this event element to the event container, which might already be populated with events.
  4078. // If an event's segment will have row equal to `adjustRow`, then explicitly set its top coordinate to `adjustTop`.
  4079. // This hack is used to maintain continuity when user is manually resizing an event.
  4080. // Returns an array of DOM elements for the event.
  4081. function renderTempDayEvent(event, adjustRow, adjustTop) {
  4082. // actually render the event. `true` for appending element to container.
  4083. // Recieve the intermediate "segment" data structures.
  4084. var segments = _renderDayEvents(
  4085. [ event ],
  4086. true, // append event elements
  4087. false // don't set the heights of the rows
  4088. );
  4089. var elements = [];
  4090. // Adjust certain elements' top coordinates
  4091. segmentElementEach(segments, function(segment, element) {
  4092. if (segment.row === adjustRow) {
  4093. element.css('top', adjustTop);
  4094. }
  4095. elements.push(element[0]); // accumulate DOM nodes
  4096. });
  4097. return elements;
  4098. }
  4099. // Render events onto the calendar. Only responsible for the VISUAL aspect.
  4100. // Not responsible for attaching handlers or calling callbacks.
  4101. // Set `doAppend` to `true` for rendering elements without clearing the existing container.
  4102. // Set `doRowHeights` to allow setting the height of each row, to compensate for vertical event overflow.
  4103. function _renderDayEvents(events, doAppend, doRowHeights) {
  4104. // where the DOM nodes will eventually end up
  4105. var finalContainer = getDaySegmentContainer();
  4106. // the container where the initial HTML will be rendered.
  4107. // If `doAppend`==true, uses a temporary container.
  4108. var renderContainer = doAppend ? $("<div/>") : finalContainer;
  4109. var segments = buildSegments(events);
  4110. var html;
  4111. var elements;
  4112. // calculate the desired `left` and `width` properties on each segment object
  4113. calculateHorizontals(segments);
  4114. // build the HTML string. relies on `left` property
  4115. html = buildHTML(segments);
  4116. // render the HTML. innerHTML is considerably faster than jQuery's .html()
  4117. renderContainer[0].innerHTML = html;
  4118. // retrieve the individual elements
  4119. elements = renderContainer.children();
  4120. // if we were appending, and thus using a temporary container,
  4121. // re-attach elements to the real container.
  4122. if (doAppend) {
  4123. finalContainer.append(elements);
  4124. }
  4125. // assigns each element to `segment.event`, after filtering them through user callbacks
  4126. resolveElements(segments, elements);
  4127. // Calculate the left and right padding+margin for each element.
  4128. // We need this for setting each element's desired outer width, because of the W3C box model.
  4129. // It's important we do this in a separate pass from acually setting the width on the DOM elements
  4130. // because alternating reading/writing dimensions causes reflow for every iteration.
  4131. segmentElementEach(segments, function(segment, element) {
  4132. segment.hsides = hsides(element, true); // include margins = `true`
  4133. });
  4134. // Set the width of each element
  4135. segmentElementEach(segments, function(segment, element) {
  4136. element.width(
  4137. Math.max(0, segment.outerWidth - segment.hsides)
  4138. );
  4139. });
  4140. // Grab each element's outerHeight (setVerticals uses this).
  4141. // To get an accurate reading, it's important to have each element's width explicitly set already.
  4142. segmentElementEach(segments, function(segment, element) {
  4143. segment.outerHeight = element.outerHeight(true); // include margins = `true`
  4144. });
  4145. // Set the top coordinate on each element (requires segment.outerHeight)
  4146. setVerticals(segments, doRowHeights);
  4147. return segments;
  4148. }
  4149. // Generate an array of "segments" for all events.
  4150. function buildSegments(events) {
  4151. var segments = [];
  4152. for (var i=0; i<events.length; i++) {
  4153. var eventSegments = buildSegmentsForEvent(events[i]);
  4154. segments.push.apply(segments, eventSegments); // append an array to an array
  4155. }
  4156. return segments;
  4157. }
  4158. // Generate an array of segments for a single event.
  4159. // A "segment" is the same data structure that View.rangeToSegments produces,
  4160. // with the addition of the `event` property being set to reference the original event.
  4161. function buildSegmentsForEvent(event) {
  4162. var startDate = event.start;
  4163. var endDate = exclEndDay(event);
  4164. var segments = rangeToSegments(startDate, endDate);
  4165. for (var i=0; i<segments.length; i++) {
  4166. segments[i].event = event;
  4167. }
  4168. return segments;
  4169. }
  4170. // Sets the `left` and `outerWidth` property of each segment.
  4171. // These values are the desired dimensions for the eventual DOM elements.
  4172. function calculateHorizontals(segments) {
  4173. var isRTL = opt('isRTL');
  4174. for (var i=0; i<segments.length; i++) {
  4175. var segment = segments[i];
  4176. // Determine functions used for calulating the elements left/right coordinates,
  4177. // depending on whether the view is RTL or not.
  4178. // NOTE:
  4179. // colLeft/colRight returns the coordinate butting up the edge of the cell.
  4180. // colContentLeft/colContentRight is indented a little bit from the edge.
  4181. var leftFunc = (isRTL ? segment.isEnd : segment.isStart) ? colContentLeft : colLeft;
  4182. var rightFunc = (isRTL ? segment.isStart : segment.isEnd) ? colContentRight : colRight;
  4183. var left = leftFunc(segment.leftCol);
  4184. var right = rightFunc(segment.rightCol);
  4185. segment.left = left;
  4186. segment.outerWidth = right - left;
  4187. }
  4188. }
  4189. // Build a concatenated HTML string for an array of segments
  4190. function buildHTML(segments) {
  4191. var html = '';
  4192. for (var i=0; i<segments.length; i++) {
  4193. html += buildHTMLForSegment(segments[i]);
  4194. }
  4195. return html;
  4196. }
  4197. // Build an HTML string for a single segment.
  4198. // Relies on the following properties:
  4199. // - `segment.event` (from `buildSegmentsForEvent`)
  4200. // - `segment.left` (from `calculateHorizontals`)
  4201. function buildHTMLForSegment(segment) {
  4202. var html = '';
  4203. var isRTL = opt('isRTL');
  4204. var event = segment.event;
  4205. var url = event.url;
  4206. // generate the list of CSS classNames
  4207. var classNames = [ 'fc-event', 'fc-event-hori' ];
  4208. if (isEventDraggable(event)) {
  4209. classNames.push('fc-event-draggable');
  4210. }
  4211. if (segment.isStart) {
  4212. classNames.push('fc-event-start');
  4213. }
  4214. if (segment.isEnd) {
  4215. classNames.push('fc-event-end');
  4216. }
  4217. // use the event's configured classNames
  4218. // guaranteed to be an array via `normalizeEvent`
  4219. classNames = classNames.concat(event.className);
  4220. if (event.source) {
  4221. // use the event's source's classNames, if specified
  4222. classNames = classNames.concat(event.source.className || []);
  4223. }
  4224. // generate a semicolon delimited CSS string for any of the "skin" properties
  4225. // of the event object (`backgroundColor`, `borderColor` and such)
  4226. var skinCss = getSkinCss(event, opt);
  4227. if (url) {
  4228. html += "<a href='" + htmlEscape(url) + "'";
  4229. }else{
  4230. html += "<div";
  4231. }
  4232. html +=
  4233. " class='" + classNames.join(' ') + "'" +
  4234. " style=" +
  4235. "'" +
  4236. "position:absolute;" +
  4237. "left:" + segment.left + "px;" +
  4238. skinCss +
  4239. "'" +
  4240. ">" +
  4241. "<div class='fc-event-inner'>";
  4242. if (!event.allDay && segment.isStart) {
  4243. html +=
  4244. "<span class='fc-event-time'>" +
  4245. htmlEscape(
  4246. formatDates(event.start, event.end, opt('timeFormat'))
  4247. ) +
  4248. "</span>";
  4249. }
  4250. html +=
  4251. "<span class='fc-event-title'>" +
  4252. htmlEscape(event.title || '') +
  4253. "</span>" +
  4254. "</div>";
  4255. if (segment.isEnd && isEventResizable(event)) {
  4256. html +=
  4257. "<div class='ui-resizable-handle ui-resizable-" + (isRTL ? 'w' : 'e') + "'>" +
  4258. "&nbsp;&nbsp;&nbsp;" + // makes hit area a lot better for IE6/7
  4259. "</div>";
  4260. }
  4261. html += "</" + (url ? "a" : "div") + ">";
  4262. // TODO:
  4263. // When these elements are initially rendered, they will be briefly visibile on the screen,
  4264. // even though their widths/heights are not set.
  4265. // SOLUTION: initially set them as visibility:hidden ?
  4266. return html;
  4267. }
  4268. // Associate each segment (an object) with an element (a jQuery object),
  4269. // by setting each `segment.element`.
  4270. // Run each element through the `eventRender` filter, which allows developers to
  4271. // modify an existing element, supply a new one, or cancel rendering.
  4272. function resolveElements(segments, elements) {
  4273. for (var i=0; i<segments.length; i++) {
  4274. var segment = segments[i];
  4275. var event = segment.event;
  4276. var element = elements.eq(i);
  4277. // call the trigger with the original element
  4278. var triggerRes = trigger('eventRender', event, event, element);
  4279. if (triggerRes === false) {
  4280. // if `false`, remove the event from the DOM and don't assign it to `segment.event`
  4281. element.remove();
  4282. }
  4283. else {
  4284. if (triggerRes && triggerRes !== true) {
  4285. // the trigger returned a new element, but not `true` (which means keep the existing element)
  4286. // re-assign the important CSS dimension properties that were already assigned in `buildHTMLForSegment`
  4287. triggerRes = $(triggerRes)
  4288. .css({
  4289. position: 'absolute',
  4290. left: segment.left
  4291. });
  4292. element.replaceWith(triggerRes);
  4293. element = triggerRes;
  4294. }
  4295. segment.element = element;
  4296. }
  4297. }
  4298. }
  4299. /* Top-coordinate Methods
  4300. -------------------------------------------------------------------------------------------------*/
  4301. // Sets the "top" CSS property for each element.
  4302. // If `doRowHeights` is `true`, also sets each row's first cell to an explicit height,
  4303. // so that if elements vertically overflow, the cell expands vertically to compensate.
  4304. function setVerticals(segments, doRowHeights) {
  4305. var rowContentHeights = calculateVerticals(segments); // also sets segment.top
  4306. var rowContentElements = getRowContentElements(); // returns 1 inner div per row
  4307. var rowContentTops = [];
  4308. // Set each row's height by setting height of first inner div
  4309. if (doRowHeights) {
  4310. for (var i=0; i<rowContentElements.length; i++) {
  4311. rowContentElements[i].height(rowContentHeights[i]);
  4312. }
  4313. }
  4314. // Get each row's top, relative to the views's origin.
  4315. // Important to do this after setting each row's height.
  4316. for (var i=0; i<rowContentElements.length; i++) {
  4317. rowContentTops.push(
  4318. rowContentElements[i].position().top
  4319. );
  4320. }
  4321. // Set each segment element's CSS "top" property.
  4322. // Each segment object has a "top" property, which is relative to the row's top, but...
  4323. segmentElementEach(segments, function(segment, element) {
  4324. element.css(
  4325. 'top',
  4326. rowContentTops[segment.row] + segment.top // ...now, relative to views's origin
  4327. );
  4328. });
  4329. }
  4330. // Calculate the "top" coordinate for each segment, relative to the "top" of the row.
  4331. // Also, return an array that contains the "content" height for each row
  4332. // (the height displaced by the vertically stacked events in the row).
  4333. // Requires segments to have their `outerHeight` property already set.
  4334. function calculateVerticals(segments) {
  4335. var rowCnt = getRowCnt();
  4336. var colCnt = getColCnt();
  4337. var rowContentHeights = []; // content height for each row
  4338. var segmentRows = buildSegmentRows(segments); // an array of segment arrays, one for each row
  4339. for (var rowI=0; rowI<rowCnt; rowI++) {
  4340. var segmentRow = segmentRows[rowI];
  4341. // an array of running total heights for each column.
  4342. // initialize with all zeros.
  4343. var colHeights = [];
  4344. for (var colI=0; colI<colCnt; colI++) {
  4345. colHeights.push(0);
  4346. }
  4347. // loop through every segment
  4348. for (var segmentI=0; segmentI<segmentRow.length; segmentI++) {
  4349. var segment = segmentRow[segmentI];
  4350. // find the segment's top coordinate by looking at the max height
  4351. // of all the columns the segment will be in.
  4352. segment.top = arrayMax(
  4353. colHeights.slice(
  4354. segment.leftCol,
  4355. segment.rightCol + 1 // make exclusive for slice
  4356. )
  4357. );
  4358. // adjust the columns to account for the segment's height
  4359. for (var colI=segment.leftCol; colI<=segment.rightCol; colI++) {
  4360. colHeights[colI] = segment.top + segment.outerHeight;
  4361. }
  4362. }
  4363. // the tallest column in the row should be the "content height"
  4364. rowContentHeights.push(arrayMax(colHeights));
  4365. }
  4366. return rowContentHeights;
  4367. }
  4368. // Build an array of segment arrays, each representing the segments that will
  4369. // be in a row of the grid, sorted by which event should be closest to the top.
  4370. function buildSegmentRows(segments) {
  4371. var rowCnt = getRowCnt();
  4372. var segmentRows = [];
  4373. var segmentI;
  4374. var segment;
  4375. var rowI;
  4376. // group segments by row
  4377. for (segmentI=0; segmentI<segments.length; segmentI++) {
  4378. segment = segments[segmentI];
  4379. rowI = segment.row;
  4380. if (segment.element) { // was rendered?
  4381. if (segmentRows[rowI]) {
  4382. // already other segments. append to array
  4383. segmentRows[rowI].push(segment);
  4384. }
  4385. else {
  4386. // first segment in row. create new array
  4387. segmentRows[rowI] = [ segment ];
  4388. }
  4389. }
  4390. }
  4391. // sort each row
  4392. for (rowI=0; rowI<rowCnt; rowI++) {
  4393. segmentRows[rowI] = sortSegmentRow(
  4394. segmentRows[rowI] || [] // guarantee an array, even if no segments
  4395. );
  4396. }
  4397. return segmentRows;
  4398. }
  4399. // Sort an array of segments according to which segment should appear closest to the top
  4400. function sortSegmentRow(segments) {
  4401. var sortedSegments = [];
  4402. // build the subrow array
  4403. var subrows = buildSegmentSubrows(segments);
  4404. // flatten it
  4405. for (var i=0; i<subrows.length; i++) {
  4406. sortedSegments.push.apply(sortedSegments, subrows[i]); // append an array to an array
  4407. }
  4408. return sortedSegments;
  4409. }
  4410. // Take an array of segments, which are all assumed to be in the same row,
  4411. // and sort into subrows.
  4412. function buildSegmentSubrows(segments) {
  4413. // Give preference to elements with certain criteria, so they have
  4414. // a chance to be closer to the top.
  4415. segments.sort(compareDaySegments);
  4416. var subrows = [];
  4417. for (var i=0; i<segments.length; i++) {
  4418. var segment = segments[i];
  4419. // loop through subrows, starting with the topmost, until the segment
  4420. // doesn't collide with other segments.
  4421. for (var j=0; j<subrows.length; j++) {
  4422. if (!isDaySegmentCollision(segment, subrows[j])) {
  4423. break;
  4424. }
  4425. }
  4426. // `j` now holds the desired subrow index
  4427. if (subrows[j]) {
  4428. subrows[j].push(segment);
  4429. }
  4430. else {
  4431. subrows[j] = [ segment ];
  4432. }
  4433. }
  4434. return subrows;
  4435. }
  4436. // Return an array of jQuery objects for the placeholder content containers of each row.
  4437. // The content containers don't actually contain anything, but their dimensions should match
  4438. // the events that are overlaid on top.
  4439. function getRowContentElements() {
  4440. var i;
  4441. var rowCnt = getRowCnt();
  4442. var rowDivs = [];
  4443. for (i=0; i<rowCnt; i++) {
  4444. rowDivs[i] = allDayRow(i)
  4445. .find('div.fc-day-content > div');
  4446. }
  4447. return rowDivs;
  4448. }
  4449. /* Mouse Handlers
  4450. ---------------------------------------------------------------------------------------------------*/
  4451. // TODO: better documentation!
  4452. function attachHandlers(segments, modifiedEventId) {
  4453. var segmentContainer = getDaySegmentContainer();
  4454. segmentElementEach(segments, function(segment, element, i) {
  4455. var event = segment.event;
  4456. if (event._id === modifiedEventId) {
  4457. bindDaySeg(event, element, segment);
  4458. }else{
  4459. element[0]._fci = i; // for lazySegBind
  4460. }
  4461. });
  4462. lazySegBind(segmentContainer, segments, bindDaySeg);
  4463. }
  4464. function bindDaySeg(event, eventElement, segment) {
  4465. if (isEventDraggable(event)) {
  4466. t.draggableDayEvent(event, eventElement, segment); // use `t` so subclasses can override
  4467. }
  4468. if (
  4469. segment.isEnd && // only allow resizing on the final segment for an event
  4470. isEventResizable(event)
  4471. ) {
  4472. t.resizableDayEvent(event, eventElement, segment); // use `t` so subclasses can override
  4473. }
  4474. // attach all other handlers.
  4475. // needs to be after, because resizableDayEvent might stopImmediatePropagation on click
  4476. eventElementHandlers(event, eventElement);
  4477. }
  4478. function draggableDayEvent(event, eventElement) {
  4479. var hoverListener = getHoverListener();
  4480. var dayDelta;
  4481. eventElement.draggable({
  4482. delay: 50,
  4483. opacity: opt('dragOpacity'),
  4484. revertDuration: opt('dragRevertDuration'),
  4485. start: function(ev, ui) {
  4486. trigger('eventDragStart', eventElement, event, ev, ui);
  4487. hideEvents(event, eventElement);
  4488. hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
  4489. eventElement.draggable('option', 'revert', !cell || !rowDelta && !colDelta);
  4490. clearOverlays();
  4491. if (cell) {
  4492. var origDate = cellToDate(origCell);
  4493. var date = cellToDate(cell);
  4494. dayDelta = dayDiff(date, origDate);
  4495. renderDayOverlay(
  4496. addDays(cloneDate(event.start), dayDelta),
  4497. addDays(exclEndDay(event), dayDelta)
  4498. );
  4499. }else{
  4500. dayDelta = 0;
  4501. }
  4502. }, ev, 'drag');
  4503. },
  4504. stop: function(ev, ui) {
  4505. hoverListener.stop();
  4506. clearOverlays();
  4507. trigger('eventDragStop', eventElement, event, ev, ui);
  4508. if (dayDelta) {
  4509. eventDrop(this, event, dayDelta, 0, event.allDay, ev, ui);
  4510. }else{
  4511. eventElement.css('filter', ''); // clear IE opacity side-effects
  4512. showEvents(event, eventElement);
  4513. }
  4514. }
  4515. });
  4516. }
  4517. function resizableDayEvent(event, element, segment) {
  4518. var isRTL = opt('isRTL');
  4519. var direction = isRTL ? 'w' : 'e';
  4520. var handle = element.find('.ui-resizable-' + direction); // TODO: stop using this class because we aren't using jqui for this
  4521. var isResizing = false;
  4522. // TODO: look into using jquery-ui mouse widget for this stuff
  4523. disableTextSelection(element); // prevent native <a> selection for IE
  4524. element
  4525. .mousedown(function(ev) { // prevent native <a> selection for others
  4526. ev.preventDefault();
  4527. })
  4528. .click(function(ev) {
  4529. if (isResizing) {
  4530. ev.preventDefault(); // prevent link from being visited (only method that worked in IE6)
  4531. ev.stopImmediatePropagation(); // prevent fullcalendar eventClick handler from being called
  4532. // (eventElementHandlers needs to be bound after resizableDayEvent)
  4533. }
  4534. });
  4535. handle.mousedown(function(ev) {
  4536. if (ev.which != 1) {
  4537. return; // needs to be left mouse button
  4538. }
  4539. isResizing = true;
  4540. var hoverListener = getHoverListener();
  4541. var rowCnt = getRowCnt();
  4542. var colCnt = getColCnt();
  4543. var elementTop = element.css('top');
  4544. var dayDelta;
  4545. var helpers;
  4546. var eventCopy = $.extend({}, event);
  4547. var minCellOffset = dayOffsetToCellOffset( dateToDayOffset(event.start) );
  4548. clearSelection();
  4549. $('body')
  4550. .css('cursor', direction + '-resize')
  4551. .one('mouseup', mouseup);
  4552. trigger('eventResizeStart', this, event, ev);
  4553. hoverListener.start(function(cell, origCell) {
  4554. if (cell) {
  4555. var origCellOffset = cellToCellOffset(origCell);
  4556. var cellOffset = cellToCellOffset(cell);
  4557. // don't let resizing move earlier than start date cell
  4558. cellOffset = Math.max(cellOffset, minCellOffset);
  4559. dayDelta =
  4560. cellOffsetToDayOffset(cellOffset) -
  4561. cellOffsetToDayOffset(origCellOffset);
  4562. if (dayDelta) {
  4563. eventCopy.end = addDays(eventEnd(event), dayDelta, true);
  4564. var oldHelpers = helpers;
  4565. helpers = renderTempDayEvent(eventCopy, segment.row, elementTop);
  4566. helpers = $(helpers); // turn array into a jQuery object
  4567. helpers.find('*').css('cursor', direction + '-resize');
  4568. if (oldHelpers) {
  4569. oldHelpers.remove();
  4570. }
  4571. hideEvents(event);
  4572. }
  4573. else {
  4574. if (helpers) {
  4575. showEvents(event);
  4576. helpers.remove();
  4577. helpers = null;
  4578. }
  4579. }
  4580. clearOverlays();
  4581. renderDayOverlay( // coordinate grid already rebuilt with hoverListener.start()
  4582. event.start,
  4583. addDays( exclEndDay(event), dayDelta )
  4584. // TODO: instead of calling renderDayOverlay() with dates,
  4585. // call _renderDayOverlay (or whatever) with cell offsets.
  4586. );
  4587. }
  4588. }, ev);
  4589. function mouseup(ev) {
  4590. trigger('eventResizeStop', this, event, ev);
  4591. $('body').css('cursor', '');
  4592. hoverListener.stop();
  4593. clearOverlays();
  4594. if (dayDelta) {
  4595. eventResize(this, event, dayDelta, 0, ev);
  4596. // event redraw will clear helpers
  4597. }
  4598. // otherwise, the drag handler already restored the old events
  4599. setTimeout(function() { // make this happen after the element's click event
  4600. isResizing = false;
  4601. },0);
  4602. }
  4603. });
  4604. }
  4605. }
  4606. /* Generalized Segment Utilities
  4607. -------------------------------------------------------------------------------------------------*/
  4608. function isDaySegmentCollision(segment, otherSegments) {
  4609. for (var i=0; i<otherSegments.length; i++) {
  4610. var otherSegment = otherSegments[i];
  4611. if (
  4612. otherSegment.leftCol <= segment.rightCol &&
  4613. otherSegment.rightCol >= segment.leftCol
  4614. ) {
  4615. return true;
  4616. }
  4617. }
  4618. return false;
  4619. }
  4620. function segmentElementEach(segments, callback) { // TODO: use in AgendaView?
  4621. for (var i=0; i<segments.length; i++) {
  4622. var segment = segments[i];
  4623. var element = segment.element;
  4624. if (element) {
  4625. callback(segment, element, i);
  4626. }
  4627. }
  4628. }
  4629. // A cmp function for determining which segments should appear higher up
  4630. function compareDaySegments(a, b) {
  4631. return (b.rightCol - b.leftCol) - (a.rightCol - a.leftCol) || // put wider events first
  4632. b.event.allDay - a.event.allDay || // if tie, put all-day events first (booleans cast to 0/1)
  4633. a.event.start - b.event.start || // if a tie, sort by event start date
  4634. (a.event.title || '').localeCompare(b.event.title) // if a tie, sort by event title
  4635. }
  4636. ;;
  4637. //BUG: unselect needs to be triggered when events are dragged+dropped
  4638. function SelectionManager() {
  4639. var t = this;
  4640. // exports
  4641. t.select = select;
  4642. t.unselect = unselect;
  4643. t.reportSelection = reportSelection;
  4644. t.daySelectionMousedown = daySelectionMousedown;
  4645. // imports
  4646. var opt = t.opt;
  4647. var trigger = t.trigger;
  4648. var defaultSelectionEnd = t.defaultSelectionEnd;
  4649. var renderSelection = t.renderSelection;
  4650. var clearSelection = t.clearSelection;
  4651. // locals
  4652. var selected = false;
  4653. // unselectAuto
  4654. if (opt('selectable') && opt('unselectAuto')) {
  4655. $(document).mousedown(function(ev) {
  4656. var ignore = opt('unselectCancel');
  4657. if (ignore) {
  4658. if ($(ev.target).parents(ignore).length) { // could be optimized to stop after first match
  4659. return;
  4660. }
  4661. }
  4662. unselect(ev);
  4663. });
  4664. }
  4665. function select(startDate, endDate, allDay) {
  4666. unselect();
  4667. if (!endDate) {
  4668. endDate = defaultSelectionEnd(startDate, allDay);
  4669. }
  4670. renderSelection(startDate, endDate, allDay);
  4671. reportSelection(startDate, endDate, allDay);
  4672. }
  4673. function unselect(ev) {
  4674. if (selected) {
  4675. selected = false;
  4676. clearSelection();
  4677. trigger('unselect', null, ev);
  4678. }
  4679. }
  4680. function reportSelection(startDate, endDate, allDay, ev) {
  4681. selected = true;
  4682. trigger('select', null, startDate, endDate, allDay, ev);
  4683. }
  4684. function daySelectionMousedown(ev) { // not really a generic manager method, oh well
  4685. var cellToDate = t.cellToDate;
  4686. var getIsCellAllDay = t.getIsCellAllDay;
  4687. var hoverListener = t.getHoverListener();
  4688. var reportDayClick = t.reportDayClick; // this is hacky and sort of weird
  4689. if (ev.which == 1 && opt('selectable')) { // which==1 means left mouse button
  4690. unselect(ev);
  4691. var _mousedownElement = this;
  4692. var dates;
  4693. hoverListener.start(function(cell, origCell) { // TODO: maybe put cellToDate/getIsCellAllDay info in cell
  4694. clearSelection();
  4695. if (cell && getIsCellAllDay(cell)) {
  4696. dates = [ cellToDate(origCell), cellToDate(cell) ].sort(dateCompare);
  4697. renderSelection(dates[0], dates[1], true);
  4698. }else{
  4699. dates = null;
  4700. }
  4701. }, ev);
  4702. $(document).one('mouseup', function(ev) {
  4703. hoverListener.stop();
  4704. if (dates) {
  4705. if (+dates[0] == +dates[1]) {
  4706. reportDayClick(dates[0], true, ev);
  4707. }
  4708. reportSelection(dates[0], dates[1], true, ev);
  4709. }
  4710. });
  4711. }
  4712. }
  4713. }
  4714. ;;
  4715. function OverlayManager() {
  4716. var t = this;
  4717. // exports
  4718. t.renderOverlay = renderOverlay;
  4719. t.clearOverlays = clearOverlays;
  4720. // locals
  4721. var usedOverlays = [];
  4722. var unusedOverlays = [];
  4723. function renderOverlay(rect, parent) {
  4724. var e = unusedOverlays.shift();
  4725. if (!e) {
  4726. e = $("<div class='fc-cell-overlay' style='position:absolute;z-index:3'/>");
  4727. }
  4728. if (e[0].parentNode != parent[0]) {
  4729. e.appendTo(parent);
  4730. }
  4731. usedOverlays.push(e.css(rect).show());
  4732. return e;
  4733. }
  4734. function clearOverlays() {
  4735. var e;
  4736. while (e = usedOverlays.shift()) {
  4737. unusedOverlays.push(e.hide().unbind());
  4738. }
  4739. }
  4740. }
  4741. ;;
  4742. function CoordinateGrid(buildFunc) {
  4743. var t = this;
  4744. var rows;
  4745. var cols;
  4746. t.build = function() {
  4747. rows = [];
  4748. cols = [];
  4749. buildFunc(rows, cols);
  4750. };
  4751. t.cell = function(x, y) {
  4752. var rowCnt = rows.length;
  4753. var colCnt = cols.length;
  4754. var i, r=-1, c=-1;
  4755. for (i=0; i<rowCnt; i++) {
  4756. if (y >= rows[i][0] && y < rows[i][1]) {
  4757. r = i;
  4758. break;
  4759. }
  4760. }
  4761. for (i=0; i<colCnt; i++) {
  4762. if (x >= cols[i][0] && x < cols[i][1]) {
  4763. c = i;
  4764. break;
  4765. }
  4766. }
  4767. return (r>=0 && c>=0) ? { row:r, col:c } : null;
  4768. };
  4769. t.rect = function(row0, col0, row1, col1, originElement) { // row1,col1 is inclusive
  4770. var origin = originElement.offset();
  4771. return {
  4772. top: rows[row0][0] - origin.top,
  4773. left: cols[col0][0] - origin.left,
  4774. width: cols[col1][1] - cols[col0][0],
  4775. height: rows[row1][1] - rows[row0][0]
  4776. };
  4777. };
  4778. }
  4779. ;;
  4780. function HoverListener(coordinateGrid) {
  4781. var t = this;
  4782. var bindType;
  4783. var change;
  4784. var firstCell;
  4785. var cell;
  4786. t.start = function(_change, ev, _bindType) {
  4787. change = _change;
  4788. firstCell = cell = null;
  4789. coordinateGrid.build();
  4790. mouse(ev);
  4791. bindType = _bindType || 'mousemove';
  4792. $(document).bind(bindType, mouse);
  4793. };
  4794. function mouse(ev) {
  4795. _fixUIEvent(ev); // see below
  4796. var newCell = coordinateGrid.cell(ev.pageX, ev.pageY);
  4797. if (!newCell != !cell || newCell && (newCell.row != cell.row || newCell.col != cell.col)) {
  4798. if (newCell) {
  4799. if (!firstCell) {
  4800. firstCell = newCell;
  4801. }
  4802. change(newCell, firstCell, newCell.row-firstCell.row, newCell.col-firstCell.col);
  4803. }else{
  4804. change(newCell, firstCell);
  4805. }
  4806. cell = newCell;
  4807. }
  4808. }
  4809. t.stop = function() {
  4810. $(document).unbind(bindType, mouse);
  4811. return cell;
  4812. };
  4813. }
  4814. // this fix was only necessary for jQuery UI 1.8.16 (and jQuery 1.7 or 1.7.1)
  4815. // upgrading to jQuery UI 1.8.17 (and using either jQuery 1.7 or 1.7.1) fixed the problem
  4816. // but keep this in here for 1.8.16 users
  4817. // and maybe remove it down the line
  4818. function _fixUIEvent(event) { // for issue 1168
  4819. if (event.pageX === undefined) {
  4820. event.pageX = event.originalEvent.pageX;
  4821. event.pageY = event.originalEvent.pageY;
  4822. }
  4823. }
  4824. ;;
  4825. function HorizontalPositionCache(getElement) {
  4826. var t = this,
  4827. elements = {},
  4828. lefts = {},
  4829. rights = {};
  4830. function e(i) {
  4831. return elements[i] = elements[i] || getElement(i);
  4832. }
  4833. t.left = function(i) {
  4834. return lefts[i] = lefts[i] === undefined ? e(i).position().left : lefts[i];
  4835. };
  4836. t.right = function(i) {
  4837. return rights[i] = rights[i] === undefined ? t.left(i) + e(i).width() : rights[i];
  4838. };
  4839. t.clear = function() {
  4840. elements = {};
  4841. lefts = {};
  4842. rights = {};
  4843. };
  4844. }
  4845. ;;
  4846. })(jQuery);
  4847. /**------------------------------------------------Chinese calendar--------------------------------------------------------------------------- */
  4848. (function(){
  4849. var DB = {
  4850. digitCN: ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十'],
  4851. heavenlyStems: ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'],
  4852. earthlyBranches: ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'],
  4853. chineseZodiac: ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪'],
  4854. term: ['冬至', '小寒', '大寒', '立春', '雨水', '惊蛰', '春分', '清明', '谷雨', '立夏', '小满', '芒种', '夏至', '小暑', '大暑', '立秋', '处暑', '白露', '秋分', '寒露', '霜降', '立冬', '小雪', '大雪'],
  4855. monthCn: ['十一', '十二', '正', '二', '三', '四', '五', '六', '七', '八', '九', '十'],
  4856. dateCn: ['初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十', '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十', '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十', '卅一']
  4857. };
  4858. function lunar(oDate){
  4859. var d = new Date(oDate);
  4860. if (+d < -10233222336000) {
  4861. return null;
  4862. }
  4863. return new Lunar(d);
  4864. }
  4865. function Lunar(oDate){
  4866. this.oDate = oDate;
  4867. this.julianDay = JulianDay.fromDate(this.oDate);
  4868. this.cnDay = this.oDate.getDay() ? DB.digitCN[this.oDate.getDay()] : '日';
  4869. var mjd = this.julianDay - JulianDay.JD2000;
  4870. var arTerm = Term.getTerms(mjd);
  4871. var arNewMoon = NewMoon.getNewMoons(arTerm);
  4872. for (var days = Math.round(mjd - arNewMoon[0].JD), i = 0, o; i < arNewMoon.length; i++) {
  4873. o = arNewMoon[i];
  4874. if (days < o.days) {
  4875. this.isBigMonth = o.days == 30;
  4876. this.isLeap = o.isLeap;
  4877. this.lMonth = o.name;
  4878. this.lNextMonth = o.nextName;
  4879. this.lDate = DB.dateCn[days];
  4880. this.dateIndex = days;
  4881. break;
  4882. } else {
  4883. days -= o.days;
  4884. }
  4885. }
  4886. mjd = Math.round(mjd);
  4887. this.lYear = Math.floor((arTerm[3].JD + (mjd < arTerm[3].JD ? -365 : 0) + 365.25 * 16 - 35) / 365.2422 + 0.5);
  4888. var t = arNewMoon[arNewMoon.zyIndex].JD;
  4889. t -= mjd < t ? 365 : 0;
  4890. t += 5810;
  4891. this.sYear = Math.floor(t / 365.2422 + 0.5);
  4892. this.hYear = this.sYear + 1984 + 2698;
  4893. var t = arTerm.hash[mjd];
  4894. this.term = t ? t.name : '';
  4895. var t = this.lYear + 6000;
  4896. this.animal = DB.chineseZodiac[t % 12];
  4897. this.gzYear = DB.heavenlyStems[t % 10] + DB.earthlyBranches[t % 12];
  4898. var t = Math.floor((mjd - arTerm[0].JD) / 30.43685);
  4899. t < 12 && mjd >= arTerm[2 * t + 1].JD && t++;
  4900. t += Math.floor((arTerm[12].JD + 390) / 365.2422) * 12 + 900000;
  4901. this.gzMonth = DB.heavenlyStems[t % 10] + DB.earthlyBranches[t % 12];
  4902. var t = mjd - 6 + 9000000;
  4903. this.gzDate = DB.heavenlyStems[t % 10] + DB.earthlyBranches[t % 12];
  4904. this.gzChrono = DB.earthlyBranches[Math.floor((this.oDate.getHours() + 1) / 2)] || '子';
  4905. }
  4906. var NewMoon = {
  4907. correction: unzip('EqoFscDcrFpmEsF1DfFideFelFpFfFfFiaipqti3ksttikptikqckstekqttgkqttgkqteksttikptikq1fjstgjqttjkqttgkqtekstfkptikq1tijstgjiFkirFsAeACoFsiDaDiADc3AFbBfgdfikijFifegF3FhaikgFag3E1btaieeibggiffdeigFfqDfaiBkF3kEaikhkigeidhhdiegcFfakF3ggkidbiaedksaFffckekidhhdhdikcikiakicjF3deedFhFccgicdekgiFbiaikcfi3kbFibefgEgFdcFkFeFkdcfkF3kfkcickEiFkDacFiEfbiaejcFfffkhkdgkaiei3ehigikhdFikfckF3dhhdikcfgjikhfjicjicgiehdikcikggcifgiejF3jkieFhegikggcikFegiegkfjebhigikggcikdgkaFkijcfkcikfkcifikiggkaeeigefkcdfcfkhkdgkegieidhijcFfakhfgeidieidiegikhfkfckfcjbdehdikggikgkfkicjicjF3dbidikFiggcifgiejkiegkigcdiegfggcikdbgfgefjF3kfegikggcikdgFkeeijcfkcikfkekcikdgkabhkFikaffcfkhkdgkegbiaekfkiakicjhfgqdq1fkiakgkfkhfkfcjiekgFebicggbedF3jikejbbbiakgbgkacgiejkijjgigfiakggfggcibFifjefjF3kfekdgjcibFeFkijcfkfhkfkeaieigekgbhkfikidfcjeaibgekgdkiffiffkiakF3jhbakgdki3dj3ikfkicjicjieeFkgdkicggkighdF3jfgkgfgbdkicggfggkidFkiekgijkeigfiskiggfaidheigF3jekijcikickiggkidhhdbgcfkFikikhkigeidieFikggikhkffaffijhidhhakgdkhkijF3kiakF3kfheakgdkifiggkigicjiejkieedikgdfcggkigieeiejfgkgkigbgikicggkiaideeijkefjeijikhkiggkiaidheigcikaikffikijgkiahi3hhdikgjfifaakekighie3hiaikggikhkffakicjhiahaikggikhkijF3kfejfeFhidikggiffiggkigicjiekgieeigikggiffiggkidheigkgfjkeigiegikifiggkidhedeijcfkFikikhkiggkidhh3ehigcikaffkhkiggkidhh3hhigikekfiFkFikcidhh3hitcikggikhkfkicjicghiediaikggikhkijbjfejfeFhaikggifikiggkigiejkikgkgieeigikggiffiggkigieeigekijcijikggifikiggkideedeijkefkfckikhkiggkidhh3ehijcikaffkhkiggkidhh3hhigikhkikFikfckcidhh3hiaikgjikhfjicjicgiehdikcikggifikigiejfejkieFhegikggifikiggfghigkfjeijkhigikggifikiggkigieeijcijcikfksikifikiggkidehdeijcfdckikhkiggkhghh3ehijikifffffkhsFngErD3pAfBoDd3BlEtFqA1AqoEpDqElAEsEeB1BmADlDkqBtC3FnEpDqnEmFsFsAFnllBbFmDsDiCtDmAB1BmtCgpEplCpAEiBiEoFqFtEqsDcCnFtADnFlEgdkEgmEtEsCtDmADqFtAFrAtEcCqAE3BoFqC3F3DrFtBmFtAC1ACnFaoCgADcADcCcFfoFtDlAFgmFqBq1bpEoAEmkqnEeCtAE3bAEqgDfFfCrgEcBrACfAAABqAAB3AAClEnFeCtCgAADqDoBmtAAACbFiAAADsEtBqAB1FsDqpFqEmFsCeDtFlCeDtoEpClEqAAFrAFoCgFmFsFqEnAEcCqFeCtFtEnAEeFtAAEkFnErAABbFkADnAAeCtFeAfBoAEpFtAABtFqAApDcCGJ'),
  4908. calcForLow: function(W){
  4909. var v = 7771.37714500204, t = (W + 1.08472) / v, L;
  4910. t -= (-0.0000331 * t * t + 0.10976 * Math.cos(0.785 + 8328.6914 * t) + 0.02224 * Math.cos(0.187 + 7214.0629 * t) - 0.03342 * Math.cos(4.669 + 628.3076 * t)) / v + (32 * (t + 1.8) * (t + 1.8) - 20) / 86400 / 36525;
  4911. return t * 36525 + 8 / 24;
  4912. },
  4913. calcForHigh: function(W){
  4914. var t = XL.MS_aLon_t2(W) * 36525;
  4915. t = t - dt_T(t) + 8 / 24;
  4916. var v = ((t + 0.5) % 1) * 86400;
  4917. if (v < 1800 || v > 86400 - 1800) t = XL.MS_aLon_t(W) * 36525 - dt_T(t) + 8 / 24;
  4918. return t;
  4919. },
  4920. cache: {},
  4921. getNewMoons: function(terms){
  4922. var jd = terms[0].JD, firstDay = NewMoon.calc(jd), ar = [];
  4923. if (firstDay > jd) {
  4924. firstDay -= 29.53;
  4925. }
  4926. if (NewMoon.cache[firstDay]) {
  4927. ar = NewMoon.cache[firstDay];
  4928. } else {
  4929. for (var i = 0; i < 15; i++) {
  4930. ar.push({
  4931. JD: NewMoon.calc(firstDay + 29.5306 * i),
  4932. index: i
  4933. });
  4934. if (i) {
  4935. ar[i - 1].days = ar[i].JD - ar[i - 1].JD;
  4936. }
  4937. }
  4938. if (ar[13].JD <= terms[24].JD) {
  4939. for (i = 1; ar[i + 1].JD > terms[2 * i].JD && i < 13; i++) {
  4940. }
  4941. ar[i].isLeap = true;
  4942. for (; i < 14; i++) {
  4943. ar[i].index--;
  4944. }
  4945. }
  4946. for (i = 0; i < 14; i++) {
  4947. ar[i].name = (ar[i].isLeap ? '闰' : '') + DB.monthCn[ar[i].index % 12];
  4948. ar[i].nextName = (ar[i+1].isLeap ? '闰' : '') + DB.monthCn[ar[i+1].index % 12];
  4949. if (ar[i].index == 2) {
  4950. ar.zyIndex = i;
  4951. }
  4952. }
  4953. NewMoon.cache[firstDay] = ar;
  4954. }
  4955. return ar;
  4956. },
  4957. calc: function(jd){
  4958. jd += JulianDay.JD2000;
  4959. var D, n, pc = 14, JDstart = 1947168.00, JD1960 = 2436935;
  4960. if (jd >= JD1960) {
  4961. return Math.floor(this.calcForHigh(Math.floor((jd + pc - 2451551) / 29.5306) * Math.PI * 2) + 0.5);
  4962. }
  4963. if (jd >= JDstart && jd < JD1960) {
  4964. D = Math.floor(this.calcForLow(Math.floor((jd + pc - 2451551) / 29.5306) * Math.PI * 2) + 0.5);
  4965. n = this.correction.substr(Math.floor((jd - JDstart) / 29.5306), 1) - 0;
  4966. return D + (n ? n - 2 : n);
  4967. }
  4968. }
  4969. };
  4970. var Term = {
  4971. correction: unzip('FrcFs11AFsckF1tsDtFqEtF3posFdFgiFseFtmelpsEfhkF1anmelpFlF3ikrotcnEqEq1FfqmcDsrFor11FgFrcgDscFs11FgEeFtE1sfFs11sCoEsaF1tsD3FpeE1eFsssEciFsFnmelpFcFhkF1tcnEqEpFgkrotcnEqrEtFermcDsrE111FgBmcmr11DaEfnaF111sD3FpeForeF1tssEfiFpEoeFssD3iFstEqFppDgFstcnEqEpFg33FscnEqrAoAF1ClAEsDmDtCtBaDlAFbAEpAAAAAD1FgBiBqoBbnBaBoAAAAAAAEgDqAdBqAFrBaBoACdAAf3AACgAAAeBbCamDgEifAE1AABa3C3BgFdiAAACoCeE3ADiEifDaAEqAAFe3AcFbcAAAAAF3iFaAAACpACmFmAAAAAAAACrDaAAADG0'),
  4972. calcForLow: function(W){
  4973. var t, L, v = 628.3319653318;
  4974. t = (W - 4.895062166) / v;
  4975. t -= (53 * t * t + 334116 * Math.cos(4.67 + 628.307585 * t) + 2061 * Math.cos(2.678 + 628.3076 * t) * t) / v / 10000000;
  4976. L = 48950621.66 + 6283319653.318 * t + 53 * t * t + 334166 * Math.cos(4.669257 + 628.307585 * t) + 3489 * Math.cos(4.6261 + 1256.61517 * t) + 2060.6 * Math.cos(2.67823 + 628.307585 * t) * t - 994 - 834 * Math.sin(2.1824 - 33.75705 * t);
  4977. t -= (L / 10000000 - W) / 628.332 + (32 * (t + 1.8) * (t + 1.8) - 20) / 86400 / 36525;
  4978. return t * 36525 + 8 / 24;
  4979. },
  4980. getNearestWinter: function(jd){
  4981. var winterDay = Math.floor((jd - 355 + 183) / 365.2422) * 365.2422 + 355;
  4982. Term.calc(winterDay) > jd && (winterDay -= 365.2422);
  4983. return winterDay;
  4984. },
  4985. cache: {},
  4986. getTerms: function(jd){
  4987. var winterDay = Term.getNearestWinter(jd), ar = [];
  4988. if (Term.cache[winterDay]) {
  4989. ar = Term.cache[winterDay];
  4990. } else {
  4991. ar.hash = {};
  4992. for (var i = 0, o; i < 25; i++) {
  4993. o = {
  4994. JD: Term.calc(winterDay + 15.2184 * i),
  4995. name: DB.term[i % 24]
  4996. };
  4997. ar.push(o);
  4998. ar.hash[o.JD] = o;
  4999. }
  5000. Term.cache[winterDay] = ar;
  5001. }
  5002. return ar;
  5003. },
  5004. calcForHigh: function(W){
  5005. var t = XL.S_aLon_t2(W) * 36525;
  5006. t = t - dt_T(t) + 8 / 24;
  5007. var v = ((t + 0.5) % 1) * 86400;
  5008. if (v < 1200 || v > 86400 - 1200) {
  5009. t = XL.S_aLon_t(W) * 36525 - dt_T(t) + 8 / 24;
  5010. }
  5011. return t;
  5012. },
  5013. calc: function(jd){
  5014. jd += JulianDay.JD2000;
  5015. var i, D, n, pc = 7, JDstart = 2322147.76, JD1960 = 2436935;
  5016. if (jd >= JD1960) {
  5017. return Math.floor(this.calcForHigh(Math.floor((jd + pc - 2451259) / 365.2422 * 24) * Math.PI / 12) + 0.5);
  5018. }
  5019. if (jd >= JDstart && jd < JD1960) {
  5020. D = Math.floor(this.calcForLow(Math.floor((jd + pc - 2451259) / 365.2422 * 24) * Math.PI / 12) + 0.5);
  5021. n = this.correction.substr(Math.floor((jd - JDstart) / 365.2422 * 24), 1) - 0;
  5022. return D + (n ? n - 2 : n);
  5023. }
  5024. }
  5025. };
  5026. var JulianDay = {
  5027. JD2000: 2451545,
  5028. fromDate: function(oDate){
  5029. var y = oDate.getFullYear(), m = oDate.getMonth() + 1, d = oDate.getDate() + ((oDate.getSeconds() / 60 + oDate.getMinutes()) / 60 + oDate.getHours()) / 24;
  5030. var isGregory = y * 372 + m * 31 + Math.floor(d) >= 588829, numLeap = 0;
  5031. if (m <= 2) {
  5032. m += 12;
  5033. y -= 1;
  5034. }
  5035. if (isGregory) {
  5036. numLeap = Math.floor(y / 100);
  5037. numLeap = 2 - numLeap + Math.floor(numLeap / 4);
  5038. }
  5039. return Math.floor(365.25 * (y + 4716)) + Math.floor(30.6001 * (m + 1)) + d + numLeap - 1524.5;
  5040. }
  5041. };
  5042. var XL0_xzb = [-0.08631, +0.00039, -0.00008, -0.07447, +0.00006, +0.00017, -0.07135, -0.00026, -0.00176, -0.20239, +0.00273, -0.00347, -0.25486, +0.00276, +0.42926, +0.24588, +0.00345, -14.46266, -0.95116, +0.02481, +58.30651];
  5043. var XL0 = new Array(new Array(10000000000, 20, 578, 920, 1100, 1124, 1136, 1148, 1217, 1226, 1229, 1229, 1229, 1229, 1937, 2363, 2618, 2633, 2660, 2666, 17534704567, 0.00000000000, 0.00000000000, 334165646, 4.669256804, 6283.075849991, 3489428, 4.6261024, 12566.1517000, 349706, 2.744118, 5753.384885, 341757, 2.828866, 3.523118, 313590, 3.627670, 77713.771468, 267622, 4.418084, 7860.419392, 234269, 6.135162, 3930.209696, 132429, 0.742464, 11506.769770, 127317, 2.037097, 529.690965, 119917, 1.109629, 1577.343542, 99025, 5.23268, 5884.92685, 90186, 2.04505, 26.29832, 85722, 3.50849, 398.14900, 77979, 1.17883, 5223.69392, 75314, 2.53339, 5507.55324, 50526, 4.58293, 18849.22755, 49238, 4.20507, 775.52261, 35666, 2.91954, 0.06731, 31709, 5.84902, 11790.62909, 28413, 1.89869, 796.29801, 27104, 0.31489, 10977.07880, 24281, 0.34481, 5486.77784, 20616, 4.80647, 2544.31442, 20539, 1.86948, 5573.14280, 20226, 2.45768, 6069.77675, 15552, 0.83306, 213.29910, 13221, 3.41118, 2942.46342, 12618, 1.08303, 20.77540, 11513, 0.64545, 0.98032, 10285, 0.63600, 4694.00295, 10190, 0.97569, 15720.83878, 10172, 4.26680, 7.11355, 9921, 6.2099, 2146.1654, 9761, 0.6810, 155.4204, 8580, 5.9832, 161000.6857, 8513, 1.2987, 6275.9623, 8471, 3.6708, 71430.6956, 7964, 1.8079, 17260.1547, 7876, 3.0370, 12036.4607, 7465, 1.7551, 5088.6288, 7387, 3.5032, 3154.6871, 7355, 4.6793, 801.8209, 6963, 0.8330, 9437.7629, 6245, 3.9776, 8827.3903, 6115, 1.8184, 7084.8968, 5696, 2.7843, 6286.5990, 5612, 4.3869, 14143.4952, 5558, 3.4701, 6279.5527, 5199, 0.1891, 12139.5535, 5161, 1.3328, 1748.0164, 5115, 0.2831, 5856.4777, 4900, 0.4874, 1194.4470, 4104, 5.3682, 8429.2413, 4094, 2.3985, 19651.0485, 3920, 6.1683, 10447.3878, 3677, 6.0413, 10213.2855, 3660, 2.5696, 1059.3819, 3595, 1.7088, 2352.8662, 3557, 1.7760, 6812.7668, 3329, 0.5931, 17789.8456, 3041, 0.4429, 83996.8473, 3005, 2.7398, 1349.8674, 2535, 3.1647, 4690.4798, 2474, 0.2148, 3.5904, 2366, 0.4847, 8031.0923, 2357, 2.0653, 3340.6124, 2282, 5.2220, 4705.7323, 2189, 5.5559, 553.5694, 2142, 1.4256, 16730.4637, 2109, 4.1483, 951.7184, 2030, 0.3713, 283.8593, 1992, 5.2221, 12168.0027, 1986, 5.7747, 6309.3742, 1912, 3.8222, 23581.2582, 1889, 5.3863, 149854.4001, 1790, 2.2149, 13367.9726, 1748, 4.5605, 135.0651, 1622, 5.9884, 11769.8537, 1508, 4.1957, 6256.7775, 1442, 4.1932, 242.7286, 1435, 3.7236, 38.0277, 1397, 4.4014, 6681.2249, 1362, 1.8893, 7632.9433, 1250, 1.1305, 5.5229, 1205, 2.6223, 955.5997, 1200, 1.0035, 632.7837, 1129, 0.1774, 4164.3120, 1083, 0.3273, 103.0928, 1052, 0.9387, 11926.2544, 1050, 5.3591, 1592.5960, 1033, 6.1998, 6438.4962, 1001, 6.0291, 5746.2713, 980, 0.999, 11371.705, 980, 5.244, 27511.468, 938, 2.624, 5760.498, 923, 0.483, 522.577, 922, 4.571, 4292.331, 905, 5.337, 6386.169, 862, 4.165, 7058.598, 841, 3.299, 7234.794, 836, 4.539, 25132.303, 813, 6.112, 4732.031, 812, 6.271, 426.598, 801, 5.821, 28.449, 787, 0.996, 5643.179, 776, 2.957, 23013.540, 769, 3.121, 7238.676, 758, 3.974, 11499.656, 735, 4.386, 316.392, 731, 0.607, 11513.883, 719, 3.998, 74.782, 706, 0.323, 263.084, 676, 5.911, 90955.552, 663, 3.665, 17298.182, 653, 5.791, 18073.705, 630, 4.717, 6836.645, 615, 1.458, 233141.314, 612, 1.075, 19804.827, 596, 3.321, 6283.009, 596, 2.876, 6283.143, 555, 2.452, 12352.853, 541, 5.392, 419.485, 531, 0.382, 31441.678, 519, 4.065, 6208.294, 513, 2.361, 10973.556, 494, 5.737, 9917.697, 450, 3.272, 11015.106, 449, 3.653, 206.186, 447, 2.064, 7079.374, 435, 4.423, 5216.580, 421, 1.906, 245.832, 413, 0.921, 3738.761, 402, 0.840, 20.355, 387, 1.826, 11856.219, 379, 2.344, 3.881, 374, 2.954, 3128.389, 370, 5.031, 536.805, 365, 1.018, 16200.773, 365, 1.083, 88860.057, 352, 5.978, 3894.182, 352, 2.056, 244287.600, 351, 3.713, 6290.189, 340, 1.106, 14712.317, 339, 0.978, 8635.942, 339, 3.202, 5120.601, 333, 0.837, 6496.375, 325, 3.479, 6133.513, 316, 5.089, 21228.392, 316, 1.328, 10873.986, 309, 3.646, 10.637, 303, 1.802, 35371.887, 296, 3.397, 9225.539, 288, 6.026, 154717.610, 281, 2.585, 14314.168, 262, 3.856, 266.607, 262, 2.579, 22483.849, 257, 1.561, 23543.231, 255, 3.949, 1990.745, 251, 3.744, 10575.407, 240, 1.161, 10984.192, 238, 0.106, 7.046, 236, 4.272, 6040.347, 234, 3.577, 10969.965, 211, 3.714, 65147.620, 210, 0.754, 13521.751, 207, 4.228, 5650.292, 202, 0.814, 170.673, 201, 4.629, 6037.244, 200, 0.381, 6172.870, 199, 3.933, 6206.810, 199, 5.197, 6262.300, 197, 1.046, 18209.330, 195, 1.070, 5230.807, 195, 4.869, 36.028, 194, 4.313, 6244.943, 192, 1.229, 709.933, 192, 5.595, 6282.096, 192, 0.602, 6284.056, 189, 3.744, 23.878, 188, 1.904, 15.252, 188, 0.867, 22003.915, 182, 3.681, 15110.466, 181, 0.491, 1.484, 179, 3.222, 39302.097, 179, 1.259, 12559.038, 62833196674749, 0.000000000000, 0.000000000000, 20605886, 2.67823456, 6283.07584999, 430343, 2.635127, 12566.151700, 42526, 1.59047, 3.52312, 11926, 5.79557, 26.29832, 10898, 2.96618, 1577.34354, 9348, 2.5921, 18849.2275, 7212, 1.1385, 529.6910, 6777, 1.8747, 398.1490, 6733, 4.4092, 5507.5532, 5903, 2.8880, 5223.6939, 5598, 2.1747, 155.4204, 4541, 0.3980, 796.2980, 3637, 0.4662, 775.5226, 2896, 2.6471, 7.1135, 2084, 5.3414, 0.9803, 1910, 1.8463, 5486.7778, 1851, 4.9686, 213.2991, 1729, 2.9912, 6275.9623, 1623, 0.0322, 2544.3144, 1583, 1.4305, 2146.1654, 1462, 1.2053, 10977.0788, 1246, 2.8343, 1748.0164, 1188, 3.2580, 5088.6288, 1181, 5.2738, 1194.4470, 1151, 2.0750, 4694.0030, 1064, 0.7661, 553.5694, 997, 1.303, 6286.599, 972, 4.239, 1349.867, 945, 2.700, 242.729, 858, 5.645, 951.718, 758, 5.301, 2352.866, 639, 2.650, 9437.763, 610, 4.666, 4690.480, 583, 1.766, 1059.382, 531, 0.909, 3154.687, 522, 5.661, 71430.696, 520, 1.854, 801.821, 504, 1.425, 6438.496, 433, 0.241, 6812.767, 426, 0.774, 10447.388, 413, 5.240, 7084.897, 374, 2.001, 8031.092, 356, 2.429, 14143.495, 350, 4.800, 6279.553, 337, 0.888, 12036.461, 337, 3.862, 1592.596, 325, 3.400, 7632.943, 322, 0.616, 8429.241, 318, 3.188, 4705.732, 297, 6.070, 4292.331, 295, 1.431, 5746.271, 290, 2.325, 20.355, 275, 0.935, 5760.498, 270, 4.804, 7234.794, 253, 6.223, 6836.645, 228, 5.003, 17789.846, 225, 5.672, 11499.656, 215, 5.202, 11513.883, 208, 3.955, 10213.286, 208, 2.268, 522.577, 206, 2.224, 5856.478, 206, 2.550, 25132.303, 203, 0.910, 6256.778, 189, 0.532, 3340.612, 188, 4.735, 83996.847, 179, 1.474, 4164.312, 178, 3.025, 5.523, 177, 3.026, 5753.385, 159, 4.637, 3.286, 157, 6.124, 5216.580, 155, 3.077, 6681.225, 154, 4.200, 13367.973, 143, 1.191, 3894.182, 138, 3.093, 135.065, 136, 4.245, 426.598, 134, 5.765, 6040.347, 128, 3.085, 5643.179, 127, 2.092, 6290.189, 125, 3.077, 11926.254, 125, 3.445, 536.805, 114, 3.244, 12168.003, 112, 2.318, 16730.464, 111, 3.901, 11506.770, 111, 5.320, 23.878, 105, 3.750, 7860.419, 103, 2.447, 1990.745, 96, 0.82, 3.88, 96, 4.08, 6127.66, 91, 5.42, 206.19, 91, 0.42, 7079.37, 88, 5.17, 11790.63, 81, 0.34, 9917.70, 80, 3.89, 10973.56, 78, 2.40, 1589.07, 78, 2.58, 11371.70, 77, 3.98, 955.60, 77, 3.36, 36.03, 76, 1.30, 103.09, 75, 5.18, 10969.97, 75, 4.96, 6496.37, 73, 5.21, 38.03, 72, 2.65, 6309.37, 70, 5.61, 3738.76, 69, 2.60, 3496.03, 69, 0.39, 15.25, 69, 2.78, 20.78, 65, 1.13, 7058.60, 64, 4.28, 28.45, 61, 5.63, 10984.19, 60, 0.73, 419.48, 60, 5.28, 10575.41, 58, 5.55, 17298.18, 58, 3.19, 4732.03, 5291887, 0.0000000, 0.0000000, 871984, 1.072097, 6283.075850, 30913, 0.86729, 12566.15170, 2734, 0.0530, 3.5231, 1633, 5.1883, 26.2983, 1575, 3.6846, 155.4204, 954, 0.757, 18849.228, 894, 2.057, 77713.771, 695, 0.827, 775.523, 506, 4.663, 1577.344, 406, 1.031, 7.114, 381, 3.441, 5573.143, 346, 5.141, 796.298, 317, 6.053, 5507.553, 302, 1.192, 242.729, 289, 6.117, 529.691, 271, 0.306, 398.149, 254, 2.280, 553.569, 237, 4.381, 5223.694, 208, 3.754, 0.980, 168, 0.902, 951.718, 153, 5.759, 1349.867, 145, 4.364, 1748.016, 134, 3.721, 1194.447, 125, 2.948, 6438.496, 122, 2.973, 2146.165, 110, 1.271, 161000.686, 104, 0.604, 3154.687, 100, 5.986, 6286.599, 92, 4.80, 5088.63, 89, 5.23, 7084.90, 83, 3.31, 213.30, 76, 3.42, 5486.78, 71, 6.19, 4690.48, 68, 3.43, 4694.00, 65, 1.60, 2544.31, 64, 1.98, 801.82, 61, 2.48, 10977.08, 50, 1.44, 6836.65, 49, 2.34, 1592.60, 46, 1.31, 4292.33, 46, 3.81, 149854.40, 43, 0.04, 7234.79, 40, 4.94, 7632.94, 39, 1.57, 71430.70, 38, 3.17, 6309.37, 35, 0.99, 6040.35, 35, 0.67, 1059.38, 31, 3.18, 2352.87, 31, 3.55, 8031.09, 30, 1.92, 10447.39, 30, 2.52, 6127.66, 28, 4.42, 9437.76, 28, 2.71, 3894.18, 27, 0.67, 25132.30, 26, 5.27, 6812.77, 25, 0.55, 6279.55, 23, 1.38, 4705.73, 22, 0.64, 6256.78, 20, 6.07, 640.88, 28923, 5.84384, 6283.07585, 3496, 0.0000, 0.0000, 1682, 5.4877, 12566.1517, 296, 5.196, 155.420, 129, 4.722, 3.523, 71, 5.30, 18849.23, 64, 5.97, 242.73, 40, 3.79, 553.57, 11408, 3.14159, 0.00000, 772, 4.134, 6283.076, 77, 3.84, 12566.15, 42, 0.42, 155.42, 88, 3.14, 0.00, 17, 2.77, 6283.08, 5, 2.01, 155.42, 3, 2.21, 12566.15, 27962, 3.19870, 84334.66158, 10164, 5.42249, 5507.55324, 8045, 3.8801, 5223.6939, 4381, 3.7044, 2352.8662, 3193, 4.0003, 1577.3435, 2272, 3.9847, 1047.7473, 1814, 4.9837, 6283.0758, 1639, 3.5646, 5856.4777, 1444, 3.7028, 9437.7629, 1430, 3.4112, 10213.2855, 1125, 4.8282, 14143.4952, 1090, 2.0857, 6812.7668, 1037, 4.0566, 71092.8814, 971, 3.473, 4694.003, 915, 1.142, 6620.890, 878, 4.440, 5753.385, 837, 4.993, 7084.897, 770, 5.554, 167621.576, 719, 3.602, 529.691, 692, 4.326, 6275.962, 558, 4.410, 7860.419, 529, 2.484, 4705.732, 521, 6.250, 18073.705, 903, 3.897, 5507.553, 618, 1.730, 5223.694, 380, 5.244, 2352.866, 166, 1.627, 84334.662, 10001398880, 0.00000000000, 0.00000000000, 167069963, 3.098463508, 6283.075849991, 1395602, 3.0552461, 12566.1517000, 308372, 5.198467, 77713.771468, 162846, 1.173877, 5753.384885, 157557, 2.846852, 7860.419392, 92480, 5.45292, 11506.76977, 54244, 4.56409, 3930.20970, 47211, 3.66100, 5884.92685, 34598, 0.96369, 5507.55324, 32878, 5.89984, 5223.69392, 30678, 0.29867, 5573.14280, 24319, 4.27350, 11790.62909, 21183, 5.84715, 1577.34354, 18575, 5.02194, 10977.07880, 17484, 3.01194, 18849.22755, 10984, 5.05511, 5486.77784, 9832, 0.8868, 6069.7768, 8650, 5.6896, 15720.8388, 8583, 1.2708, 161000.6857, 6490, 0.2725, 17260.1547, 6292, 0.9218, 529.6910, 5706, 2.0137, 83996.8473, 5574, 5.2416, 71430.6956, 4938, 3.2450, 2544.3144, 4696, 2.5781, 775.5226, 4466, 5.5372, 9437.7629, 4252, 6.0111, 6275.9623, 3897, 5.3607, 4694.0030, 3825, 2.3926, 8827.3903, 3749, 0.8295, 19651.0485, 3696, 4.9011, 12139.5535, 3566, 1.6747, 12036.4607, 3454, 1.8427, 2942.4634, 3319, 0.2437, 7084.8968, 3192, 0.1837, 5088.6288, 3185, 1.7778, 398.1490, 2846, 1.2134, 6286.5990, 2779, 1.8993, 6279.5527, 2628, 4.5890, 10447.3878, 2460, 3.7866, 8429.2413, 2393, 4.9960, 5856.4777, 2359, 0.2687, 796.2980, 2329, 2.8078, 14143.4952, 2210, 1.9500, 3154.6871, 2035, 4.6527, 2146.1654, 1951, 5.3823, 2352.8662, 1883, 0.6731, 149854.4001, 1833, 2.2535, 23581.2582, 1796, 0.1987, 6812.7668, 1731, 6.1520, 16730.4637, 1717, 4.4332, 10213.2855, 1619, 5.2316, 17789.8456, 1381, 5.1896, 8031.0923, 1364, 3.6852, 4705.7323, 1314, 0.6529, 13367.9726, 1041, 4.3329, 11769.8537, 1017, 1.5939, 4690.4798, 998, 4.201, 6309.374, 966, 3.676, 27511.468, 874, 6.064, 1748.016, 779, 3.674, 12168.003, 771, 0.312, 7632.943, 756, 2.626, 6256.778, 746, 5.648, 11926.254, 693, 2.924, 6681.225, 680, 1.423, 23013.540, 674, 0.563, 3340.612, 663, 5.661, 11371.705, 659, 3.136, 801.821, 648, 2.650, 19804.827, 615, 3.029, 233141.314, 612, 5.134, 1194.447, 563, 4.341, 90955.552, 552, 2.091, 17298.182, 534, 5.100, 31441.678, 531, 2.407, 11499.656, 523, 4.624, 6438.496, 513, 5.324, 11513.883, 477, 0.256, 11856.219, 461, 1.722, 7234.794, 458, 3.766, 6386.169, 458, 4.466, 5746.271, 423, 1.055, 5760.498, 422, 1.557, 7238.676, 415, 2.599, 7058.598, 401, 3.030, 1059.382, 397, 1.201, 1349.867, 379, 4.907, 4164.312, 360, 5.707, 5643.179, 352, 3.626, 244287.600, 348, 0.761, 10973.556, 342, 3.001, 4292.331, 336, 4.546, 4732.031, 334, 3.138, 6836.645, 324, 4.164, 9917.697, 316, 1.691, 11015.106, 307, 0.238, 35371.887, 298, 1.306, 6283.143, 298, 1.750, 6283.009, 293, 5.738, 16200.773, 286, 5.928, 14712.317, 281, 3.515, 21228.392, 280, 5.663, 8635.942, 277, 0.513, 26.298, 268, 4.207, 18073.705, 266, 0.900, 12352.853, 260, 2.962, 25132.303, 255, 2.477, 6208.294, 242, 2.800, 709.933, 231, 1.054, 22483.849, 229, 1.070, 14314.168, 216, 1.314, 154717.610, 215, 6.038, 10873.986, 200, 0.561, 7079.374, 198, 2.614, 951.718, 197, 4.369, 167283.762, 186, 2.861, 5216.580, 183, 1.660, 39302.097, 183, 5.912, 3738.761, 175, 2.145, 6290.189, 173, 2.168, 10575.407, 171, 3.702, 1592.596, 171, 1.343, 3128.389, 164, 5.550, 6496.375, 164, 5.856, 10984.192, 161, 1.998, 10969.965, 161, 1.909, 6133.513, 157, 4.955, 25158.602, 154, 6.216, 23543.231, 153, 5.357, 13521.751, 150, 5.770, 18209.330, 150, 5.439, 155.420, 139, 1.778, 9225.539, 139, 1.626, 5120.601, 128, 2.460, 13916.019, 123, 0.717, 143571.324, 122, 2.654, 88860.057, 121, 4.414, 3894.182, 121, 1.192, 3.523, 120, 4.030, 553.569, 119, 1.513, 17654.781, 117, 3.117, 14945.316, 113, 2.698, 6040.347, 110, 3.085, 43232.307, 109, 0.998, 955.600, 108, 2.939, 17256.632, 107, 5.285, 65147.620, 103, 0.139, 11712.955, 103, 5.850, 213.299, 102, 3.046, 6037.244, 101, 2.842, 8662.240, 100, 3.626, 6262.300, 98, 2.36, 6206.81, 98, 5.11, 6172.87, 98, 2.00, 15110.47, 97, 2.67, 5650.29, 97, 2.75, 6244.94, 96, 4.02, 6282.10, 96, 5.31, 6284.06, 92, 0.10, 29088.81, 85, 3.26, 20426.57, 84, 2.60, 28766.92, 81, 3.58, 10177.26, 80, 5.81, 5230.81, 78, 2.53, 16496.36, 77, 4.06, 6127.66, 73, 0.04, 5481.25, 72, 5.96, 12559.04, 72, 5.92, 4136.91, 71, 5.49, 22003.91, 70, 3.41, 7.11, 69, 0.62, 11403.68, 69, 3.90, 1589.07, 69, 1.96, 12416.59, 69, 4.51, 426.60, 67, 1.61, 11087.29, 66, 4.50, 47162.52, 66, 5.08, 283.86, 66, 4.32, 16858.48, 65, 1.04, 6062.66, 64, 1.59, 18319.54, 63, 5.70, 45892.73, 63, 4.60, 66567.49, 63, 3.82, 13517.87, 62, 2.62, 11190.38, 61, 1.54, 33019.02, 60, 5.58, 10344.30, 60, 5.38, 316428.23, 60, 5.78, 632.78, 59, 6.12, 9623.69, 57, 0.16, 17267.27, 57, 3.86, 6076.89, 57, 1.98, 7668.64, 56, 4.78, 20199.09, 55, 4.56, 18875.53, 55, 3.51, 17253.04, 54, 3.07, 226858.24, 54, 4.83, 18422.63, 53, 5.02, 12132.44, 52, 3.63, 5333.90, 52, 0.97, 155427.54, 51, 3.36, 20597.24, 50, 0.99, 11609.86, 50, 2.21, 1990.75, 48, 1.62, 12146.67, 48, 1.17, 12569.67, 47, 4.62, 5436.99, 47, 1.81, 12562.63, 47, 0.59, 21954.16, 47, 0.76, 7342.46, 46, 0.27, 4590.91, 46, 3.77, 156137.48, 45, 5.66, 10454.50, 44, 5.84, 3496.03, 43, 0.24, 17996.03, 41, 5.93, 51092.73, 41, 4.21, 12592.45, 40, 5.14, 1551.05, 40, 5.28, 15671.08, 39, 3.69, 18052.93, 39, 4.94, 24356.78, 38, 2.72, 11933.37, 38, 5.23, 7477.52, 38, 4.99, 9779.11, 37, 3.70, 9388.01, 37, 4.44, 4535.06, 36, 2.16, 28237.23, 36, 2.54, 242.73, 36, 0.22, 5429.88, 35, 6.15, 19800.95, 35, 2.92, 36949.23, 34, 5.63, 2379.16, 34, 5.73, 16460.33, 34, 5.11, 5849.36, 33, 6.19, 6268.85, 10301861, 1.10748970, 6283.07584999, 172124, 1.064423, 12566.151700, 70222, 3.14159, 0.00000, 3235, 1.0217, 18849.2275, 3080, 2.8435, 5507.5532, 2497, 1.3191, 5223.6939, 1849, 1.4243, 1577.3435, 1008, 5.9138, 10977.0788, 865, 1.420, 6275.962, 863, 0.271, 5486.778, 507, 1.686, 5088.629, 499, 6.014, 6286.599, 467, 5.987, 529.691, 440, 0.518, 4694.003, 410, 1.084, 9437.763, 387, 4.750, 2544.314, 375, 5.071, 796.298, 352, 0.023, 83996.847, 344, 0.949, 71430.696, 341, 5.412, 775.523, 322, 6.156, 2146.165, 286, 5.484, 10447.388, 284, 3.420, 2352.866, 255, 6.132, 6438.496, 252, 0.243, 398.149, 243, 3.092, 4690.480, 225, 3.689, 7084.897, 220, 4.952, 6812.767, 219, 0.420, 8031.092, 209, 1.282, 1748.016, 193, 5.314, 8429.241, 185, 1.820, 7632.943, 175, 3.229, 6279.553, 173, 1.537, 4705.732, 158, 4.097, 11499.656, 158, 5.539, 3154.687, 150, 3.633, 11513.883, 148, 3.222, 7234.794, 147, 3.653, 1194.447, 144, 0.817, 14143.495, 135, 6.151, 5746.271, 134, 4.644, 6836.645, 128, 2.693, 1349.867, 123, 5.650, 5760.498, 118, 2.577, 13367.973, 113, 3.357, 17789.846, 110, 4.497, 4292.331, 108, 5.828, 12036.461, 102, 5.621, 6256.778, 99, 1.14, 1059.38, 98, 0.66, 5856.48, 93, 2.32, 10213.29, 92, 0.77, 16730.46, 88, 1.50, 11926.25, 86, 1.42, 5753.38, 85, 0.66, 155.42, 81, 1.64, 6681.22, 80, 4.11, 951.72, 66, 4.55, 5216.58, 65, 0.98, 25132.30, 64, 4.19, 6040.35, 64, 0.52, 6290.19, 63, 1.51, 5643.18, 59, 6.18, 4164.31, 57, 2.30, 10973.56, 55, 2.32, 11506.77, 55, 2.20, 1592.60, 55, 5.27, 3340.61, 54, 5.54, 553.57, 53, 5.04, 9917.70, 53, 0.92, 11371.70, 52, 3.98, 17298.18, 52, 3.60, 10969.97, 49, 5.91, 3894.18, 49, 2.51, 6127.66, 48, 1.67, 12168.00, 46, 0.31, 801.82, 42, 3.70, 10575.41, 42, 4.05, 10984.19, 40, 2.17, 7860.42, 40, 4.17, 26.30, 38, 5.82, 7058.60, 37, 3.39, 6496.37, 36, 1.08, 6309.37, 36, 5.34, 7079.37, 34, 3.62, 11790.63, 32, 0.32, 16200.77, 31, 4.24, 3738.76, 29, 4.55, 11856.22, 29, 1.26, 8635.94, 27, 3.45, 5884.93, 26, 5.08, 10177.26, 26, 5.38, 21228.39, 24, 2.26, 11712.96, 24, 1.05, 242.73, 24, 5.59, 6069.78, 23, 3.63, 6284.06, 23, 1.64, 4732.03, 22, 3.46, 213.30, 21, 1.05, 3496.03, 21, 3.92, 13916.02, 21, 4.01, 5230.81, 20, 5.16, 12352.85, 20, 0.69, 1990.75, 19, 2.73, 6062.66, 19, 5.01, 11015.11, 18, 6.04, 6283.01, 18, 2.85, 7238.68, 18, 5.60, 6283.14, 18, 5.16, 17253.04, 18, 2.54, 14314.17, 17, 1.58, 7.11, 17, 0.98, 3930.21, 17, 4.75, 17267.27, 16, 2.19, 6076.89, 16, 2.19, 18073.70, 16, 6.12, 3.52, 16, 4.61, 9623.69, 16, 3.40, 16496.36, 15, 0.19, 9779.11, 15, 5.30, 13517.87, 15, 4.26, 3128.39, 15, 0.81, 709.93, 14, 0.50, 25158.60, 14, 4.38, 4136.91, 13, 0.98, 65147.62, 13, 3.31, 154717.61, 13, 2.11, 1589.07, 13, 1.92, 22483.85, 12, 6.03, 9225.54, 12, 1.53, 12559.04, 12, 5.82, 6282.10, 12, 5.61, 5642.20, 12, 2.38, 167283.76, 12, 0.39, 12132.44, 12, 3.98, 4686.89, 12, 5.81, 12569.67, 12, 0.56, 5849.36, 11, 0.45, 6172.87, 11, 5.80, 16858.48, 11, 6.22, 12146.67, 11, 2.27, 5429.88, 435939, 5.784551, 6283.075850, 12363, 5.57935, 12566.15170, 1234, 3.1416, 0.0000, 879, 3.628, 77713.771, 569, 1.870, 5573.143, 330, 5.470, 18849.228, 147, 4.480, 5507.553, 110, 2.842, 161000.686, 101, 2.815, 5223.694, 85, 3.11, 1577.34, 65, 5.47, 775.52, 61, 1.38, 6438.50, 50, 4.42, 6286.60, 47, 3.66, 7084.90, 46, 5.39, 149854.40, 42, 0.90, 10977.08, 40, 3.20, 5088.63, 35, 1.81, 5486.78, 32, 5.35, 3154.69, 30, 3.52, 796.30, 29, 4.62, 4690.48, 28, 1.84, 4694.00, 27, 3.14, 71430.70, 27, 6.17, 6836.65, 26, 1.42, 2146.17, 25, 2.81, 1748.02, 24, 2.18, 155.42, 23, 4.76, 7234.79, 21, 3.38, 7632.94, 21, 0.22, 4705.73, 20, 4.22, 1349.87, 20, 2.01, 1194.45, 20, 4.58, 529.69, 19, 1.59, 6309.37, 18, 5.70, 6040.35, 18, 6.03, 4292.33, 17, 2.90, 9437.76, 17, 2.00, 8031.09, 17, 5.78, 83996.85, 16, 0.05, 2544.31, 15, 0.95, 6127.66, 14, 0.36, 10447.39, 14, 1.48, 2352.87, 13, 0.77, 553.57, 13, 5.48, 951.72, 13, 5.27, 6279.55, 13, 3.76, 6812.77, 11, 5.41, 6256.78, 10, 0.68, 1592.60, 10, 4.95, 398.15, 10, 1.15, 3894.18, 10, 5.20, 244287.60, 10, 1.94, 11856.22, 9, 5.39, 25132.30, 8, 6.18, 1059.38, 8, 0.69, 8429.24, 8, 5.85, 242.73, 7, 5.26, 14143.50, 7, 0.52, 801.82, 6, 2.24, 8635.94, 6, 4.00, 13367.97, 6, 2.77, 90955.55, 6, 5.17, 7058.60, 5, 1.46, 233141.31, 5, 4.13, 7860.42, 5, 3.91, 26.30, 5, 3.89, 12036.46, 5, 5.58, 6290.19, 5, 5.54, 1990.75, 5, 0.83, 11506.77, 5, 6.22, 6681.22, 4, 5.26, 10575.41, 4, 1.91, 7477.52, 4, 0.43, 10213.29, 4, 1.09, 709.93, 4, 5.09, 11015.11, 4, 4.22, 88860.06, 4, 3.57, 7079.37, 4, 1.98, 6284.06, 4, 3.93, 10973.56, 4, 6.18, 9917.70, 4, 0.36, 10177.26, 4, 2.75, 3738.76, 4, 3.33, 5643.18, 4, 5.36, 25158.60, 14459, 4.27319, 6283.07585, 673, 3.917, 12566.152, 77, 0.00, 0.00, 25, 3.73, 18849.23, 4, 2.80, 6286.60, 386, 2.564, 6283.076, 31, 2.27, 12566.15, 5, 3.44, 5573.14, 2, 2.05, 18849.23, 1, 2.06, 77713.77, 1, 4.41, 161000.69, 1, 3.82, 149854.40, 1, 4.08, 6127.66, 1, 5.26, 6438.50, 9, 1.22, 6283.08, 1, 0.66, 12566.15));
  5044. var XL1 = [[[22639.586, 0.78475822, 8328.691424623, 1.5229241, 25.0719, -0.123598, 4586.438, 0.1873974, 7214.06286536, -2.184756, -18.860, 0.08280, 2369.914, 2.5429520, 15542.75428998, -0.661832, 6.212, -0.04080, 769.026, 3.140313, 16657.38284925, 3.04585, 50.144, -0.2472, 666.418, 1.527671, 628.30195521, -0.02664, 0.062, -0.0054, 411.596, 4.826607, 16866.9323150, -1.28012, -1.07, -0.0059, 211.656, 4.115028, -1114.6285593, -3.70768, -43.93, 0.2064, 205.436, 0.230523, 6585.7609101, -2.15812, -18.92, 0.0882, 191.956, 4.898507, 23871.4457146, 0.86109, 31.28, -0.164, 164.729, 2.586078, 14914.4523348, -0.6352, 6.15, -0.035, 147.321, 5.45530, -7700.3894694, -1.5496, -25.01, 0.118, 124.988, 0.48608, 7771.3771450, -0.3309, 3.11, -0.020, 109.380, 3.88323, 8956.9933798, 1.4963, 25.13, -0.129, 55.177, 5.57033, -1324.1780250, 0.6183, 7.3, -0.035, 45.100, 0.89898, 25195.623740, 0.2428, 24.0, -0.129, 39.533, 3.81213, -8538.240890, 2.8030, 26.1, -0.118, 38.430, 4.30115, 22756.817155, -2.8466, -12.6, 0.042, 36.124, 5.49587, 24986.074274, 4.5688, 75.2, -0.371, 30.773, 1.94559, 14428.125731, -4.3695, -37.7, 0.166, 28.397, 3.28586, 7842.364821, -2.2114, -18.8, 0.077, 24.358, 5.64142, 16171.056245, -0.6885, 6.3, -0.046, 18.585, 4.41371, -557.314280, -1.8538, -22.0, 0.10, 17.954, 3.58454, 8399.679100, -0.3576, 3.2, -0.03, 14.530, 4.9416, 23243.143759, 0.888, 31.2, -0.16, 14.380, 0.9709, 32200.137139, 2.384, 56.4, -0.29, 14.251, 5.7641, -2.301200, 1.523, 25.1, -0.12, 13.899, 0.3735, 31085.508580, -1.324, 12.4, -0.08, 13.194, 1.7595, -9443.319984, -5.231, -69.0, 0.33, 9.679, 3.0997, -16029.080894, -3.072, -50.1, 0.24, 9.366, 0.3016, 24080.995180, -3.465, -19.9, 0.08, 8.606, 4.1582, -1742.930514, -3.681, -44.0, 0.21, 8.453, 2.8416, 16100.068570, 1.192, 28.2, -0.14, 8.050, 2.6292, 14286.150380, -0.609, 6.1, -0.03, 7.630, 6.2388, 17285.684804, 3.019, 50.2, -0.25, 7.447, 1.4845, 1256.603910, -0.053, 0.1, -0.01, 7.371, 0.2736, 5957.458955, -2.131, -19.0, 0.09, 7.063, 5.6715, 33.757047, -0.308, -3.6, 0.02, 6.383, 4.7843, 7004.513400, 2.141, 32.4, -0.16, 5.742, 2.6572, 32409.686605, -1.942, 5, -0.05, 4.374, 4.3443, 22128.51520, -2.820, -13, 0.05, 3.998, 3.2545, 33524.31516, 1.766, 49, -0.25, 3.210, 2.2443, 14985.44001, -2.516, -16, 0.06, 2.915, 1.7138, 24499.74767, 0.834, 31, -0.17, 2.732, 1.9887, 13799.82378, -4.343, -38, 0.17, 2.568, 5.4122, -7072.08751, -1.576, -25, 0.11, 2.521, 3.2427, 8470.66678, -2.238, -19, 0.07, 2.489, 4.0719, -486.32660, -3.734, -44, 0.20, 2.146, 5.6135, -1952.47998, 0.645, 7, -0.03, 1.978, 2.7291, 39414.20000, 0.199, 37, -0.21, 1.934, 1.5682, 33314.76570, 6.092, 100, -0.5, 1.871, 0.4166, 30457.20662, -1.297, 12, -0.1, 1.753, 2.0582, -8886.00570, -3.38, -47, 0.2, 1.437, 2.386, -695.87607, 0.59, 7, 0, 1.373, 3.026, -209.54947, 4.33, 51, -0.2, 1.262, 5.940, 16728.37052, 1.17, 28, -0.1, 1.224, 6.172, 6656.74859, -4.04, -41, 0.2, 1.187, 5.873, 6099.43431, -5.89, -63, 0.3, 1.177, 1.014, 31571.83518, 2.41, 56, -0.3, 1.162, 3.840, 9585.29534, 1.47, 25, -0.1, 1.143, 5.639, 8364.73984, -2.18, -19, 0.1, 1.078, 1.229, 70.98768, -1.88, -22, 0.1, 1.059, 3.326, 40528.82856, 3.91, 81, -0.4, 0.990, 5.013, 40738.37803, -0.42, 30, -0.2, 0.948, 5.687, -17772.01141, -6.75, -94, 0.5, 0.876, 0.298, -0.35232, 0, 0, 0, 0.822, 2.994, 393.02097, 0, 0, 0, 0.788, 1.836, 8326.39022, 3.05, 50, -0.2, 0.752, 4.985, 22614.84180, 0.91, 31, -0.2, 0.740, 2.875, 8330.99262, 0, 0, 0, 0.669, 0.744, -24357.77232, -4.60, -75, 0.4, 0.644, 1.314, 8393.12577, -2.18, -19, 0.1, 0.639, 5.888, 575.33849, 0, 0, 0, 0.635, 1.116, 23385.11911, -2.87, -13, 0, 0.584, 5.197, 24428.75999, 2.71, 53, -0.3, 0.583, 3.513, -9095.55517, 0.95, 4, 0, 0.572, 6.059, 29970.88002, -5.03, -32, 0.1, 0.565, 2.960, 0.32863, 1.52, 25, -0.1, 0.561, 4.001, -17981.56087, -2.43, -43, 0.2, 0.557, 0.529, 7143.07519, -0.30, 3, 0, 0.546, 2.311, 25614.37623, 4.54, 75, -0.4, 0.536, 4.229, 15752.30376, -4.99, -45, 0.2, 0.493, 3.316, -8294.9344, -1.83, -29, 0.1, 0.491, 1.744, 8362.4485, 1.21, 21, -0.1, 0.478, 1.803, -10071.6219, -5.20, -69, 0.3, 0.454, 0.857, 15333.2048, 3.66, 57, -0.3, 0.445, 2.071, 8311.7707, -2.18, -19, 0.1, 0.426, 0.345, 23452.6932, -3.44, -20, 0.1, 0.420, 4.941, 33733.8646, -2.56, -2, 0, 0.413, 1.642, 17495.2343, -1.31, -1, 0, 0.404, 1.458, 23314.1314, -0.99, 9, -0.1, 0.395, 2.132, 38299.5714, -3.51, -6, 0, 0.382, 2.700, 31781.3846, -1.92, 5, 0, 0.375, 4.827, 6376.2114, 2.17, 32, -0.2, 0.361, 3.867, 16833.1753, -0.97, 3, 0, 0.358, 5.044, 15056.4277, -4.40, -38, 0.2, 0.350, 5.157, -8257.7037, -3.40, -47, 0.2, 0.344, 4.233, 157.7344, 0, 0, 0, 0.340, 2.672, 13657.8484, -0.58, 6, 0, 0.329, 5.610, 41853.0066, 3.29, 74, -0.4, 0.325, 5.895, -39.8149, 0, 0, 0, 0.309, 4.387, 21500.2132, -2.79, -13, 0.1, 0.302, 1.278, 786.0419, 0, 0, 0, 0.302, 5.341, -24567.3218, -0.27, -24, 0.1, 0.301, 1.045, 5889.8848, -1.57, -12, 0, 0.294, 4.201, -2371.2325, -3.65, -44, 0.2, 0.293, 3.704, 21642.1886, -6.55, -57, 0.2, 0.290, 4.069, 32828.4391, 2.36, 56, -0.3, 0.289, 3.472, 31713.8105, -1.35, 12, -0.1, 0.285, 5.407, -33.7814, 0.31, 4, 0, 0.283, 5.998, -16.9207, -3.71, -44, 0.2, 0.283, 2.772, 38785.8980, 0.23, 37, -0.2, 0.274, 5.343, 15613.7420, -2.54, -16, 0.1, 0.263, 3.997, 25823.9257, 0.22, 24, -0.1, 0.254, 0.600, 24638.3095, -1.61, 2, 0, 0.253, 1.344, 6447.1991, 0.29, 10, -0.1, 0.250, 0.887, 141.9754, -3.76, -44, 0.2, 0.247, 0.317, 5329.1570, -2.10, -19, 0.1, 0.245, 0.141, 36.0484, -3.71, -44, 0.2, 0.231, 2.287, 14357.1381, -2.49, -16, 0.1, 0.227, 5.158, 2.6298, 0, 0, 0, 0.219, 5.085, 47742.8914, 1.72, 63, -0.3, 0.211, 2.145, 6638.7244, -2.18, -19, 0.1, 0.201, 4.415, 39623.7495, -4.13, -14, 0, 0.194, 2.091, 588.4927, 0, 0, 0, 0.193, 3.057, -15400.7789, -3.10, -50, 0, 0.186, 5.598, 16799.3582, -0.72, 6, 0, 0.185, 3.886, 1150.6770, 0, 0, 0, 0.183, 1.619, 7178.0144, 1.52, 25, 0, 0.181, 2.635, 8328.3391, 1.52, 25, 0, 0.181, 2.077, 8329.0437, 1.52, 25, 0, 0.179, 3.215, -9652.8694, -0.90, -18, 0, 0.176, 1.716, -8815.0180, -5.26, -69, 0, 0.175, 5.673, 550.7553, 0, 0, 0, 0.170, 2.060, 31295.0580, -5.6, -39, 0, 0.167, 1.239, 7211.7617, -0.7, 6, 0, 0.165, 4.499, 14967.4158, -0.7, 6, 0, 0.164, 3.595, 15540.4531, 0.9, 31, 0, 0.164, 4.237, 522.3694, 0, 0, 0, 0.163, 4.633, 15545.0555, -2.2, -19, 0, 0.161, 0.478, 6428.0209, -2.2, -19, 0, 0.158, 2.03, 13171.5218, -4.3, -38, 0, 0.157, 2.28, 7216.3641, -3.7, -44, 0, 0.154, 5.65, 7935.6705, 1.5, 25, 0, 0.152, 0.46, 29828.9047, -1.3, 12, 0, 0.151, 1.19, -0.7113, 0, 0, 0, 0.150, 1.42, 23942.4334, -1.0, 9, 0, 0.144, 2.75, 7753.3529, 1.5, 25, 0, 0.137, 2.08, 7213.7105, -2.2, -19, 0, 0.137, 1.44, 7214.4152, -2.2, -19, 0, 0.136, 4.46, -1185.6162, -1.8, -22, 0, 0.136, 3.03, 8000.1048, -2.2, -19, 0, 0.134, 2.83, 14756.7124, -0.7, 6, 0, 0.131, 5.05, 6821.0419, -2.2, -19, 0, 0.128, 5.99, -17214.6971, -4.9, -72, 0, 0.127, 5.35, 8721.7124, 1.5, 25, 0, 0.126, 4.49, 46628.2629, -2.0, 19, 0, 0.125, 5.94, 7149.6285, 1.5, 25, 0, 0.124, 1.09, 49067.0695, 1.1, 55, 0, 0.121, 2.88, 15471.7666, 1.2, 28, 0, 0.111, 3.92, 41643.4571, 7.6, 125, -1, 0.110, 1.96, 8904.0299, 1.5, 25, 0, 0.106, 3.30, -18.0489, -2.2, -19, 0, 0.105, 2.30, -4.9310, 1.5, 25, 0, 0.104, 2.22, -6.5590, -1.9, -22, 0, 0.101, 1.44, 1884.9059, -0.1, 0, 0, 0.100, 5.92, 5471.1324, -5.9, -63, 0, 0.099, 1.12, 15149.7333, -0.7, 6, 0, 0.096, 4.73, 15508.9972, -0.4, 10, 0, 0.095, 5.18, 7230.9835, 1.5, 25, 0, 0.093, 3.37, 39900.5266, 3.9, 81, 0, 0.092, 2.01, 25057.0619, 2.7, 53, 0, 0.092, 1.21, -79.6298, 0, 0, 0, 0.092, 1.65, -26310.2523, -4.0, -68, 0, 0.091, 1.01, 42062.5561, -1.0, 23, 0, 0.090, 6.10, 29342.5781, -5.0, -32, 0, 0.090, 4.43, 15542.4020, -0.7, 6, 0, 0.090, 3.80, 15543.1066, -0.7, 6, 0, 0.089, 4.15, 6063.3859, -2.2, -19, 0, 0.086, 4.03, 52.9691, 0, 0, 0, 0.085, 0.49, 47952.4409, -2.6, 11, 0, 0.085, 1.60, 7632.8154, 2.1, 32, 0, 0.084, 0.22, 14392.0773, -0.7, 6, 0, 0.083, 6.22, 6028.4466, -4.0, -41, 0, 0.083, 0.63, -7909.9389, 2.8, 26, 0, 0.083, 5.20, -77.5523, 0, 0, 0, 0.082, 2.74, 8786.1467, -2.2, -19, 0, 0.080, 2.43, 9166.5428, -2.8, -26, 0, 0.080, 3.70, -25405.1732, 4.1, 27, 0, 0.078, 5.68, 48857.5200, 5.4, 106, -1, 0.077, 1.85, 8315.5735, -2.2, -19, 0, 0.075, 5.46, -18191.1103, 1.9, 8, 0, 0.075, 1.41, -16238.6304, 1.3, 1, 0, 0.074, 5.06, 40110.0761, -0.4, 30, 0, 0.072, 2.10, 64.4343, -3.7, -44, 0, 0.071, 2.17, 37671.2695, -3.5, -6, 0, 0.069, 1.71, 16693.4313, -0.7, 6, 0, 0.069, 3.33, -26100.7028, -8.3, -119, 1, 0.068, 1.09, 8329.4028, 1.5, 25, 0, 0.068, 3.62, 8327.9801, 1.5, 25, 0, 0.068, 2.41, 16833.1509, -1.0, 3, 0, 0.067, 3.40, 24709.2971, -3.5, -20, 0, 0.067, 1.65, 8346.7156, -0.3, 3, 0, 0.066, 2.61, 22547.2677, 1.5, 39, 0, 0.066, 3.50, 15576.5113, -1.0, 3, 0, 0.065, 5.76, 33037.9886, -2.0, 5, 0, 0.065, 4.58, 8322.1325, -0.3, 3, 0, 0.065, 6.20, 17913.9868, 3.0, 50, 0, 0.065, 1.50, 22685.8295, -1.0, 9, 0, 0.065, 2.37, 7180.3058, -1.9, -15, 0, 0.064, 1.06, 30943.5332, 2.4, 56, 0, 0.064, 1.89, 8288.8765, 1.5, 25, 0, 0.064, 4.70, 6.0335, 0.3, 4, 0, 0.063, 2.83, 8368.5063, 1.5, 25, 0, 0.063, 5.66, -2580.7819, 0.7, 7, 0, 0.062, 3.78, 7056.3285, -2.2, -19, 0, 0.061, 1.49, 8294.9100, 1.8, 29, 0, 0.061, 0.12, -10281.1714, -0.9, -18, 0, 0.061, 3.06, -8362.4729, -1.2, -21, 0, 0.061, 4.43, 8170.9571, 1.5, 25, 0, 0.059, 5.78, -13.1179, -3.7, -44, 0, 0.059, 5.97, 6625.5702, -2.2, -19, 0, 0.058, 5.01, -0.5080, -0.3, 0, 0, 0.058, 2.73, 7161.0938, -2.2, -19, 0, 0.057, 0.19, 7214.0629, -2.2, -19, 0, 0.057, 4.00, 22199.5029, -4.7, -35, 0, 0.057, 5.38, 8119.1420, 5.8, 76, 0, 0.056, 1.07, 7542.6495, 1.5, 25, 0, 0.056, 0.28, 8486.4258, 1.5, 25, 0, 0.054, 4.19, 16655.0816, 4.6, 75, 0, 0.053, 0.72, 7267.0320, -2.2, -19, 0, 0.053, 3.12, 12.6192, 0.6, 7, 0, 0.052, 2.99, -32896.013, -1.8, -49, 0, 0.052, 3.46, 1097.708, 0, 0, 0, 0.051, 5.37, -6443.786, -1.6, -25, 0, 0.051, 1.35, 7789.401, -2.2, -19, 0, 0.051, 5.83, 40042.502, 0.2, 38, 0, 0.051, 3.63, 9114.733, 1.5, 25, 0, 0.050, 1.51, 8504.484, -2.5, -22, 0, 0.050, 5.23, 16659.684, 1.5, 25, 0, 0.050, 1.15, 7247.820, -2.5, -23, 0, 0.047, 0.25, -1290.421, 0.3, 0, 0, 0.047, 4.67, -32686.464, -6.1, -100, 0, 0.047, 3.49, 548.678, 0, 0, 0, 0.047, 2.37, 6663.308, -2.2, -19, 0, 0.046, 0.98, 1572.084, 0, 0, 0, 0.046, 2.04, 14954.262, -0.7, 6, 0, 0.046, 3.72, 6691.693, -2.2, -19, 0, 0.045, 6.19, -235.287, 0, 0, 0, 0.044, 2.96, 32967.001, -0.1, 27, 0, 0.044, 3.82, -1671.943, -5.6, -66, 0, 0.043, 5.82, 1179.063, 0, 0, 0, 0.043, 0.07, 34152.617, 1.7, 49, 0, 0.043, 3.71, 6514.773, -0.3, 0, 0, 0.043, 5.62, 15.732, -2.5, -23, 0, 0.043, 5.80, 8351.233, -2.2, -19, 0, 0.042, 0.27, 7740.199, 1.5, 25, 0, 0.042, 6.14, 15385.020, -0.7, 6, 0, 0.042, 6.13, 7285.051, -4.1, -41, 0, 0.041, 1.27, 32757.451, 4.2, 78, 0, 0.041, 4.46, 8275.722, 1.5, 25, 0, 0.040, 0.23, 8381.661, 1.5, 25, 0, 0.040, 5.87, -766.864, 2.5, 29, 0, 0.040, 1.66, 254.431, 0, 0, 0, 0.040, 0.40, 9027.981, -0.4, 0, 0, 0.040, 2.96, 7777.936, 1.5, 25, 0, 0.039, 4.67, 33943.068, 6.1, 100, 0, 0.039, 3.52, 8326.062, 1.5, 25, 0, 0.039, 3.75, 21013.887, -6.5, -57, 0, 0.039, 5.60, 606.978, 0, 0, 0, 0.039, 1.19, 8331.321, 1.5, 25, 0, 0.039, 2.84, 7211.433, -2.2, -19, 0, 0.038, 0.67, 7216.693, -2.2, -19, 0, 0.038, 6.22, 25161.867, 0.6, 28, 0, 0.038, 4.40, 7806.322, 1.5, 25, 0, 0.038, 4.16, 9179.168, -2.2, -19, 0, 0.037, 4.73, 14991.999, -0.7, 6, 0, 0.036, 0.35, 67.514, -0.6, -7, 0, 0.036, 3.70, 25266.611, -1.6, 0, 0, 0.036, 5.39, 16328.796, -0.7, 6, 0, 0.035, 1.44, 7174.248, -2.2, -19, 0, 0.035, 5.00, 15684.730, -4.4, -38, 0, 0.035, 0.39, -15.419, -2.2, -19, 0, 0.035, 6.07, 15020.385, -0.7, 6, 0, 0.034, 6.01, 7371.797, -2.2, -19, 0, 0.034, 0.96, -16623.626, -3.4, -54, 0, 0.033, 6.24, 9479.368, 1.5, 25, 0, 0.033, 3.21, 23661.896, 5.2, 82, 0, 0.033, 4.06, 8311.418, -2.2, -19, 0, 0.033, 2.40, 1965.105, 0, 0, 0, 0.033, 5.17, 15489.785, -0.7, 6, 0, 0.033, 5.03, 21986.540, 0.9, 31, 0, 0.033, 4.10, 16691.140, 2.7, 46, 0, 0.033, 5.13, 47114.589, 1.7, 63, 0, 0.033, 4.45, 8917.184, 1.5, 25, 0, 0.033, 4.23, 2.078, 0, 0, 0, 0.032, 2.33, 75.251, 1.5, 25, 0, 0.032, 2.10, 7253.878, -2.2, -19, 0, 0.032, 3.11, -0.224, 1.5, 25, 0, 0.032, 4.43, 16640.462, -0.7, 6, 0, 0.032, 5.68, 8328.363, 0, 0, 0, 0.031, 5.32, 8329.020, 3.0, 50, 0, 0.031, 3.70, 16118.093, -0.7, 6, 0, 0.030, 3.67, 16721.817, -0.7, 6, 0, 0.030, 5.27, -1881.492, -1.2, -15, 0, 0.030, 5.72, 8157.839, -2.2, -19, 0, 0.029, 5.73, -18400.313, -6.7, -94, 0, 0.029, 2.76, 16.000, -2.2, -19, 0, 0.029, 1.75, 8879.447, 1.5, 25, 0, 0.029, 0.32, 8851.061, 1.5, 25, 0, 0.029, 0.90, 14704.903, 3.7, 57, 0, 0.028, 2.90, 15595.723, -0.7, 6, 0, 0.028, 5.88, 16864.631, 0.2, 24, 0, 0.028, 0.63, 16869.234, -2.8, -26, 0, 0.028, 4.04, -18609.863, -2.4, -43, 0, 0.027, 5.83, 6727.736, -5.9, -63, 0, 0.027, 6.12, 418.752, 4.3, 51, 0, 0.027, 0.14, 41157.131, 3.9, 81, 0, 0.026, 3.80, 15.542, 0, 0, 0, 0.026, 1.68, 50181.698, 4.8, 99, -1, 0.026, 0.32, 315.469, 0, 0, 0, 0.025, 5.67, 19.188, 0.3, 0, 0, 0.025, 3.16, 62.133, -2.2, -19, 0, 0.025, 3.76, 15502.939, -0.7, 6, 0, 0.025, 4.53, 45999.961, -2.0, 19, 0, 0.024, 3.21, 837.851, -4.4, -51, 0, 0.024, 2.82, 38157.596, 0.3, 37, 0, 0.024, 5.21, 15540.124, -0.7, 6, 0, 0.024, 0.26, 14218.576, 0, 13, 0, 0.024, 3.01, 15545.384, -0.7, 6, 0, 0.024, 1.16, -17424.247, -0.6, -21, 0, 0.023, 2.34, -67.574, 0.6, 7, 0, 0.023, 2.44, 18.024, -1.9, -22, 0, 0.023, 3.70, 469.400, 0, 0, 0, 0.023, 0.72, 7136.511, -2.2, -19, 0, 0.023, 4.50, 15582.569, -0.7, 6, 0, 0.023, 2.80, -16586.395, -4.9, -72, 0, 0.023, 1.51, 80.182, 0, 0, 0, 0.023, 1.09, 5261.583, -1.5, -12, 0, 0.023, 0.56, 54956.954, -0.5, 44, 0, 0.023, 4.01, 8550.860, -2.2, -19, 0, 0.023, 4.46, 38995.448, -4.1, -14, 0, 0.023, 3.82, 2358.126, 0, 0, 0, 0.022, 3.77, 32271.125, 0.5, 34, 0, 0.022, 0.82, 15935.775, -0.7, 6, 0, 0.022, 1.07, 24013.421, -2.9, -13, 0, 0.022, 0.40, 8940.078, -2.2, -19, 0, 0.022, 2.06, 15700.489, -0.7, 6, 0, 0.022, 4.27, 15124.002, -5.0, -45, 0, 0.021, 1.16, 56071.583, 3.2, 88, 0, 0.021, 5.58, 9572.189, -2.2, -19, 0, 0.020, 1.70, -17.273, -3.7, -44, 0, 0.020, 3.05, 214.617, 0, 0, 0, 0.020, 4.41, 8391.048, -2.2, -19, 0, 0.020, 5.95, 23869.145, 2.4, 56, 0, 0.020, 0.42, 40947.927, -4.7, -21, 0, 0.019, 1.39, 5818.897, 0.3, 10, 0, 0.019, 0.71, 23873.747, -0.7, 6, 0, 0.019, 2.81, 7291.615, -2.2, -19, 0, 0.019, 5.09, 8428.018, -2.2, -19, 0, 0.019, 4.14, 6518.187, -1.6, -12, 0, 0.019, 3.85, 21.330, 0, 0, 0, 0.018, 0.66, 14445.046, -0.7, 6, 0, 0.018, 1.65, 0.966, -4.0, -48, 0, 0.018, 5.64, -17143.709, -6.8, -94, 0, 0.018, 6.01, 7736.432, -2.2, -19, 0, 0.018, 2.74, 31153.083, -1.9, 5, 0, 0.018, 4.58, 6116.355, -2.2, -19, 0, 0.018, 2.28, 46.401, 0.3, 0, 0, 0.018, 3.80, 10213.597, 1.4, 25, 0, 0.018, 2.84, 56281.132, -1.1, 36, 0, 0.018, 3.53, 8249.062, 1.5, 25, 0, 0.017, 4.43, 20871.911, -3, -13, 0, 0.017, 4.44, 627.596, 0, 0, 0, 0.017, 1.85, 628.308, 0, 0, 0, 0.017, 1.19, 8408.321, 2, 25, 0, 0.017, 1.95, 7214.056, -2, -19, 0, 0.017, 1.57, 7214.070, -2, -19, 0, 0.017, 1.65, 13870.811, -6, -60, 0, 0.017, 0.30, 22.542, -4, -44, 0, 0.017, 2.62, -119.445, 0, 0, 0, 0.016, 4.87, 5747.909, 2, 32, 0, 0.016, 4.45, 14339.108, -1, 6, 0, 0.016, 1.83, 41366.680, 0, 30, 0, 0.016, 4.53, 16309.618, -3, -23, 0, 0.016, 2.54, 15542.754, -1, 6, 0, 0.016, 6.05, 1203.646, 0, 0, 0, 0.015, 5.2, 2751.147, 0, 0, 0, 0.015, 1.8, -10699.924, -5, -69, 0, 0.015, 0.4, 22824.391, -3, -20, 0, 0.015, 2.1, 30666.756, -6, -39, 0, 0.015, 2.1, 6010.417, -2, -19, 0, 0.015, 0.7, -23729.470, -5, -75, 0, 0.015, 1.4, 14363.691, -1, 6, 0, 0.015, 5.8, 16900.689, -2, 0, 0, 0.015, 5.2, 23800.458, 3, 53, 0, 0.015, 5.3, 6035.000, -2, -19, 0, 0.015, 1.2, 8251.139, 2, 25, 0, 0.015, 3.6, -8.860, 0, 0, 0, 0.015, 0.8, 882.739, 0, 0, 0, 0.015, 3.0, 1021.329, 0, 0, 0, 0.015, 0.6, 23296.107, 1, 31, 0, 0.014, 5.4, 7227.181, 2, 25, 0, 0.014, 0.1, 7213.352, -2, -19, 0, 0.014, 4.0, 15506.706, 3, 50, 0, 0.014, 3.4, 7214.774, -2, -19, 0, 0.014, 4.6, 6665.385, -2, -19, 0, 0.014, 0.1, -8.636, -2, -22, 0, 0.014, 3.1, 15465.202, -1, 6, 0, 0.014, 4.9, 508.863, 0, 0, 0, 0.014, 3.5, 8406.244, 2, 25, 0, 0.014, 1.3, 13313.497, -8, -82, 0, 0.014, 2.8, 49276.619, -3, 0, 0, 0.014, 0.1, 30528.194, -3, -10, 0, 0.013, 1.7, 25128.050, 1, 31, 0, 0.013, 2.9, 14128.405, -1, 6, 0, 0.013, 3.4, 57395.761, 3, 80, 0, 0.013, 2.7, 13029.546, -1, 6, 0, 0.013, 3.9, 7802.556, -2, -19, 0, 0.013, 1.6, 8258.802, -2, -19, 0, 0.013, 2.2, 8417.709, -2, -19, 0, 0.013, 0.7, 9965.210, -2, -19, 0, 0.013, 3.4, 50391.247, 0, 48, 0, 0.013, 3.0, 7134.433, -2, -19, 0, 0.013, 2.9, 30599.182, -5, -31, 0, 0.013, 3.6, -9723.857, 1, 0, 0, 0.013, 4.8, 7607.084, -2, -19, 0, 0.012, 0.8, 23837.689, 1, 35, 0, 0.012, 3.6, 4.409, -4, -44, 0, 0.012, 5.0, 16657.031, 3, 50, 0, 0.012, 4.4, 16657.735, 3, 50, 0, 0.012, 1.1, 15578.803, -4, -38, 0, 0.012, 6.0, -11.490, 0, 0, 0, 0.012, 1.9, 8164.398, 0, 0, 0, 0.012, 2.4, 31852.372, -4, -17, 0, 0.012, 2.4, 6607.085, -2, -19, 0, 0.012, 4.2, 8359.870, 0, 0, 0, 0.012, 0.5, 5799.713, -2, -19, 0, 0.012, 2.7, 7220.622, 0, 0, 0, 0.012, 4.3, -139.720, 0, 0, 0, 0.012, 2.3, 13728.836, -2, -16, 0, 0.011, 3.6, 14912.146, 1, 31, 0, 0.011, 4.7, 14916.748, -2, -19, 0], [1.67680, 4.66926, 628.301955, -0.0266, 0.1, -0.005, 0.51642, 3.3721, 6585.760910, -2.158, -18.9, 0.09, 0.41383, 5.7277, 14914.452335, -0.635, 6.2, -0.04, 0.37115, 3.9695, 7700.389469, 1.550, 25.0, -0.12, 0.27560, 0.7416, 8956.993380, 1.496, 25.1, -0.13, 0.24599, 4.2253, -2.301200, 1.523, 25.1, -0.12, 0.07118, 0.1443, 7842.36482, -2.211, -19, 0.08, 0.06128, 2.4998, 16171.05625, -0.688, 6, 0, 0.04516, 0.443, 8399.67910, -0.36, 3, 0, 0.04048, 5.771, 14286.15038, -0.61, 6, 0, 0.03747, 4.626, 1256.60391, -0.05, 0, 0, 0.03707, 3.415, 5957.45895, -2.13, -19, 0.1, 0.03649, 1.800, 23243.14376, 0.89, 31, -0.2, 0.02438, 0.042, 16029.08089, 3.07, 50, -0.2, 0.02165, 1.017, -1742.93051, -3.68, -44, 0.2, 0.01923, 3.097, 17285.68480, 3.02, 50, -0.3, 0.01692, 1.280, 0.3286, 1.52, 25, -0.1, 0.01361, 0.298, 8326.3902, 3.05, 50, -0.2, 0.01293, 4.013, 7072.0875, 1.58, 25, -0.1, 0.01276, 4.413, 8330.9926, 0, 0, 0, 0.01270, 0.101, 8470.6668, -2.24, -19, 0.1, 0.01097, 1.203, 22128.5152, -2.82, -13, 0, 0.01088, 2.545, 15542.7543, -0.66, 6, 0, 0.00835, 0.190, 7214.0629, -2.18, -19, 0.1, 0.00734, 4.855, 24499.7477, 0.83, 31, -0.2, 0.00686, 5.130, 13799.8238, -4.34, -38, 0.2, 0.00631, 0.930, -486.3266, -3.73, -44, 0, 0.00585, 0.699, 9585.2953, 1.5, 25, 0, 0.00566, 4.073, 8328.3391, 1.5, 25, 0, 0.00566, 0.638, 8329.0437, 1.5, 25, 0, 0.00539, 2.472, -1952.4800, 0.6, 7, 0, 0.00509, 2.88, -0.7113, 0, 0, 0, 0.00469, 3.56, 30457.2066, -1.3, 12, 0, 0.00387, 0.78, -0.3523, 0, 0, 0, 0.00378, 1.84, 22614.8418, 0.9, 31, 0, 0.00362, 5.53, -695.8761, 0.6, 7, 0, 0.00317, 2.80, 16728.3705, 1.2, 28, 0, 0.00303, 6.07, 157.7344, 0, 0, 0, 0.00300, 2.53, 33.7570, -0.3, -4, 0, 0.00295, 4.16, 31571.8352, 2.4, 56, 0, 0.00289, 5.98, 7211.7617, -0.7, 6, 0, 0.00285, 2.06, 15540.4531, 0.9, 31, 0, 0.00283, 2.65, 2.6298, 0, 0, 0, 0.00282, 6.17, 15545.0555, -2.2, -19, 0, 0.00278, 1.23, -39.8149, 0, 0, 0, 0.00272, 3.82, 7216.3641, -3.7, -44, 0, 0.00270, 4.37, 70.9877, -1.9, -22, 0, 0.00256, 5.81, 13657.8484, -0.6, 6, 0, 0.00244, 5.64, -0.2237, 1.5, 25, 0, 0.00240, 2.96, 8311.7707, -2.2, -19, 0, 0.00239, 0.87, -33.7814, 0.3, 4, 0, 0.00216, 2.31, 15.9995, -2.2, -19, 0, 0.00186, 3.46, 5329.1570, -2.1, -19, 0, 0.00169, 2.40, 24357.772, 4.6, 75, 0, 0.00161, 5.80, 8329.403, 1.5, 25, 0, 0.00161, 5.20, 8327.980, 1.5, 25, 0, 0.00160, 4.26, 23385.119, -2.9, -13, 0, 0.00156, 1.26, 550.755, 0, 0, 0, 0.00155, 1.25, 21500.213, -2.8, -13, 0, 0.00152, 0.60, -16.921, -3.7, -44, 0, 0.00150, 2.71, -79.630, 0, 0, 0, 0.00150, 5.29, 15.542, 0, 0, 0, 0.00148, 1.06, -2371.232, -3.7, -44, 0, 0.00141, 0.77, 8328.691, 1.5, 25, 0, 0.00141, 3.67, 7143.075, -0.3, 0, 0, 0.00138, 5.45, 25614.376, 4.5, 75, 0, 0.00129, 4.90, 23871.446, 0.9, 31, 0, 0.00126, 4.03, 141.975, -3.8, -44, 0, 0.00124, 6.01, 522.369, 0, 0, 0, 0.00120, 4.94, -10071.622, -5.2, -69, 0, 0.00118, 5.07, -15.419, -2.2, -19, 0, 0.00107, 3.49, 23452.693, -3.4, -20, 0, 0.00104, 4.78, 17495.234, -1.3, 0, 0, 0.00103, 1.44, -18.049, -2.2, -19, 0, 0.00102, 5.63, 15542.402, -0.7, 6, 0, 0.00102, 2.59, 15543.107, -0.7, 6, 0, 0.00100, 4.11, -6.559, -1.9, -22, 0, 0.00097, 0.08, 15400.779, 3.1, 50, 0, 0.00096, 5.84, 31781.385, -1.9, 5, 0, 0.00094, 1.08, 8328.363, 0, 0, 0, 0.00094, 2.46, 16799.358, -0.7, 6, 0, 0.00094, 1.69, 6376.211, 2.2, 32, 0, 0.00093, 3.64, 8329.020, 3.0, 50, 0, 0.00093, 2.65, 16655.082, 4.6, 75, 0, 0.00090, 1.90, 15056.428, -4.4, -38, 0, 0.00089, 1.59, 52.969, 0, 0, 0, 0.00088, 2.02, -8257.704, -3.4, -47, 0, 0.00088, 3.02, 7213.711, -2.2, -19, 0, 0.00087, 0.50, 7214.415, -2.2, -19, 0, 0.00087, 0.49, 16659.684, 1.5, 25, 0, 0.00082, 5.64, -4.931, 1.5, 25, 0, 0.00079, 5.17, 13171.522, -4.3, -38, 0, 0.00076, 3.60, 29828.905, -1.3, 12, 0, 0.00076, 4.08, 24567.322, 0.3, 24, 0, 0.00076, 4.58, 1884.906, -0.1, 0, 0, 0.00073, 0.33, 31713.811, -1.4, 12, 0, 0.00073, 0.93, 32828.439, 2.4, 56, 0, 0.00071, 5.91, 38785.898, 0.2, 37, 0, 0.00069, 2.20, 15613.742, -2.5, -16, 0, 0.00066, 3.87, 15.732, -2.5, -23, 0, 0.00066, 0.86, 25823.926, 0.2, 24, 0, 0.00065, 2.52, 8170.957, 1.5, 25, 0, 0.00063, 0.18, 8322.132, -0.3, 0, 0, 0.00060, 5.84, 8326.062, 1.5, 25, 0, 0.00060, 5.15, 8331.321, 1.5, 25, 0, 0.00060, 2.18, 8486.426, 1.5, 25, 0, 0.00058, 2.30, -1.731, -4, -44, 0, 0.00058, 5.43, 14357.138, -2, -16, 0, 0.00057, 3.09, 8294.910, 2, 29, 0, 0.00057, 4.67, -8362.473, -1, -21, 0, 0.00056, 4.15, 16833.151, -1, 0, 0, 0.00054, 1.93, 7056.329, -2, -19, 0, 0.00054, 5.27, 8315.574, -2, -19, 0, 0.00052, 5.6, 8311.418, -2, -19, 0, 0.00052, 2.7, -77.552, 0, 0, 0, 0.00051, 4.3, 7230.984, 2, 25, 0, 0.00050, 0.4, -0.508, 0, 0, 0, 0.00049, 5.4, 7211.433, -2, -19, 0, 0.00049, 4.4, 7216.693, -2, -19, 0, 0.00049, 4.3, 16864.631, 0, 24, 0, 0.00049, 2.2, 16869.234, -3, -26, 0, 0.00047, 6.1, 627.596, 0, 0, 0, 0.00047, 5.0, 12.619, 1, 7, 0, 0.00045, 4.9, -8815.018, -5, -69, 0, 0.00044, 1.6, 62.133, -2, -19, 0, 0.00042, 2.9, -13.118, -4, -44, 0, 0.00042, 4.1, -119.445, 0, 0, 0, 0.00041, 4.3, 22756.817, -3, -13, 0, 0.00041, 3.6, 8288.877, 2, 25, 0, 0.00040, 0.5, 6663.308, -2, -19, 0, 0.00040, 1.1, 8368.506, 2, 25, 0, 0.00039, 4.1, 6443.786, 2, 25, 0, 0.00039, 3.1, 16657.383, 3, 50, 0, 0.00038, 0.1, 16657.031, 3, 50, 0, 0.00038, 3.0, 16657.735, 3, 50, 0, 0.00038, 4.6, 23942.433, -1, 9, 0, 0.00037, 4.3, 15385.020, -1, 6, 0, 0.00037, 5.0, 548.678, 0, 0, 0, 0.00036, 1.8, 7213.352, -2, -19, 0, 0.00036, 1.7, 7214.774, -2, -19, 0, 0.00035, 1.1, 7777.936, 2, 25, 0, 0.00035, 1.6, -8.860, 0, 0, 0, 0.00035, 4.4, 23869.145, 2, 56, 0, 0.00035, 2.0, 6691.693, -2, -19, 0, 0.00034, 1.3, -1185.616, -2, -22, 0, 0.00034, 2.2, 23873.747, -1, 6, 0, 0.00033, 2.0, -235.287, 0, 0, 0, 0.00033, 3.1, 17913.987, 3, 50, 0, 0.00033, 1.0, 8351.233, -2, -19, 0], [0.004870, 4.6693, 628.30196, -0.027, 0, -0.01, 0.002280, 2.6746, -2.30120, 1.523, 25, -0.12, 0.001500, 3.372, 6585.76091, -2.16, -19, 0.1, 0.001200, 5.728, 14914.45233, -0.64, 6, 0, 0.001080, 3.969, 7700.38947, 1.55, 25, -0.1, 0.000800, 0.742, 8956.99338, 1.50, 25, -0.1, 0.000254, 6.002, 0.3286, 1.52, 25, -0.1, 0.000210, 0.144, 7842.3648, -2.21, -19, 0, 0.000180, 2.500, 16171.0562, -0.7, 6, 0, 0.000130, 0.44, 8399.6791, -0.4, 3, 0, 0.000126, 5.03, 8326.3902, 3.0, 50, 0, 0.000120, 5.77, 14286.1504, -0.6, 6, 0, 0.000118, 5.96, 8330.9926, 0, 0, 0, 0.000110, 1.80, 23243.1438, 0.9, 31, 0, 0.000110, 3.42, 5957.4590, -2.1, -19, 0, 0.000110, 4.63, 1256.6039, -0.1, 0, 0, 0.000099, 4.70, -0.7113, 0, 0, 0, 0.000070, 0.04, 16029.0809, 3.1, 50, 0, 0.000070, 5.14, 8328.3391, 1.5, 25, 0, 0.000070, 5.85, 8329.0437, 1.5, 25, 0, 0.000060, 1.02, -1742.9305, -3.7, -44, 0, 0.000060, 3.10, 17285.6848, 3.0, 50, 0, 0.000054, 5.69, -0.352, 0, 0, 0, 0.000043, 0.52, 15.542, 0, 0, 0, 0.000041, 2.03, 2.630, 0, 0, 0, 0.000040, 0.10, 8470.667, -2.2, -19, 0, 0.000040, 4.01, 7072.088, 1.6, 25, 0, 0.000036, 2.93, -8.860, -0.3, 0, 0, 0.000030, 1.20, 22128.515, -2.8, -13, 0, 0.000030, 2.54, 15542.754, -0.7, 6, 0, 0.000027, 4.43, 7211.762, -0.7, 6, 0, 0.000026, 0.51, 15540.453, 0.9, 31, 0, 0.000026, 1.44, 15545.055, -2.2, -19, 0, 0.000025, 5.37, 7216.364, -3.7, -44, 0], [0.00001200, 1.041, -2.3012, 1.52, 25, -0.1, 0.00000170, 0.31, -0.711, 0, 0, 0]]];
  5045. var rad = 180 * 3600 / Math.PI;
  5046. var dt_at = new Array(-4000, 108371.7, -13036.80, 392.000, 0.0000, -500, 17201.0, -627.82, 16.170, -0.3413, -150, 12200.6, -346.41, 5.403, -0.1593, 150, 9113.8, -328.13, -1.647, 0.0377, 500, 5707.5, -391.41, 0.915, 0.3145, 900, 2203.4, -283.45, 13.034, -0.1778, 1300, 490.1, -57.35, 2.085, -0.0072, 1600, 120.0, -9.81, -1.532, 0.1403, 1700, 10.2, -0.91, 0.510, -0.0370, 1800, 13.4, -0.72, 0.202, -0.0193, 1830, 7.8, -1.81, 0.416, -0.0247, 1860, 8.3, -0.13, -0.406, 0.0292, 1880, -5.4, 0.32, -0.183, 0.0173, 1900, -2.3, 2.06, 0.169, -0.0135, 1920, 21.2, 1.69, -0.304, 0.0167, 1940, 24.2, 1.22, -0.064, 0.0031, 1960, 33.2, 0.51, 0.231, -0.0109, 1980, 51.0, 1.29, -0.026, 0.0032, 2000, 63.87, 0.1, 0, 0, 2005, 64.7, 0.4, 0, 0, 2015, 69);
  5047. var nutB = new Array(2.1824, -33.75705, 36e-6, -1720, 920, 3.5069, 1256.66393, 11e-6, -132, 57, 1.3375, 16799.4182, -51e-6, -23, 10, 4.3649, -67.5141, 72e-6, 21, -9, 0.04, -628.302, 0, -14, 0, 2.36, 8328.691, 0, 7, 0, 3.46, 1884.966, 0, -5, 2, 5.44, 16833.175, 0, -4, 2, 3.69, 25128.110, 0, -3, 0, 3.55, 628.362, 0, 2, 0);
  5048. function gxc_sunLon(t){
  5049. var v = -0.043126 + 628.301955 * t - 0.000002732 * t * t;
  5050. var e = 0.016708634 - 0.000042037 * t - 0.0000001267 * t * t;
  5051. return (-20.49552 * (1 + e * Math.cos(v))) / rad;
  5052. }
  5053. function gxc_moonLon(t){
  5054. return -3.4E-6;
  5055. }
  5056. function nutationLon2(t){
  5057. var i, a, t2 = t * t, dL = 0, B = nutB;
  5058. for (i = 0; i < B.length; i += 5) {
  5059. if (i == 0) a = -1.742 * t;
  5060. else a = 0;
  5061. dL += (B[i + 3] + a) * Math.sin(B[i] + B[i + 1] * t + B[i + 2] * t2);
  5062. }
  5063. return dL / 100 / rad;
  5064. }
  5065. function dt_T(t){
  5066. return dt_calc(t / 365.2425 + 2000) / 86400.0;
  5067. }
  5068. function dt_ext(y, jsd){
  5069. var dy = (y - 1820) / 100;
  5070. return -20 + jsd * dy * dy;
  5071. }
  5072. function dt_calc(y){
  5073. var y0 = dt_at[dt_at.length - 2];
  5074. var t0 = dt_at[dt_at.length - 1];
  5075. if (y >= y0) {
  5076. var jsd = 31;
  5077. if (y > y0 + 100) return dt_ext(y, jsd);
  5078. var v = dt_ext(y, jsd);
  5079. var dv = dt_ext(y0, jsd) - t0;
  5080. return v - dv * (y0 + 100 - y) / 100;
  5081. }
  5082. var i, d = dt_at;
  5083. for (i = 0; i < d.length; i += 5)
  5084. if (y < d[i + 5]) break;
  5085. var t1 = (y - d[i]) / (d[i + 5] - d[i]) * 10, t2 = t1 * t1, t3 = t2 * t1;
  5086. return d[i + 1] + d[i + 2] * t1 + d[i + 3] * t2 + d[i + 4] * t3;
  5087. }
  5088. function XL0_calc(xt, zn, t, n){
  5089. t /= 10;
  5090. var i, j, v = 0, tn = 1, c;
  5091. var F = XL0[xt], n1, n2, N;
  5092. var n0, pn = zn * 6 + 1, N0 = F[pn + 1] - F[pn];
  5093. for (i = 0; i < 6; i++, tn *= t) {
  5094. n1 = F[pn + i], n2 = F[pn + 1 + i], n0 = n2 - n1;
  5095. if (!n0) continue;
  5096. if (n < 0) N = n2;
  5097. else {
  5098. N = Math.floor(3 * n * n0 / N0 + 0.5) + n1;
  5099. if (i) N += 3;
  5100. if (N > n2) N = n2;
  5101. }
  5102. for (j = n1, c = 0; j < N; j += 3)
  5103. c += F[j] * Math.cos(F[j + 1] + t * F[j + 2]);
  5104. v += c * tn;
  5105. }
  5106. v /= F[0];
  5107. if (xt == 0) {
  5108. var t2 = t * t, t3 = t2 * t;
  5109. if (zn == 0) v += (-0.0728 - 2.7702 * t - 1.1019 * t2 - 0.0996 * t3) / rad;
  5110. if (zn == 1) v += (+0.0000 + 0.0004 * t + 0.0004 * t2 - 0.0026 * t3) / rad;
  5111. if (zn == 2) v += (-0.0020 + 0.0044 * t + 0.0213 * t2 - 0.0250 * t3) / 1000000;
  5112. } else {
  5113. var dv = XL0_xzb[(xt - 1) * 3 + zn];
  5114. if (zn == 0) v += -3 * t / rad;
  5115. if (zn == 2) v += dv / 1000000;
  5116. else v += dv / rad;
  5117. }
  5118. return v;
  5119. }
  5120. function XL1_calc(zn, t, n){
  5121. var ob = XL1[zn];
  5122. var i, j, F, N, v = 0, tn = 1, c;
  5123. var t2 = t * t, t3 = t2 * t, t4 = t3 * t, t5 = t4 * t, tx = t - 10;
  5124. if (zn == 0) {
  5125. v += (3.81034409 + 8399.684730072 * t - 3.319e-05 * t2 + 3.11e-08 * t3 - 2.033e-10 * t4) * rad;
  5126. v += 5028.792262 * t + 1.1124406 * t2 + 0.00007699 * t3 - 0.000023479 * t4 - 0.0000000178 * t5;
  5127. if (tx > 0) v += -0.866 + 1.43 * tx + 0.054 * tx * tx;
  5128. }
  5129. t2 /= 1e4, t3 /= 1e8, t4 /= 1e8;
  5130. n *= 6;
  5131. if (n < 0) n = ob[0].length;
  5132. for (i = 0; i < ob.length; i++, tn *= t) {
  5133. F = ob[i];
  5134. N = Math.floor(n * F.length / ob[0].length + 0.5);
  5135. if (i) N += 6;
  5136. if (N >= F.length) N = F.length;
  5137. for (j = 0, c = 0; j < N; j += 6)
  5138. c += F[j] * Math.cos(F[j + 1] + t * F[j + 2] + t2 * F[j + 3] + t3 * F[j + 4] + t4 * F[j + 5]);
  5139. v += c * tn;
  5140. }
  5141. if (zn != 2) v /= rad;
  5142. return v;
  5143. }
  5144. var XL = {
  5145. E_Lon: function(t, n){
  5146. return XL0_calc(0, 0, t, n);
  5147. },
  5148. M_Lon: function(t, n){
  5149. return XL1_calc(0, t, n);
  5150. },
  5151. E_v: function(t){
  5152. var f = 628.307585 * t;
  5153. return 628.332 + 21 * Math.sin(1.527 + f) + 0.44 * Math.sin(1.48 + f * 2) + 0.129 * Math.sin(5.82 + f) * t + 0.00055 * Math.sin(4.21 + f) * t * t;
  5154. },
  5155. M_v: function(t){
  5156. var v = 8399.71 - 914 * Math.sin(0.7848 + 8328.691425 * t + 0.0001523 * t * t);
  5157. v -= 179 * Math.sin(2.543 + 15542.7543 * t) + 160 * Math.sin(0.1874 + 7214.0629 * t) + 62 * Math.sin(3.14 + 16657.3828 * t) + 34 * Math.sin(4.827 + 16866.9323 * t) + 22 * Math.sin(4.9 + 23871.4457 * t) + 12 * Math.sin(2.59 + 14914.4523 * t) + 7 * Math.sin(0.23 + 6585.7609 * t) + 5 * Math.sin(0.9 + 25195.624 * t) + 5 * Math.sin(2.32 - 7700.3895 * t) + 5 * Math.sin(3.88 + 8956.9934 * t) + 5 * Math.sin(0.49 + 7771.3771 * t);
  5158. return v;
  5159. },
  5160. MS_aLon: function(t, Mn, Sn){
  5161. return this.M_Lon(t, Mn) + gxc_moonLon(t) - (this.E_Lon(t, Sn) + gxc_sunLon(t) + Math.PI);
  5162. },
  5163. S_aLon: function(t, n){
  5164. return this.E_Lon(t, n) + nutationLon2(t) + gxc_sunLon(t) + Math.PI;
  5165. },
  5166. MS_aLon_t: function(W){
  5167. var t, v = 7771.37714500204;
  5168. t = (W + 1.08472) / v;
  5169. t += (W - this.MS_aLon(t, 3, 3)) / v;
  5170. v = this.M_v(t) - this.E_v(t);
  5171. t += (W - this.MS_aLon(t, 20, 10)) / v;
  5172. t += (W - this.MS_aLon(t, -1, 60)) / v;
  5173. return t;
  5174. },
  5175. S_aLon_t: function(W){
  5176. var t, v = 628.3319653318;
  5177. t = (W - 1.75347 - Math.PI) / v;
  5178. v = this.E_v(t);
  5179. t += (W - this.S_aLon(t, 10)) / v;
  5180. v = this.E_v(t);
  5181. t += (W - this.S_aLon(t, -1)) / v;
  5182. return t;
  5183. },
  5184. MS_aLon_t2: function(W){
  5185. var t, v = 7771.37714500204;
  5186. t = (W + 1.08472) / v;
  5187. var L, t2 = t * t;
  5188. t -= (-0.00003309 * t2 + 0.10976 * Math.cos(0.784758 + 8328.6914246 * t + 0.000152292 * t2) + 0.02224 * Math.cos(0.18740 + 7214.0628654 * t - 0.00021848 * t2) - 0.03342 * Math.cos(4.669257 + 628.307585 * t)) / v;
  5189. L = this.M_Lon(t, 20) - (4.8950632 + 628.3319653318 * t + 0.000005297 * t * t + 0.0334166 * Math.cos(4.669257 + 628.307585 * t) + 0.0002061 * Math.cos(2.67823 + 628.307585 * t) * t + 0.000349 * Math.cos(4.6261 + 1256.61517 * t) - 20.5 / rad);
  5190. v = 7771.38 - 914 * Math.sin(0.7848 + 8328.691425 * t + 0.0001523 * t * t) - 179 * Math.sin(2.543 + 15542.7543 * t) - 160 * Math.sin(0.1874 + 7214.0629 * t);
  5191. t += (W - L) / v;
  5192. return t;
  5193. },
  5194. S_aLon_t2: function(W){
  5195. var t, L, v = 628.3319653318;
  5196. t = (W - 1.75347 - Math.PI) / v;
  5197. t -= (0.000005297 * t * t + 0.0334166 * Math.cos(4.669257 + 628.307585 * t) + 0.0002061 * Math.cos(2.67823 + 628.307585 * t) * t) / v;
  5198. t += (W - this.E_Lon(t, 8) - Math.PI + (20.5 + 17.2 * Math.sin(2.1824 - 33.75705 * t)) / rad) / v;
  5199. return t;
  5200. }
  5201. };
  5202. function unzip(str){
  5203. var z10 = "0000000000", z20 = z10 + z10;
  5204. var map={J:'00',I:'000',H:'0000',G:'00000',t:'01',s:'001',r:'0001',q:'00001',p:'000001',o:'0000001',n:'00000001',m:'000000001',l:'0000000001',k:'03',j:'0303',i:'003',h:'003003',g:'0003',f:'00003',e:'000003',d:'0000003',c:'00000003',b:'000000003',a:'0000000003',A:z20+z20+z20,B:z20+z20+z10,C:z20+z20,D:z20+z10,E:z20,F:z10};
  5205. return str.replace(/\D/g, function(c){
  5206. return map[c];
  5207. });
  5208. }
  5209. window.Lunar = Lunar;
  5210. window.lunar = lunar;
  5211. })();
  5212. (function(){
  5213. /**
  5214. * 节日的分类:
  5215. * t:中国传统节日
  5216. * i:世界性节日
  5217. * h:中国节假日
  5218. * c:基督教节日
  5219. * a:北美节日
  5220. */
  5221. var FESTIVAL = {
  5222. //t:中国传统节日
  5223. '正月初一': 't,春节 ',
  5224. '正月十五': 't,元宵节',
  5225. '二月初二': 't,龙头节(龙抬头)',
  5226. '五月初五': 't,端午节',
  5227. '七月初七': 't,乞巧节(七夕)',
  5228. '七月十五': 't,中元节(鬼节)',
  5229. '八月十五': 't,中秋节',
  5230. '九月初九': 't,重阳节',
  5231. '十月初一': 't,寒衣节',
  5232. '十月十五': 't,下元节',
  5233. '腊月初八': 't,腊八节',
  5234. '腊月廿三': 't,祭灶节',
  5235. //'十二月卅日(或廿九) 除夕
  5236. //i:世界性节日
  5237. '0202': 'i,世界湿地日,1996',
  5238. '0308': 'i,国际妇女节,1975',
  5239. '0315': 'i,国际消费者权益日,1983',
  5240. '0422': 'i,世界地球日,1990',
  5241. '0501': 'i,国际劳动节,1889',
  5242. '0512': 'i,国际护士节,1912',
  5243. '0518': 'i,国际博物馆日,1977',
  5244. '0605': 'i,世界环境日,1972',
  5245. '0623': 'i,国际奥林匹克日,1948',
  5246. '0624': 'i,世界骨质疏松日,1997',
  5247. '1117': 'i,世界学生日,1942',
  5248. '1201': 'i,世界爱滋病日,1988',
  5249. //h:中国节假日
  5250. '0101': 'h,元旦',
  5251. '0312': 'h,植树节,1979',
  5252. '0504': 'h,五四青年节,1939',
  5253. '0601': 'h,儿童节,1950',
  5254. '0701': 'h,建党节,1941',
  5255. '0801': 'h,建军节,1933',
  5256. '0910': 'h,教师节,1985',
  5257. '1001': 'h,国庆节,1949',
  5258. //c:基督教节日
  5259. '1224': 'c,平安夜',
  5260. '1225': 'c,圣诞节',
  5261. //a:北美节日
  5262. '0214': 'a,西洋情人节',
  5263. w: {
  5264. '0520': 'i,母亲节,1913',
  5265. '0630': 'a,父亲节',
  5266. '1144': 'a,感恩节(美国)',
  5267. '1021': 'a,感恩节(加拿大)'
  5268. }
  5269. };
  5270. Lunar.prototype.festival = function(){
  5271. var y = this.oDate.getFullYear(), m = this.oDate.getMonth() + 1, d = this.oDate.getDate(), skey = align(m) + align(d);
  5272. var day = this.oDate.getDay(), nWeek = Math.ceil(d / 7), wkey = align(m) + nWeek + day;
  5273. var M = this.lMonth, D = this.lDate, lkey = M + '月' + D;
  5274. var arRslt = [];
  5275. if (this.lNextMonth == '正' && D == (this.isBigMonth ? '三十' : '廿九')) {
  5276. arRslt.push('t,除夕');
  5277. }
  5278. if (FESTIVAL.w[wkey]) {
  5279. arRslt = arRslt.concat(FESTIVAL.w[wkey].split('|'));
  5280. }
  5281. if (FESTIVAL[skey]) {
  5282. arRslt = arRslt.concat(FESTIVAL[skey].split('|'));
  5283. }
  5284. if (FESTIVAL[lkey]) {
  5285. arRslt = arRslt.concat(FESTIVAL[lkey].split('|'));
  5286. }
  5287. for (var t, i = arRslt.length - 1; i > -1; i--) {
  5288. t = arRslt[i].split(',');
  5289. if (t[2] && y < t[2] - 0) {
  5290. arRslt.splice(i, 1);
  5291. continue;
  5292. }
  5293. arRslt[i] = {
  5294. type: t[0],
  5295. desc: t[1]
  5296. }
  5297. }
  5298. return arRslt.sort(function(a, b){
  5299. return b.type.charCodeAt(0) - a.type.charCodeAt(0);
  5300. });
  5301. };
  5302. function align(n){
  5303. return (n < 10 ? '0' : '') + n;
  5304. }
  5305. })();