jquery-layout.js 291 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174
  1. /*! jQuery UI - v1.10.3 - 2013-10-24
  2. * http://jqueryui.com
  3. * Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js
  4. * Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */
  5. (function( $, undefined ) {
  6. var uuid = 0,
  7. runiqueId = /^ui-id-\d+$/;
  8. // $.ui might exist from components with no dependencies, e.g., $.ui.position
  9. $.ui = $.ui || {};
  10. $.extend( $.ui, {
  11. version: "1.10.3",
  12. keyCode: {
  13. BACKSPACE: 8,
  14. COMMA: 188,
  15. DELETE: 46,
  16. DOWN: 40,
  17. END: 35,
  18. ENTER: 13,
  19. ESCAPE: 27,
  20. HOME: 36,
  21. LEFT: 37,
  22. NUMPAD_ADD: 107,
  23. NUMPAD_DECIMAL: 110,
  24. NUMPAD_DIVIDE: 111,
  25. NUMPAD_ENTER: 108,
  26. NUMPAD_MULTIPLY: 106,
  27. NUMPAD_SUBTRACT: 109,
  28. PAGE_DOWN: 34,
  29. PAGE_UP: 33,
  30. PERIOD: 190,
  31. RIGHT: 39,
  32. SPACE: 32,
  33. TAB: 9,
  34. UP: 38
  35. }
  36. });
  37. // plugins
  38. $.fn.extend({
  39. focus: (function( orig ) {
  40. return function( delay, fn ) {
  41. return typeof delay === "number" ?
  42. this.each(function() {
  43. var elem = this;
  44. setTimeout(function() {
  45. $( elem ).focus();
  46. if ( fn ) {
  47. fn.call( elem );
  48. }
  49. }, delay );
  50. }) :
  51. orig.apply( this, arguments );
  52. };
  53. })( $.fn.focus ),
  54. scrollParent: function() {
  55. var scrollParent;
  56. if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) {
  57. scrollParent = this.parents().filter(function() {
  58. return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
  59. }).eq(0);
  60. } else {
  61. scrollParent = this.parents().filter(function() {
  62. return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
  63. }).eq(0);
  64. }
  65. return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent;
  66. },
  67. zIndex: function( zIndex ) {
  68. if ( zIndex !== undefined ) {
  69. return this.css( "zIndex", zIndex );
  70. }
  71. if ( this.length ) {
  72. var elem = $( this[ 0 ] ), position, value;
  73. while ( elem.length && elem[ 0 ] !== document ) {
  74. // Ignore z-index if position is set to a value where z-index is ignored by the browser
  75. // This makes behavior of this function consistent across browsers
  76. // WebKit always returns auto if the element is positioned
  77. position = elem.css( "position" );
  78. if ( position === "absolute" || position === "relative" || position === "fixed" ) {
  79. // IE returns 0 when zIndex is not specified
  80. // other browsers return a string
  81. // we ignore the case of nested elements with an explicit value of 0
  82. // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
  83. value = parseInt( elem.css( "zIndex" ), 10 );
  84. if ( !isNaN( value ) && value !== 0 ) {
  85. return value;
  86. }
  87. }
  88. elem = elem.parent();
  89. }
  90. }
  91. return 0;
  92. },
  93. uniqueId: function() {
  94. return this.each(function() {
  95. if ( !this.id ) {
  96. this.id = "ui-id-" + (++uuid);
  97. }
  98. });
  99. },
  100. removeUniqueId: function() {
  101. return this.each(function() {
  102. if ( runiqueId.test( this.id ) ) {
  103. $( this ).removeAttr( "id" );
  104. }
  105. });
  106. }
  107. });
  108. // selectors
  109. function focusable( element, isTabIndexNotNaN ) {
  110. var map, mapName, img,
  111. nodeName = element.nodeName.toLowerCase();
  112. if ( "area" === nodeName ) {
  113. map = element.parentNode;
  114. mapName = map.name;
  115. if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
  116. return false;
  117. }
  118. img = $( "img[usemap=#" + mapName + "]" )[0];
  119. return !!img && visible( img );
  120. }
  121. return ( /input|select|textarea|button|object/.test( nodeName ) ?
  122. !element.disabled :
  123. "a" === nodeName ?
  124. element.href || isTabIndexNotNaN :
  125. isTabIndexNotNaN) &&
  126. // the element and all of its ancestors must be visible
  127. visible( element );
  128. }
  129. function visible( element ) {
  130. return $.expr.filters.visible( element ) &&
  131. !$( element ).parents().addBack().filter(function() {
  132. return $.css( this, "visibility" ) === "hidden";
  133. }).length;
  134. }
  135. $.extend( $.expr[ ":" ], {
  136. data: $.expr.createPseudo ?
  137. $.expr.createPseudo(function( dataName ) {
  138. return function( elem ) {
  139. return !!$.data( elem, dataName );
  140. };
  141. }) :
  142. // support: jQuery <1.8
  143. function( elem, i, match ) {
  144. return !!$.data( elem, match[ 3 ] );
  145. },
  146. focusable: function( element ) {
  147. return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
  148. },
  149. tabbable: function( element ) {
  150. var tabIndex = $.attr( element, "tabindex" ),
  151. isTabIndexNaN = isNaN( tabIndex );
  152. return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
  153. }
  154. });
  155. // support: jQuery <1.8
  156. if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
  157. $.each( [ "Width", "Height" ], function( i, name ) {
  158. var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
  159. type = name.toLowerCase(),
  160. orig = {
  161. innerWidth: $.fn.innerWidth,
  162. innerHeight: $.fn.innerHeight,
  163. outerWidth: $.fn.outerWidth,
  164. outerHeight: $.fn.outerHeight
  165. };
  166. function reduce( elem, size, border, margin ) {
  167. $.each( side, function() {
  168. size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
  169. if ( border ) {
  170. size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
  171. }
  172. if ( margin ) {
  173. size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
  174. }
  175. });
  176. return size;
  177. }
  178. $.fn[ "inner" + name ] = function( size ) {
  179. if ( size === undefined ) {
  180. return orig[ "inner" + name ].call( this );
  181. }
  182. return this.each(function() {
  183. $( this ).css( type, reduce( this, size ) + "px" );
  184. });
  185. };
  186. $.fn[ "outer" + name] = function( size, margin ) {
  187. if ( typeof size !== "number" ) {
  188. return orig[ "outer" + name ].call( this, size );
  189. }
  190. return this.each(function() {
  191. $( this).css( type, reduce( this, size, true, margin ) + "px" );
  192. });
  193. };
  194. });
  195. }
  196. // support: jQuery <1.8
  197. if ( !$.fn.addBack ) {
  198. $.fn.addBack = function( selector ) {
  199. return this.add( selector == null ?
  200. this.prevObject : this.prevObject.filter( selector )
  201. );
  202. };
  203. }
  204. // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
  205. if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
  206. $.fn.removeData = (function( removeData ) {
  207. return function( key ) {
  208. if ( arguments.length ) {
  209. return removeData.call( this, $.camelCase( key ) );
  210. } else {
  211. return removeData.call( this );
  212. }
  213. };
  214. })( $.fn.removeData );
  215. }
  216. // deprecated
  217. $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
  218. $.support.selectstart = "onselectstart" in document.createElement( "div" );
  219. $.fn.extend({
  220. disableSelection: function() {
  221. return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
  222. ".ui-disableSelection", function( event ) {
  223. event.preventDefault();
  224. });
  225. },
  226. enableSelection: function() {
  227. return this.unbind( ".ui-disableSelection" );
  228. }
  229. });
  230. $.extend( $.ui, {
  231. // $.ui.plugin is deprecated. Use $.widget() extensions instead.
  232. plugin: {
  233. add: function( module, option, set ) {
  234. var i,
  235. proto = $.ui[ module ].prototype;
  236. for ( i in set ) {
  237. proto.plugins[ i ] = proto.plugins[ i ] || [];
  238. proto.plugins[ i ].push( [ option, set[ i ] ] );
  239. }
  240. },
  241. call: function( instance, name, args ) {
  242. var i,
  243. set = instance.plugins[ name ];
  244. if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
  245. return;
  246. }
  247. for ( i = 0; i < set.length; i++ ) {
  248. if ( instance.options[ set[ i ][ 0 ] ] ) {
  249. set[ i ][ 1 ].apply( instance.element, args );
  250. }
  251. }
  252. }
  253. },
  254. // only used by resizable
  255. hasScroll: function( el, a ) {
  256. //If overflow is hidden, the element might have extra content, but the user wants to hide it
  257. if ( $( el ).css( "overflow" ) === "hidden") {
  258. return false;
  259. }
  260. var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
  261. has = false;
  262. if ( el[ scroll ] > 0 ) {
  263. return true;
  264. }
  265. // TODO: determine which cases actually cause this to happen
  266. // if the element doesn't have the scroll set, see if it's possible to
  267. // set the scroll
  268. el[ scroll ] = 1;
  269. has = ( el[ scroll ] > 0 );
  270. el[ scroll ] = 0;
  271. return has;
  272. }
  273. });
  274. })( jQuery );
  275. (function( $, undefined ) {
  276. var uuid = 0,
  277. slice = Array.prototype.slice,
  278. _cleanData = $.cleanData;
  279. $.cleanData = function( elems ) {
  280. for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
  281. try {
  282. $( elem ).triggerHandler( "remove" );
  283. // http://bugs.jquery.com/ticket/8235
  284. } catch( e ) {}
  285. }
  286. _cleanData( elems );
  287. };
  288. $.widget = function( name, base, prototype ) {
  289. var fullName, existingConstructor, constructor, basePrototype,
  290. // proxiedPrototype allows the provided prototype to remain unmodified
  291. // so that it can be used as a mixin for multiple widgets (#8876)
  292. proxiedPrototype = {},
  293. namespace = name.split( "." )[ 0 ];
  294. name = name.split( "." )[ 1 ];
  295. fullName = namespace + "-" + name;
  296. if ( !prototype ) {
  297. prototype = base;
  298. base = $.Widget;
  299. }
  300. // create selector for plugin
  301. $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
  302. return !!$.data( elem, fullName );
  303. };
  304. $[ namespace ] = $[ namespace ] || {};
  305. existingConstructor = $[ namespace ][ name ];
  306. constructor = $[ namespace ][ name ] = function( options, element ) {
  307. // allow instantiation without "new" keyword
  308. if ( !this._createWidget ) {
  309. return new constructor( options, element );
  310. }
  311. // allow instantiation without initializing for simple inheritance
  312. // must use "new" keyword (the code above always passes args)
  313. if ( arguments.length ) {
  314. this._createWidget( options, element );
  315. }
  316. };
  317. // extend with the existing constructor to carry over any static properties
  318. $.extend( constructor, existingConstructor, {
  319. version: prototype.version,
  320. // copy the object used to create the prototype in case we need to
  321. // redefine the widget later
  322. _proto: $.extend( {}, prototype ),
  323. // track widgets that inherit from this widget in case this widget is
  324. // redefined after a widget inherits from it
  325. _childConstructors: []
  326. });
  327. basePrototype = new base();
  328. // we need to make the options hash a property directly on the new instance
  329. // otherwise we'll modify the options hash on the prototype that we're
  330. // inheriting from
  331. basePrototype.options = $.widget.extend( {}, basePrototype.options );
  332. $.each( prototype, function( prop, value ) {
  333. if ( !$.isFunction( value ) ) {
  334. proxiedPrototype[ prop ] = value;
  335. return;
  336. }
  337. proxiedPrototype[ prop ] = (function() {
  338. var _super = function() {
  339. return base.prototype[ prop ].apply( this, arguments );
  340. },
  341. _superApply = function( args ) {
  342. return base.prototype[ prop ].apply( this, args );
  343. };
  344. return function() {
  345. var __super = this._super,
  346. __superApply = this._superApply,
  347. returnValue;
  348. this._super = _super;
  349. this._superApply = _superApply;
  350. returnValue = value.apply( this, arguments );
  351. this._super = __super;
  352. this._superApply = __superApply;
  353. return returnValue;
  354. };
  355. })();
  356. });
  357. constructor.prototype = $.widget.extend( basePrototype, {
  358. // TODO: remove support for widgetEventPrefix
  359. // always use the name + a colon as the prefix, e.g., draggable:start
  360. // don't prefix for widgets that aren't DOM-based
  361. widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
  362. }, proxiedPrototype, {
  363. constructor: constructor,
  364. namespace: namespace,
  365. widgetName: name,
  366. widgetFullName: fullName
  367. });
  368. // If this widget is being redefined then we need to find all widgets that
  369. // are inheriting from it and redefine all of them so that they inherit from
  370. // the new version of this widget. We're essentially trying to replace one
  371. // level in the prototype chain.
  372. if ( existingConstructor ) {
  373. $.each( existingConstructor._childConstructors, function( i, child ) {
  374. var childPrototype = child.prototype;
  375. // redefine the child widget using the same prototype that was
  376. // originally used, but inherit from the new version of the base
  377. $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
  378. });
  379. // remove the list of existing child constructors from the old constructor
  380. // so the old child constructors can be garbage collected
  381. delete existingConstructor._childConstructors;
  382. } else {
  383. base._childConstructors.push( constructor );
  384. }
  385. $.widget.bridge( name, constructor );
  386. };
  387. $.widget.extend = function( target ) {
  388. var input = slice.call( arguments, 1 ),
  389. inputIndex = 0,
  390. inputLength = input.length,
  391. key,
  392. value;
  393. for ( ; inputIndex < inputLength; inputIndex++ ) {
  394. for ( key in input[ inputIndex ] ) {
  395. value = input[ inputIndex ][ key ];
  396. if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
  397. // Clone objects
  398. if ( $.isPlainObject( value ) ) {
  399. target[ key ] = $.isPlainObject( target[ key ] ) ?
  400. $.widget.extend( {}, target[ key ], value ) :
  401. // Don't extend strings, arrays, etc. with objects
  402. $.widget.extend( {}, value );
  403. // Copy everything else by reference
  404. } else {
  405. target[ key ] = value;
  406. }
  407. }
  408. }
  409. }
  410. return target;
  411. };
  412. $.widget.bridge = function( name, object ) {
  413. var fullName = object.prototype.widgetFullName || name;
  414. $.fn[ name ] = function( options ) {
  415. var isMethodCall = typeof options === "string",
  416. args = slice.call( arguments, 1 ),
  417. returnValue = this;
  418. // allow multiple hashes to be passed on init
  419. options = !isMethodCall && args.length ?
  420. $.widget.extend.apply( null, [ options ].concat(args) ) :
  421. options;
  422. if ( isMethodCall ) {
  423. this.each(function() {
  424. var methodValue,
  425. instance = $.data( this, fullName );
  426. if ( !instance ) {
  427. return $.error( "cannot call methods on " + name + " prior to initialization; " +
  428. "attempted to call method '" + options + "'" );
  429. }
  430. if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
  431. return $.error( "no such method '" + options + "' for " + name + " widget instance" );
  432. }
  433. methodValue = instance[ options ].apply( instance, args );
  434. if ( methodValue !== instance && methodValue !== undefined ) {
  435. returnValue = methodValue && methodValue.jquery ?
  436. returnValue.pushStack( methodValue.get() ) :
  437. methodValue;
  438. return false;
  439. }
  440. });
  441. } else {
  442. this.each(function() {
  443. var instance = $.data( this, fullName );
  444. if ( instance ) {
  445. instance.option( options || {} )._init();
  446. } else {
  447. $.data( this, fullName, new object( options, this ) );
  448. }
  449. });
  450. }
  451. return returnValue;
  452. };
  453. };
  454. $.Widget = function( /* options, element */ ) {};
  455. $.Widget._childConstructors = [];
  456. $.Widget.prototype = {
  457. widgetName: "widget",
  458. widgetEventPrefix: "",
  459. defaultElement: "<div>",
  460. options: {
  461. disabled: false,
  462. // callbacks
  463. create: null
  464. },
  465. _createWidget: function( options, element ) {
  466. element = $( element || this.defaultElement || this )[ 0 ];
  467. this.element = $( element );
  468. this.uuid = uuid++;
  469. this.eventNamespace = "." + this.widgetName + this.uuid;
  470. this.options = $.widget.extend( {},
  471. this.options,
  472. this._getCreateOptions(),
  473. options );
  474. this.bindings = $();
  475. this.hoverable = $();
  476. this.focusable = $();
  477. if ( element !== this ) {
  478. $.data( element, this.widgetFullName, this );
  479. this._on( true, this.element, {
  480. remove: function( event ) {
  481. if ( event.target === element ) {
  482. this.destroy();
  483. }
  484. }
  485. });
  486. this.document = $( element.style ?
  487. // element within the document
  488. element.ownerDocument :
  489. // element is window or document
  490. element.document || element );
  491. this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
  492. }
  493. this._create();
  494. this._trigger( "create", null, this._getCreateEventData() );
  495. this._init();
  496. },
  497. _getCreateOptions: $.noop,
  498. _getCreateEventData: $.noop,
  499. _create: $.noop,
  500. _init: $.noop,
  501. destroy: function() {
  502. this._destroy();
  503. // we can probably remove the unbind calls in 2.0
  504. // all event bindings should go through this._on()
  505. this.element
  506. .unbind( this.eventNamespace )
  507. // 1.9 BC for #7810
  508. // TODO remove dual storage
  509. .removeData( this.widgetName )
  510. .removeData( this.widgetFullName )
  511. // support: jquery <1.6.3
  512. // http://bugs.jquery.com/ticket/9413
  513. .removeData( $.camelCase( this.widgetFullName ) );
  514. this.widget()
  515. .unbind( this.eventNamespace )
  516. .removeAttr( "aria-disabled" )
  517. .removeClass(
  518. this.widgetFullName + "-disabled " +
  519. "ui-state-disabled" );
  520. // clean up events and states
  521. this.bindings.unbind( this.eventNamespace );
  522. this.hoverable.removeClass( "ui-state-hover" );
  523. this.focusable.removeClass( "ui-state-focus" );
  524. },
  525. _destroy: $.noop,
  526. widget: function() {
  527. return this.element;
  528. },
  529. option: function( key, value ) {
  530. var options = key,
  531. parts,
  532. curOption,
  533. i;
  534. if ( arguments.length === 0 ) {
  535. // don't return a reference to the internal hash
  536. return $.widget.extend( {}, this.options );
  537. }
  538. if ( typeof key === "string" ) {
  539. // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
  540. options = {};
  541. parts = key.split( "." );
  542. key = parts.shift();
  543. if ( parts.length ) {
  544. curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
  545. for ( i = 0; i < parts.length - 1; i++ ) {
  546. curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
  547. curOption = curOption[ parts[ i ] ];
  548. }
  549. key = parts.pop();
  550. if ( value === undefined ) {
  551. return curOption[ key ] === undefined ? null : curOption[ key ];
  552. }
  553. curOption[ key ] = value;
  554. } else {
  555. if ( value === undefined ) {
  556. return this.options[ key ] === undefined ? null : this.options[ key ];
  557. }
  558. options[ key ] = value;
  559. }
  560. }
  561. this._setOptions( options );
  562. return this;
  563. },
  564. _setOptions: function( options ) {
  565. var key;
  566. for ( key in options ) {
  567. this._setOption( key, options[ key ] );
  568. }
  569. return this;
  570. },
  571. _setOption: function( key, value ) {
  572. this.options[ key ] = value;
  573. if ( key === "disabled" ) {
  574. this.widget()
  575. .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
  576. .attr( "aria-disabled", value );
  577. this.hoverable.removeClass( "ui-state-hover" );
  578. this.focusable.removeClass( "ui-state-focus" );
  579. }
  580. return this;
  581. },
  582. enable: function() {
  583. return this._setOption( "disabled", false );
  584. },
  585. disable: function() {
  586. return this._setOption( "disabled", true );
  587. },
  588. _on: function( suppressDisabledCheck, element, handlers ) {
  589. var delegateElement,
  590. instance = this;
  591. // no suppressDisabledCheck flag, shuffle arguments
  592. if ( typeof suppressDisabledCheck !== "boolean" ) {
  593. handlers = element;
  594. element = suppressDisabledCheck;
  595. suppressDisabledCheck = false;
  596. }
  597. // no element argument, shuffle and use this.element
  598. if ( !handlers ) {
  599. handlers = element;
  600. element = this.element;
  601. delegateElement = this.widget();
  602. } else {
  603. // accept selectors, DOM elements
  604. element = delegateElement = $( element );
  605. this.bindings = this.bindings.add( element );
  606. }
  607. $.each( handlers, function( event, handler ) {
  608. function handlerProxy() {
  609. // allow widgets to customize the disabled handling
  610. // - disabled as an array instead of boolean
  611. // - disabled class as method for disabling individual parts
  612. if ( !suppressDisabledCheck &&
  613. ( instance.options.disabled === true ||
  614. $( this ).hasClass( "ui-state-disabled" ) ) ) {
  615. return;
  616. }
  617. return ( typeof handler === "string" ? instance[ handler ] : handler )
  618. .apply( instance, arguments );
  619. }
  620. // copy the guid so direct unbinding works
  621. if ( typeof handler !== "string" ) {
  622. handlerProxy.guid = handler.guid =
  623. handler.guid || handlerProxy.guid || $.guid++;
  624. }
  625. var match = event.match( /^(\w+)\s*(.*)$/ ),
  626. eventName = match[1] + instance.eventNamespace,
  627. selector = match[2];
  628. if ( selector ) {
  629. delegateElement.delegate( selector, eventName, handlerProxy );
  630. } else {
  631. element.bind( eventName, handlerProxy );
  632. }
  633. });
  634. },
  635. _off: function( element, eventName ) {
  636. eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
  637. element.unbind( eventName ).undelegate( eventName );
  638. },
  639. _delay: function( handler, delay ) {
  640. function handlerProxy() {
  641. return ( typeof handler === "string" ? instance[ handler ] : handler )
  642. .apply( instance, arguments );
  643. }
  644. var instance = this;
  645. return setTimeout( handlerProxy, delay || 0 );
  646. },
  647. _hoverable: function( element ) {
  648. this.hoverable = this.hoverable.add( element );
  649. this._on( element, {
  650. mouseenter: function( event ) {
  651. $( event.currentTarget ).addClass( "ui-state-hover" );
  652. },
  653. mouseleave: function( event ) {
  654. $( event.currentTarget ).removeClass( "ui-state-hover" );
  655. }
  656. });
  657. },
  658. _focusable: function( element ) {
  659. this.focusable = this.focusable.add( element );
  660. this._on( element, {
  661. focusin: function( event ) {
  662. $( event.currentTarget ).addClass( "ui-state-focus" );
  663. },
  664. focusout: function( event ) {
  665. $( event.currentTarget ).removeClass( "ui-state-focus" );
  666. }
  667. });
  668. },
  669. _trigger: function( type, event, data ) {
  670. var prop, orig,
  671. callback = this.options[ type ];
  672. data = data || {};
  673. event = $.Event( event );
  674. event.type = ( type === this.widgetEventPrefix ?
  675. type :
  676. this.widgetEventPrefix + type ).toLowerCase();
  677. // the original event may come from any element
  678. // so we need to reset the target on the new event
  679. event.target = this.element[ 0 ];
  680. // copy original event properties over to the new event
  681. orig = event.originalEvent;
  682. if ( orig ) {
  683. for ( prop in orig ) {
  684. if ( !( prop in event ) ) {
  685. event[ prop ] = orig[ prop ];
  686. }
  687. }
  688. }
  689. this.element.trigger( event, data );
  690. return !( $.isFunction( callback ) &&
  691. callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
  692. event.isDefaultPrevented() );
  693. }
  694. };
  695. $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
  696. $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
  697. if ( typeof options === "string" ) {
  698. options = { effect: options };
  699. }
  700. var hasOptions,
  701. effectName = !options ?
  702. method :
  703. options === true || typeof options === "number" ?
  704. defaultEffect :
  705. options.effect || defaultEffect;
  706. options = options || {};
  707. if ( typeof options === "number" ) {
  708. options = { duration: options };
  709. }
  710. hasOptions = !$.isEmptyObject( options );
  711. options.complete = callback;
  712. if ( options.delay ) {
  713. element.delay( options.delay );
  714. }
  715. if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
  716. element[ method ]( options );
  717. } else if ( effectName !== method && element[ effectName ] ) {
  718. element[ effectName ]( options.duration, options.easing, callback );
  719. } else {
  720. element.queue(function( next ) {
  721. $( this )[ method ]();
  722. if ( callback ) {
  723. callback.call( element[ 0 ] );
  724. }
  725. next();
  726. });
  727. }
  728. };
  729. });
  730. })( jQuery );
  731. (function( $, undefined ) {
  732. var mouseHandled = false;
  733. $( document ).mouseup( function() {
  734. mouseHandled = false;
  735. });
  736. $.widget("ui.mouse", {
  737. version: "1.10.3",
  738. options: {
  739. cancel: "input,textarea,button,select,option",
  740. distance: 1,
  741. delay: 0
  742. },
  743. _mouseInit: function() {
  744. var that = this;
  745. this.element
  746. .bind("mousedown."+this.widgetName, function(event) {
  747. return that._mouseDown(event);
  748. })
  749. .bind("click."+this.widgetName, function(event) {
  750. if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
  751. $.removeData(event.target, that.widgetName + ".preventClickEvent");
  752. event.stopImmediatePropagation();
  753. return false;
  754. }
  755. });
  756. this.started = false;
  757. },
  758. // TODO: make sure destroying one instance of mouse doesn't mess with
  759. // other instances of mouse
  760. _mouseDestroy: function() {
  761. this.element.unbind("."+this.widgetName);
  762. if ( this._mouseMoveDelegate ) {
  763. $(document)
  764. .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
  765. .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
  766. }
  767. },
  768. _mouseDown: function(event) {
  769. // don't let more than one widget handle mouseStart
  770. if( mouseHandled ) { return; }
  771. // we may have missed mouseup (out of window)
  772. (this._mouseStarted && this._mouseUp(event));
  773. this._mouseDownEvent = event;
  774. var that = this,
  775. btnIsLeft = (event.which === 1),
  776. // event.target.nodeName works around a bug in IE 8 with
  777. // disabled inputs (#7620)
  778. elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
  779. if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
  780. return true;
  781. }
  782. this.mouseDelayMet = !this.options.delay;
  783. if (!this.mouseDelayMet) {
  784. this._mouseDelayTimer = setTimeout(function() {
  785. that.mouseDelayMet = true;
  786. }, this.options.delay);
  787. }
  788. if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
  789. this._mouseStarted = (this._mouseStart(event) !== false);
  790. if (!this._mouseStarted) {
  791. event.preventDefault();
  792. return true;
  793. }
  794. }
  795. // Click event may never have fired (Gecko & Opera)
  796. if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
  797. $.removeData(event.target, this.widgetName + ".preventClickEvent");
  798. }
  799. // these delegates are required to keep context
  800. this._mouseMoveDelegate = function(event) {
  801. return that._mouseMove(event);
  802. };
  803. this._mouseUpDelegate = function(event) {
  804. return that._mouseUp(event);
  805. };
  806. $(document)
  807. .bind("mousemove."+this.widgetName, this._mouseMoveDelegate)
  808. .bind("mouseup."+this.widgetName, this._mouseUpDelegate);
  809. event.preventDefault();
  810. mouseHandled = true;
  811. return true;
  812. },
  813. _mouseMove: function(event) {
  814. // IE mouseup check - mouseup happened when mouse was out of window
  815. if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) {
  816. return this._mouseUp(event);
  817. }
  818. if (this._mouseStarted) {
  819. this._mouseDrag(event);
  820. return event.preventDefault();
  821. }
  822. if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
  823. this._mouseStarted =
  824. (this._mouseStart(this._mouseDownEvent, event) !== false);
  825. (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
  826. }
  827. return !this._mouseStarted;
  828. },
  829. _mouseUp: function(event) {
  830. $(document)
  831. .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
  832. .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
  833. if (this._mouseStarted) {
  834. this._mouseStarted = false;
  835. if (event.target === this._mouseDownEvent.target) {
  836. $.data(event.target, this.widgetName + ".preventClickEvent", true);
  837. }
  838. this._mouseStop(event);
  839. }
  840. return false;
  841. },
  842. _mouseDistanceMet: function(event) {
  843. return (Math.max(
  844. Math.abs(this._mouseDownEvent.pageX - event.pageX),
  845. Math.abs(this._mouseDownEvent.pageY - event.pageY)
  846. ) >= this.options.distance
  847. );
  848. },
  849. _mouseDelayMet: function(/* event */) {
  850. return this.mouseDelayMet;
  851. },
  852. // These are placeholder methods, to be overriden by extending plugin
  853. _mouseStart: function(/* event */) {},
  854. _mouseDrag: function(/* event */) {},
  855. _mouseStop: function(/* event */) {},
  856. _mouseCapture: function(/* event */) { return true; }
  857. });
  858. })(jQuery);
  859. (function( $, undefined ) {
  860. $.widget("ui.draggable", $.ui.mouse, {
  861. version: "1.10.3",
  862. widgetEventPrefix: "drag",
  863. options: {
  864. addClasses: true,
  865. appendTo: "parent",
  866. axis: false,
  867. connectToSortable: false,
  868. containment: false,
  869. cursor: "auto",
  870. cursorAt: false,
  871. grid: false,
  872. handle: false,
  873. helper: "original",
  874. iframeFix: false,
  875. opacity: false,
  876. refreshPositions: false,
  877. revert: false,
  878. revertDuration: 500,
  879. scope: "default",
  880. scroll: true,
  881. scrollSensitivity: 20,
  882. scrollSpeed: 20,
  883. snap: false,
  884. snapMode: "both",
  885. snapTolerance: 20,
  886. stack: false,
  887. zIndex: false,
  888. // callbacks
  889. drag: null,
  890. start: null,
  891. stop: null
  892. },
  893. _create: function() {
  894. if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) {
  895. this.element[0].style.position = "relative";
  896. }
  897. if (this.options.addClasses){
  898. this.element.addClass("ui-draggable");
  899. }
  900. if (this.options.disabled){
  901. this.element.addClass("ui-draggable-disabled");
  902. }
  903. this._mouseInit();
  904. },
  905. _destroy: function() {
  906. this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
  907. this._mouseDestroy();
  908. },
  909. _mouseCapture: function(event) {
  910. var o = this.options;
  911. // among others, prevent a drag on a resizable-handle
  912. if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
  913. return false;
  914. }
  915. //Quit if we're not on a valid handle
  916. this.handle = this._getHandle(event);
  917. if (!this.handle) {
  918. return false;
  919. }
  920. $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
  921. $("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>")
  922. .css({
  923. width: this.offsetWidth+"px", height: this.offsetHeight+"px",
  924. position: "absolute", opacity: "0.001", zIndex: 1000
  925. })
  926. .css($(this).offset())
  927. .appendTo("body");
  928. });
  929. return true;
  930. },
  931. _mouseStart: function(event) {
  932. var o = this.options;
  933. //Create and append the visible helper
  934. this.helper = this._createHelper(event);
  935. this.helper.addClass("ui-draggable-dragging");
  936. //Cache the helper size
  937. this._cacheHelperProportions();
  938. //If ddmanager is used for droppables, set the global draggable
  939. if($.ui.ddmanager) {
  940. $.ui.ddmanager.current = this;
  941. }
  942. /*
  943. * - Position generation -
  944. * This block generates everything position related - it's the core of draggables.
  945. */
  946. //Cache the margins of the original element
  947. this._cacheMargins();
  948. //Store the helper's css position
  949. this.cssPosition = this.helper.css( "position" );
  950. this.scrollParent = this.helper.scrollParent();
  951. this.offsetParent = this.helper.offsetParent();
  952. this.offsetParentCssPosition = this.offsetParent.css( "position" );
  953. //The element's absolute position on the page minus margins
  954. this.offset = this.positionAbs = this.element.offset();
  955. this.offset = {
  956. top: this.offset.top - this.margins.top,
  957. left: this.offset.left - this.margins.left
  958. };
  959. //Reset scroll cache
  960. this.offset.scroll = false;
  961. $.extend(this.offset, {
  962. click: { //Where the click happened, relative to the element
  963. left: event.pageX - this.offset.left,
  964. top: event.pageY - this.offset.top
  965. },
  966. parent: this._getParentOffset(),
  967. relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
  968. });
  969. //Generate the original position
  970. this.originalPosition = this.position = this._generatePosition(event);
  971. this.originalPageX = event.pageX;
  972. this.originalPageY = event.pageY;
  973. //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
  974. (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
  975. //Set a containment if given in the options
  976. this._setContainment();
  977. //Trigger event + callbacks
  978. if(this._trigger("start", event) === false) {
  979. this._clear();
  980. return false;
  981. }
  982. //Recache the helper size
  983. this._cacheHelperProportions();
  984. //Prepare the droppable offsets
  985. if ($.ui.ddmanager && !o.dropBehaviour) {
  986. $.ui.ddmanager.prepareOffsets(this, event);
  987. }
  988. this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
  989. //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
  990. if ( $.ui.ddmanager ) {
  991. $.ui.ddmanager.dragStart(this, event);
  992. }
  993. return true;
  994. },
  995. _mouseDrag: function(event, noPropagation) {
  996. // reset any necessary cached properties (see #5009)
  997. if ( this.offsetParentCssPosition === "fixed" ) {
  998. this.offset.parent = this._getParentOffset();
  999. }
  1000. //Compute the helpers position
  1001. this.position = this._generatePosition(event);
  1002. this.positionAbs = this._convertPositionTo("absolute");
  1003. //Call plugins and callbacks and use the resulting position if something is returned
  1004. if (!noPropagation) {
  1005. var ui = this._uiHash();
  1006. if(this._trigger("drag", event, ui) === false) {
  1007. this._mouseUp({});
  1008. return false;
  1009. }
  1010. this.position = ui.position;
  1011. }
  1012. if(!this.options.axis || this.options.axis !== "y") {
  1013. this.helper[0].style.left = this.position.left+"px";
  1014. }
  1015. if(!this.options.axis || this.options.axis !== "x") {
  1016. this.helper[0].style.top = this.position.top+"px";
  1017. }
  1018. if($.ui.ddmanager) {
  1019. $.ui.ddmanager.drag(this, event);
  1020. }
  1021. return false;
  1022. },
  1023. _mouseStop: function(event) {
  1024. //If we are using droppables, inform the manager about the drop
  1025. var that = this,
  1026. dropped = false;
  1027. if ($.ui.ddmanager && !this.options.dropBehaviour) {
  1028. dropped = $.ui.ddmanager.drop(this, event);
  1029. }
  1030. //if a drop comes from outside (a sortable)
  1031. if(this.dropped) {
  1032. dropped = this.dropped;
  1033. this.dropped = false;
  1034. }
  1035. //if the original element is no longer in the DOM don't bother to continue (see #8269)
  1036. if ( this.options.helper === "original" && !$.contains( this.element[ 0 ].ownerDocument, this.element[ 0 ] ) ) {
  1037. return false;
  1038. }
  1039. if((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
  1040. $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
  1041. if(that._trigger("stop", event) !== false) {
  1042. that._clear();
  1043. }
  1044. });
  1045. } else {
  1046. if(this._trigger("stop", event) !== false) {
  1047. this._clear();
  1048. }
  1049. }
  1050. return false;
  1051. },
  1052. _mouseUp: function(event) {
  1053. //Remove frame helpers
  1054. $("div.ui-draggable-iframeFix").each(function() {
  1055. this.parentNode.removeChild(this);
  1056. });
  1057. //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
  1058. if( $.ui.ddmanager ) {
  1059. $.ui.ddmanager.dragStop(this, event);
  1060. }
  1061. return $.ui.mouse.prototype._mouseUp.call(this, event);
  1062. },
  1063. cancel: function() {
  1064. if(this.helper.is(".ui-draggable-dragging")) {
  1065. this._mouseUp({});
  1066. } else {
  1067. this._clear();
  1068. }
  1069. return this;
  1070. },
  1071. _getHandle: function(event) {
  1072. return this.options.handle ?
  1073. !!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
  1074. true;
  1075. },
  1076. _createHelper: function(event) {
  1077. var o = this.options,
  1078. helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element);
  1079. if(!helper.parents("body").length) {
  1080. helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
  1081. }
  1082. if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
  1083. helper.css("position", "absolute");
  1084. }
  1085. return helper;
  1086. },
  1087. _adjustOffsetFromHelper: function(obj) {
  1088. if (typeof obj === "string") {
  1089. obj = obj.split(" ");
  1090. }
  1091. if ($.isArray(obj)) {
  1092. obj = {left: +obj[0], top: +obj[1] || 0};
  1093. }
  1094. if ("left" in obj) {
  1095. this.offset.click.left = obj.left + this.margins.left;
  1096. }
  1097. if ("right" in obj) {
  1098. this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
  1099. }
  1100. if ("top" in obj) {
  1101. this.offset.click.top = obj.top + this.margins.top;
  1102. }
  1103. if ("bottom" in obj) {
  1104. this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
  1105. }
  1106. },
  1107. _getParentOffset: function() {
  1108. //Get the offsetParent and cache its position
  1109. var po = this.offsetParent.offset();
  1110. // This is a special case where we need to modify a offset calculated on start, since the following happened:
  1111. // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
  1112. // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
  1113. // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
  1114. if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
  1115. po.left += this.scrollParent.scrollLeft();
  1116. po.top += this.scrollParent.scrollTop();
  1117. }
  1118. //This needs to be actually done for all browsers, since pageX/pageY includes this information
  1119. //Ugly IE fix
  1120. if((this.offsetParent[0] === document.body) ||
  1121. (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
  1122. po = { top: 0, left: 0 };
  1123. }
  1124. return {
  1125. top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
  1126. left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
  1127. };
  1128. },
  1129. _getRelativeOffset: function() {
  1130. if(this.cssPosition === "relative") {
  1131. var p = this.element.position();
  1132. return {
  1133. top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
  1134. left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
  1135. };
  1136. } else {
  1137. return { top: 0, left: 0 };
  1138. }
  1139. },
  1140. _cacheMargins: function() {
  1141. this.margins = {
  1142. left: (parseInt(this.element.css("marginLeft"),10) || 0),
  1143. top: (parseInt(this.element.css("marginTop"),10) || 0),
  1144. right: (parseInt(this.element.css("marginRight"),10) || 0),
  1145. bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
  1146. };
  1147. },
  1148. _cacheHelperProportions: function() {
  1149. this.helperProportions = {
  1150. width: this.helper.outerWidth(),
  1151. height: this.helper.outerHeight()
  1152. };
  1153. },
  1154. _setContainment: function() {
  1155. var over, c, ce,
  1156. o = this.options;
  1157. if ( !o.containment ) {
  1158. this.containment = null;
  1159. return;
  1160. }
  1161. if ( o.containment === "window" ) {
  1162. this.containment = [
  1163. $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
  1164. $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
  1165. $( window ).scrollLeft() + $( window ).width() - this.helperProportions.width - this.margins.left,
  1166. $( window ).scrollTop() + ( $( window ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
  1167. ];
  1168. return;
  1169. }
  1170. if ( o.containment === "document") {
  1171. this.containment = [
  1172. 0,
  1173. 0,
  1174. $( document ).width() - this.helperProportions.width - this.margins.left,
  1175. ( $( document ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
  1176. ];
  1177. return;
  1178. }
  1179. if ( o.containment.constructor === Array ) {
  1180. this.containment = o.containment;
  1181. return;
  1182. }
  1183. if ( o.containment === "parent" ) {
  1184. o.containment = this.helper[ 0 ].parentNode;
  1185. }
  1186. c = $( o.containment );
  1187. ce = c[ 0 ];
  1188. if( !ce ) {
  1189. return;
  1190. }
  1191. over = c.css( "overflow" ) !== "hidden";
  1192. this.containment = [
  1193. ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
  1194. ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ) ,
  1195. ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) - ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) - this.helperProportions.width - this.margins.left - this.margins.right,
  1196. ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) - ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) - this.helperProportions.height - this.margins.top - this.margins.bottom
  1197. ];
  1198. this.relative_container = c;
  1199. },
  1200. _convertPositionTo: function(d, pos) {
  1201. if(!pos) {
  1202. pos = this.position;
  1203. }
  1204. var mod = d === "absolute" ? 1 : -1,
  1205. scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent;
  1206. //Cache the scroll
  1207. if (!this.offset.scroll) {
  1208. this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
  1209. }
  1210. return {
  1211. top: (
  1212. pos.top + // The absolute mouse position
  1213. this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
  1214. this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
  1215. ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top ) * mod )
  1216. ),
  1217. left: (
  1218. pos.left + // The absolute mouse position
  1219. this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
  1220. this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
  1221. ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left ) * mod )
  1222. )
  1223. };
  1224. },
  1225. _generatePosition: function(event) {
  1226. var containment, co, top, left,
  1227. o = this.options,
  1228. scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent,
  1229. pageX = event.pageX,
  1230. pageY = event.pageY;
  1231. //Cache the scroll
  1232. if (!this.offset.scroll) {
  1233. this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
  1234. }
  1235. /*
  1236. * - Position constraining -
  1237. * Constrain the position to a mix of grid, containment.
  1238. */
  1239. // If we are not dragging yet, we won't check for options
  1240. if ( this.originalPosition ) {
  1241. if ( this.containment ) {
  1242. if ( this.relative_container ){
  1243. co = this.relative_container.offset();
  1244. containment = [
  1245. this.containment[ 0 ] + co.left,
  1246. this.containment[ 1 ] + co.top,
  1247. this.containment[ 2 ] + co.left,
  1248. this.containment[ 3 ] + co.top
  1249. ];
  1250. }
  1251. else {
  1252. containment = this.containment;
  1253. }
  1254. if(event.pageX - this.offset.click.left < containment[0]) {
  1255. pageX = containment[0] + this.offset.click.left;
  1256. }
  1257. if(event.pageY - this.offset.click.top < containment[1]) {
  1258. pageY = containment[1] + this.offset.click.top;
  1259. }
  1260. if(event.pageX - this.offset.click.left > containment[2]) {
  1261. pageX = containment[2] + this.offset.click.left;
  1262. }
  1263. if(event.pageY - this.offset.click.top > containment[3]) {
  1264. pageY = containment[3] + this.offset.click.top;
  1265. }
  1266. }
  1267. if(o.grid) {
  1268. //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
  1269. top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
  1270. pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
  1271. left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
  1272. pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
  1273. }
  1274. }
  1275. return {
  1276. top: (
  1277. pageY - // The absolute mouse position
  1278. this.offset.click.top - // Click offset (relative to the element)
  1279. this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
  1280. this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
  1281. ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top )
  1282. ),
  1283. left: (
  1284. pageX - // The absolute mouse position
  1285. this.offset.click.left - // Click offset (relative to the element)
  1286. this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
  1287. this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
  1288. ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left )
  1289. )
  1290. };
  1291. },
  1292. _clear: function() {
  1293. this.helper.removeClass("ui-draggable-dragging");
  1294. if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
  1295. this.helper.remove();
  1296. }
  1297. this.helper = null;
  1298. this.cancelHelperRemoval = false;
  1299. },
  1300. // From now on bulk stuff - mainly helpers
  1301. _trigger: function(type, event, ui) {
  1302. ui = ui || this._uiHash();
  1303. $.ui.plugin.call(this, type, [event, ui]);
  1304. //The absolute position has to be recalculated after plugins
  1305. if(type === "drag") {
  1306. this.positionAbs = this._convertPositionTo("absolute");
  1307. }
  1308. return $.Widget.prototype._trigger.call(this, type, event, ui);
  1309. },
  1310. plugins: {},
  1311. _uiHash: function() {
  1312. return {
  1313. helper: this.helper,
  1314. position: this.position,
  1315. originalPosition: this.originalPosition,
  1316. offset: this.positionAbs
  1317. };
  1318. }
  1319. });
  1320. $.ui.plugin.add("draggable", "connectToSortable", {
  1321. start: function(event, ui) {
  1322. var inst = $(this).data("ui-draggable"), o = inst.options,
  1323. uiSortable = $.extend({}, ui, { item: inst.element });
  1324. inst.sortables = [];
  1325. $(o.connectToSortable).each(function() {
  1326. var sortable = $.data(this, "ui-sortable");
  1327. if (sortable && !sortable.options.disabled) {
  1328. inst.sortables.push({
  1329. instance: sortable,
  1330. shouldRevert: sortable.options.revert
  1331. });
  1332. sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
  1333. sortable._trigger("activate", event, uiSortable);
  1334. }
  1335. });
  1336. },
  1337. stop: function(event, ui) {
  1338. //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
  1339. var inst = $(this).data("ui-draggable"),
  1340. uiSortable = $.extend({}, ui, { item: inst.element });
  1341. $.each(inst.sortables, function() {
  1342. if(this.instance.isOver) {
  1343. this.instance.isOver = 0;
  1344. inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
  1345. this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
  1346. //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid"
  1347. if(this.shouldRevert) {
  1348. this.instance.options.revert = this.shouldRevert;
  1349. }
  1350. //Trigger the stop of the sortable
  1351. this.instance._mouseStop(event);
  1352. this.instance.options.helper = this.instance.options._helper;
  1353. //If the helper has been the original item, restore properties in the sortable
  1354. if(inst.options.helper === "original") {
  1355. this.instance.currentItem.css({ top: "auto", left: "auto" });
  1356. }
  1357. } else {
  1358. this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
  1359. this.instance._trigger("deactivate", event, uiSortable);
  1360. }
  1361. });
  1362. },
  1363. drag: function(event, ui) {
  1364. var inst = $(this).data("ui-draggable"), that = this;
  1365. $.each(inst.sortables, function() {
  1366. var innermostIntersecting = false,
  1367. thisSortable = this;
  1368. //Copy over some variables to allow calling the sortable's native _intersectsWith
  1369. this.instance.positionAbs = inst.positionAbs;
  1370. this.instance.helperProportions = inst.helperProportions;
  1371. this.instance.offset.click = inst.offset.click;
  1372. if(this.instance._intersectsWith(this.instance.containerCache)) {
  1373. innermostIntersecting = true;
  1374. $.each(inst.sortables, function () {
  1375. this.instance.positionAbs = inst.positionAbs;
  1376. this.instance.helperProportions = inst.helperProportions;
  1377. this.instance.offset.click = inst.offset.click;
  1378. if (this !== thisSortable &&
  1379. this.instance._intersectsWith(this.instance.containerCache) &&
  1380. $.contains(thisSortable.instance.element[0], this.instance.element[0])
  1381. ) {
  1382. innermostIntersecting = false;
  1383. }
  1384. return innermostIntersecting;
  1385. });
  1386. }
  1387. if(innermostIntersecting) {
  1388. //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
  1389. if(!this.instance.isOver) {
  1390. this.instance.isOver = 1;
  1391. //Now we fake the start of dragging for the sortable instance,
  1392. //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
  1393. //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
  1394. this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true);
  1395. this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
  1396. this.instance.options.helper = function() { return ui.helper[0]; };
  1397. event.target = this.instance.currentItem[0];
  1398. this.instance._mouseCapture(event, true);
  1399. this.instance._mouseStart(event, true, true);
  1400. //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
  1401. this.instance.offset.click.top = inst.offset.click.top;
  1402. this.instance.offset.click.left = inst.offset.click.left;
  1403. this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
  1404. this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
  1405. inst._trigger("toSortable", event);
  1406. inst.dropped = this.instance.element; //draggable revert needs that
  1407. //hack so receive/update callbacks work (mostly)
  1408. inst.currentItem = inst.element;
  1409. this.instance.fromOutside = inst;
  1410. }
  1411. //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
  1412. if(this.instance.currentItem) {
  1413. this.instance._mouseDrag(event);
  1414. }
  1415. } else {
  1416. //If it doesn't intersect with the sortable, and it intersected before,
  1417. //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
  1418. if(this.instance.isOver) {
  1419. this.instance.isOver = 0;
  1420. this.instance.cancelHelperRemoval = true;
  1421. //Prevent reverting on this forced stop
  1422. this.instance.options.revert = false;
  1423. // The out event needs to be triggered independently
  1424. this.instance._trigger("out", event, this.instance._uiHash(this.instance));
  1425. this.instance._mouseStop(event, true);
  1426. this.instance.options.helper = this.instance.options._helper;
  1427. //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
  1428. this.instance.currentItem.remove();
  1429. if(this.instance.placeholder) {
  1430. this.instance.placeholder.remove();
  1431. }
  1432. inst._trigger("fromSortable", event);
  1433. inst.dropped = false; //draggable revert needs that
  1434. }
  1435. }
  1436. });
  1437. }
  1438. });
  1439. $.ui.plugin.add("draggable", "cursor", {
  1440. start: function() {
  1441. var t = $("body"), o = $(this).data("ui-draggable").options;
  1442. if (t.css("cursor")) {
  1443. o._cursor = t.css("cursor");
  1444. }
  1445. t.css("cursor", o.cursor);
  1446. },
  1447. stop: function() {
  1448. var o = $(this).data("ui-draggable").options;
  1449. if (o._cursor) {
  1450. $("body").css("cursor", o._cursor);
  1451. }
  1452. }
  1453. });
  1454. $.ui.plugin.add("draggable", "opacity", {
  1455. start: function(event, ui) {
  1456. var t = $(ui.helper), o = $(this).data("ui-draggable").options;
  1457. if(t.css("opacity")) {
  1458. o._opacity = t.css("opacity");
  1459. }
  1460. t.css("opacity", o.opacity);
  1461. },
  1462. stop: function(event, ui) {
  1463. var o = $(this).data("ui-draggable").options;
  1464. if(o._opacity) {
  1465. $(ui.helper).css("opacity", o._opacity);
  1466. }
  1467. }
  1468. });
  1469. $.ui.plugin.add("draggable", "scroll", {
  1470. start: function() {
  1471. var i = $(this).data("ui-draggable");
  1472. if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
  1473. i.overflowOffset = i.scrollParent.offset();
  1474. }
  1475. },
  1476. drag: function( event ) {
  1477. var i = $(this).data("ui-draggable"), o = i.options, scrolled = false;
  1478. if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
  1479. if(!o.axis || o.axis !== "x") {
  1480. if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
  1481. i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
  1482. } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) {
  1483. i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
  1484. }
  1485. }
  1486. if(!o.axis || o.axis !== "y") {
  1487. if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
  1488. i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
  1489. } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) {
  1490. i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
  1491. }
  1492. }
  1493. } else {
  1494. if(!o.axis || o.axis !== "x") {
  1495. if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
  1496. scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
  1497. } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
  1498. scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
  1499. }
  1500. }
  1501. if(!o.axis || o.axis !== "y") {
  1502. if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
  1503. scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
  1504. } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
  1505. scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
  1506. }
  1507. }
  1508. }
  1509. if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
  1510. $.ui.ddmanager.prepareOffsets(i, event);
  1511. }
  1512. }
  1513. });
  1514. $.ui.plugin.add("draggable", "snap", {
  1515. start: function() {
  1516. var i = $(this).data("ui-draggable"),
  1517. o = i.options;
  1518. i.snapElements = [];
  1519. $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
  1520. var $t = $(this),
  1521. $o = $t.offset();
  1522. if(this !== i.element[0]) {
  1523. i.snapElements.push({
  1524. item: this,
  1525. width: $t.outerWidth(), height: $t.outerHeight(),
  1526. top: $o.top, left: $o.left
  1527. });
  1528. }
  1529. });
  1530. },
  1531. drag: function(event, ui) {
  1532. var ts, bs, ls, rs, l, r, t, b, i, first,
  1533. inst = $(this).data("ui-draggable"),
  1534. o = inst.options,
  1535. d = o.snapTolerance,
  1536. x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
  1537. y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
  1538. for (i = inst.snapElements.length - 1; i >= 0; i--){
  1539. l = inst.snapElements[i].left;
  1540. r = l + inst.snapElements[i].width;
  1541. t = inst.snapElements[i].top;
  1542. b = t + inst.snapElements[i].height;
  1543. if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || !$.contains( inst.snapElements[ i ].item.ownerDocument, inst.snapElements[ i ].item ) ) {
  1544. if(inst.snapElements[i].snapping) {
  1545. (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
  1546. }
  1547. inst.snapElements[i].snapping = false;
  1548. continue;
  1549. }
  1550. if(o.snapMode !== "inner") {
  1551. ts = Math.abs(t - y2) <= d;
  1552. bs = Math.abs(b - y1) <= d;
  1553. ls = Math.abs(l - x2) <= d;
  1554. rs = Math.abs(r - x1) <= d;
  1555. if(ts) {
  1556. ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
  1557. }
  1558. if(bs) {
  1559. ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
  1560. }
  1561. if(ls) {
  1562. ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
  1563. }
  1564. if(rs) {
  1565. ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
  1566. }
  1567. }
  1568. first = (ts || bs || ls || rs);
  1569. if(o.snapMode !== "outer") {
  1570. ts = Math.abs(t - y1) <= d;
  1571. bs = Math.abs(b - y2) <= d;
  1572. ls = Math.abs(l - x1) <= d;
  1573. rs = Math.abs(r - x2) <= d;
  1574. if(ts) {
  1575. ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
  1576. }
  1577. if(bs) {
  1578. ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
  1579. }
  1580. if(ls) {
  1581. ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
  1582. }
  1583. if(rs) {
  1584. ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
  1585. }
  1586. }
  1587. if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
  1588. (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
  1589. }
  1590. inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
  1591. }
  1592. }
  1593. });
  1594. $.ui.plugin.add("draggable", "stack", {
  1595. start: function() {
  1596. var min,
  1597. o = this.data("ui-draggable").options,
  1598. group = $.makeArray($(o.stack)).sort(function(a,b) {
  1599. return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
  1600. });
  1601. if (!group.length) { return; }
  1602. min = parseInt($(group[0]).css("zIndex"), 10) || 0;
  1603. $(group).each(function(i) {
  1604. $(this).css("zIndex", min + i);
  1605. });
  1606. this.css("zIndex", (min + group.length));
  1607. }
  1608. });
  1609. $.ui.plugin.add("draggable", "zIndex", {
  1610. start: function(event, ui) {
  1611. var t = $(ui.helper), o = $(this).data("ui-draggable").options;
  1612. if(t.css("zIndex")) {
  1613. o._zIndex = t.css("zIndex");
  1614. }
  1615. t.css("zIndex", o.zIndex);
  1616. },
  1617. stop: function(event, ui) {
  1618. var o = $(this).data("ui-draggable").options;
  1619. if(o._zIndex) {
  1620. $(ui.helper).css("zIndex", o._zIndex);
  1621. }
  1622. }
  1623. });
  1624. })(jQuery);
  1625. (function( $, undefined ) {
  1626. function isOverAxis( x, reference, size ) {
  1627. return ( x > reference ) && ( x < ( reference + size ) );
  1628. }
  1629. $.widget("ui.droppable", {
  1630. version: "1.10.3",
  1631. widgetEventPrefix: "drop",
  1632. options: {
  1633. accept: "*",
  1634. activeClass: false,
  1635. addClasses: true,
  1636. greedy: false,
  1637. hoverClass: false,
  1638. scope: "default",
  1639. tolerance: "intersect",
  1640. // callbacks
  1641. activate: null,
  1642. deactivate: null,
  1643. drop: null,
  1644. out: null,
  1645. over: null
  1646. },
  1647. _create: function() {
  1648. var o = this.options,
  1649. accept = o.accept;
  1650. this.isover = false;
  1651. this.isout = true;
  1652. this.accept = $.isFunction(accept) ? accept : function(d) {
  1653. return d.is(accept);
  1654. };
  1655. //Store the droppable's proportions
  1656. this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
  1657. // Add the reference and positions to the manager
  1658. $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
  1659. $.ui.ddmanager.droppables[o.scope].push(this);
  1660. (o.addClasses && this.element.addClass("ui-droppable"));
  1661. },
  1662. _destroy: function() {
  1663. var i = 0,
  1664. drop = $.ui.ddmanager.droppables[this.options.scope];
  1665. for ( ; i < drop.length; i++ ) {
  1666. if ( drop[i] === this ) {
  1667. drop.splice(i, 1);
  1668. }
  1669. }
  1670. this.element.removeClass("ui-droppable ui-droppable-disabled");
  1671. },
  1672. _setOption: function(key, value) {
  1673. if(key === "accept") {
  1674. this.accept = $.isFunction(value) ? value : function(d) {
  1675. return d.is(value);
  1676. };
  1677. }
  1678. $.Widget.prototype._setOption.apply(this, arguments);
  1679. },
  1680. _activate: function(event) {
  1681. var draggable = $.ui.ddmanager.current;
  1682. if(this.options.activeClass) {
  1683. this.element.addClass(this.options.activeClass);
  1684. }
  1685. if(draggable){
  1686. this._trigger("activate", event, this.ui(draggable));
  1687. }
  1688. },
  1689. _deactivate: function(event) {
  1690. var draggable = $.ui.ddmanager.current;
  1691. if(this.options.activeClass) {
  1692. this.element.removeClass(this.options.activeClass);
  1693. }
  1694. if(draggable){
  1695. this._trigger("deactivate", event, this.ui(draggable));
  1696. }
  1697. },
  1698. _over: function(event) {
  1699. var draggable = $.ui.ddmanager.current;
  1700. // Bail if draggable and droppable are same element
  1701. if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
  1702. return;
  1703. }
  1704. if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
  1705. if(this.options.hoverClass) {
  1706. this.element.addClass(this.options.hoverClass);
  1707. }
  1708. this._trigger("over", event, this.ui(draggable));
  1709. }
  1710. },
  1711. _out: function(event) {
  1712. var draggable = $.ui.ddmanager.current;
  1713. // Bail if draggable and droppable are same element
  1714. if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
  1715. return;
  1716. }
  1717. if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
  1718. if(this.options.hoverClass) {
  1719. this.element.removeClass(this.options.hoverClass);
  1720. }
  1721. this._trigger("out", event, this.ui(draggable));
  1722. }
  1723. },
  1724. _drop: function(event,custom) {
  1725. var draggable = custom || $.ui.ddmanager.current,
  1726. childrenIntersection = false;
  1727. // Bail if draggable and droppable are same element
  1728. if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
  1729. return false;
  1730. }
  1731. this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() {
  1732. var inst = $.data(this, "ui-droppable");
  1733. if(
  1734. inst.options.greedy &&
  1735. !inst.options.disabled &&
  1736. inst.options.scope === draggable.options.scope &&
  1737. inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) &&
  1738. $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
  1739. ) { childrenIntersection = true; return false; }
  1740. });
  1741. if(childrenIntersection) {
  1742. return false;
  1743. }
  1744. if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
  1745. if(this.options.activeClass) {
  1746. this.element.removeClass(this.options.activeClass);
  1747. }
  1748. if(this.options.hoverClass) {
  1749. this.element.removeClass(this.options.hoverClass);
  1750. }
  1751. this._trigger("drop", event, this.ui(draggable));
  1752. return this.element;
  1753. }
  1754. return false;
  1755. },
  1756. ui: function(c) {
  1757. return {
  1758. draggable: (c.currentItem || c.element),
  1759. helper: c.helper,
  1760. position: c.position,
  1761. offset: c.positionAbs
  1762. };
  1763. }
  1764. });
  1765. $.ui.intersect = function(draggable, droppable, toleranceMode) {
  1766. if (!droppable.offset) {
  1767. return false;
  1768. }
  1769. var draggableLeft, draggableTop,
  1770. x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
  1771. y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height,
  1772. l = droppable.offset.left, r = l + droppable.proportions.width,
  1773. t = droppable.offset.top, b = t + droppable.proportions.height;
  1774. switch (toleranceMode) {
  1775. case "fit":
  1776. return (l <= x1 && x2 <= r && t <= y1 && y2 <= b);
  1777. case "intersect":
  1778. return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half
  1779. x2 - (draggable.helperProportions.width / 2) < r && // Left Half
  1780. t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half
  1781. y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
  1782. case "pointer":
  1783. draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left);
  1784. draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top);
  1785. return isOverAxis( draggableTop, t, droppable.proportions.height ) && isOverAxis( draggableLeft, l, droppable.proportions.width );
  1786. case "touch":
  1787. return (
  1788. (y1 >= t && y1 <= b) || // Top edge touching
  1789. (y2 >= t && y2 <= b) || // Bottom edge touching
  1790. (y1 < t && y2 > b) // Surrounded vertically
  1791. ) && (
  1792. (x1 >= l && x1 <= r) || // Left edge touching
  1793. (x2 >= l && x2 <= r) || // Right edge touching
  1794. (x1 < l && x2 > r) // Surrounded horizontally
  1795. );
  1796. default:
  1797. return false;
  1798. }
  1799. };
  1800. /*
  1801. This manager tracks offsets of draggables and droppables
  1802. */
  1803. $.ui.ddmanager = {
  1804. current: null,
  1805. droppables: { "default": [] },
  1806. prepareOffsets: function(t, event) {
  1807. var i, j,
  1808. m = $.ui.ddmanager.droppables[t.options.scope] || [],
  1809. type = event ? event.type : null, // workaround for #2317
  1810. list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack();
  1811. droppablesLoop: for (i = 0; i < m.length; i++) {
  1812. //No disabled and non-accepted
  1813. if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) {
  1814. continue;
  1815. }
  1816. // Filter out elements in the current dragged item
  1817. for (j=0; j < list.length; j++) {
  1818. if(list[j] === m[i].element[0]) {
  1819. m[i].proportions.height = 0;
  1820. continue droppablesLoop;
  1821. }
  1822. }
  1823. m[i].visible = m[i].element.css("display") !== "none";
  1824. if(!m[i].visible) {
  1825. continue;
  1826. }
  1827. //Activate the droppable if used directly from draggables
  1828. if(type === "mousedown") {
  1829. m[i]._activate.call(m[i], event);
  1830. }
  1831. m[i].offset = m[i].element.offset();
  1832. m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
  1833. }
  1834. },
  1835. drop: function(draggable, event) {
  1836. var dropped = false;
  1837. // Create a copy of the droppables in case the list changes during the drop (#9116)
  1838. $.each(($.ui.ddmanager.droppables[draggable.options.scope] || []).slice(), function() {
  1839. if(!this.options) {
  1840. return;
  1841. }
  1842. if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) {
  1843. dropped = this._drop.call(this, event) || dropped;
  1844. }
  1845. if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
  1846. this.isout = true;
  1847. this.isover = false;
  1848. this._deactivate.call(this, event);
  1849. }
  1850. });
  1851. return dropped;
  1852. },
  1853. dragStart: function( draggable, event ) {
  1854. //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
  1855. draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
  1856. if( !draggable.options.refreshPositions ) {
  1857. $.ui.ddmanager.prepareOffsets( draggable, event );
  1858. }
  1859. });
  1860. },
  1861. drag: function(draggable, event) {
  1862. //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
  1863. if(draggable.options.refreshPositions) {
  1864. $.ui.ddmanager.prepareOffsets(draggable, event);
  1865. }
  1866. //Run through all droppables and check their positions based on specific tolerance options
  1867. $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
  1868. if(this.options.disabled || this.greedyChild || !this.visible) {
  1869. return;
  1870. }
  1871. var parentInstance, scope, parent,
  1872. intersects = $.ui.intersect(draggable, this, this.options.tolerance),
  1873. c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null);
  1874. if(!c) {
  1875. return;
  1876. }
  1877. if (this.options.greedy) {
  1878. // find droppable parents with same scope
  1879. scope = this.options.scope;
  1880. parent = this.element.parents(":data(ui-droppable)").filter(function () {
  1881. return $.data(this, "ui-droppable").options.scope === scope;
  1882. });
  1883. if (parent.length) {
  1884. parentInstance = $.data(parent[0], "ui-droppable");
  1885. parentInstance.greedyChild = (c === "isover");
  1886. }
  1887. }
  1888. // we just moved into a greedy child
  1889. if (parentInstance && c === "isover") {
  1890. parentInstance.isover = false;
  1891. parentInstance.isout = true;
  1892. parentInstance._out.call(parentInstance, event);
  1893. }
  1894. this[c] = true;
  1895. this[c === "isout" ? "isover" : "isout"] = false;
  1896. this[c === "isover" ? "_over" : "_out"].call(this, event);
  1897. // we just moved out of a greedy child
  1898. if (parentInstance && c === "isout") {
  1899. parentInstance.isout = false;
  1900. parentInstance.isover = true;
  1901. parentInstance._over.call(parentInstance, event);
  1902. }
  1903. });
  1904. },
  1905. dragStop: function( draggable, event ) {
  1906. draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
  1907. //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
  1908. if( !draggable.options.refreshPositions ) {
  1909. $.ui.ddmanager.prepareOffsets( draggable, event );
  1910. }
  1911. }
  1912. };
  1913. })(jQuery);
  1914. (function( $, undefined ) {
  1915. function num(v) {
  1916. return parseInt(v, 10) || 0;
  1917. }
  1918. function isNumber(value) {
  1919. return !isNaN(parseInt(value, 10));
  1920. }
  1921. $.widget("ui.resizable", $.ui.mouse, {
  1922. version: "1.10.3",
  1923. widgetEventPrefix: "resize",
  1924. options: {
  1925. alsoResize: false,
  1926. animate: false,
  1927. animateDuration: "slow",
  1928. animateEasing: "swing",
  1929. aspectRatio: false,
  1930. autoHide: false,
  1931. containment: false,
  1932. ghost: false,
  1933. grid: false,
  1934. handles: "e,s,se",
  1935. helper: false,
  1936. maxHeight: null,
  1937. maxWidth: null,
  1938. minHeight: 10,
  1939. minWidth: 10,
  1940. // See #7960
  1941. zIndex: 90,
  1942. // callbacks
  1943. resize: null,
  1944. start: null,
  1945. stop: null
  1946. },
  1947. _create: function() {
  1948. var n, i, handle, axis, hname,
  1949. that = this,
  1950. o = this.options;
  1951. this.element.addClass("ui-resizable");
  1952. $.extend(this, {
  1953. _aspectRatio: !!(o.aspectRatio),
  1954. aspectRatio: o.aspectRatio,
  1955. originalElement: this.element,
  1956. _proportionallyResizeElements: [],
  1957. _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
  1958. });
  1959. //Wrap the element if it cannot hold child nodes
  1960. if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
  1961. //Create a wrapper element and set the wrapper to the new current internal element
  1962. this.element.wrap(
  1963. $("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({
  1964. position: this.element.css("position"),
  1965. width: this.element.outerWidth(),
  1966. height: this.element.outerHeight(),
  1967. top: this.element.css("top"),
  1968. left: this.element.css("left")
  1969. })
  1970. );
  1971. //Overwrite the original this.element
  1972. this.element = this.element.parent().data(
  1973. "ui-resizable", this.element.data("ui-resizable")
  1974. );
  1975. this.elementIsWrapper = true;
  1976. //Move margins to the wrapper
  1977. this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
  1978. this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
  1979. //Prevent Safari textarea resize
  1980. this.originalResizeStyle = this.originalElement.css("resize");
  1981. this.originalElement.css("resize", "none");
  1982. //Push the actual element to our proportionallyResize internal array
  1983. this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" }));
  1984. // avoid IE jump (hard set the margin)
  1985. this.originalElement.css({ margin: this.originalElement.css("margin") });
  1986. // fix handlers offset
  1987. this._proportionallyResize();
  1988. }
  1989. this.handles = o.handles || (!$(".ui-resizable-handle", this.element).length ? "e,s,se" : { n: ".ui-resizable-n", e: ".ui-resizable-e", s: ".ui-resizable-s", w: ".ui-resizable-w", se: ".ui-resizable-se", sw: ".ui-resizable-sw", ne: ".ui-resizable-ne", nw: ".ui-resizable-nw" });
  1990. if(this.handles.constructor === String) {
  1991. if ( this.handles === "all") {
  1992. this.handles = "n,e,s,w,se,sw,ne,nw";
  1993. }
  1994. n = this.handles.split(",");
  1995. this.handles = {};
  1996. for(i = 0; i < n.length; i++) {
  1997. handle = $.trim(n[i]);
  1998. hname = "ui-resizable-"+handle;
  1999. axis = $("<div class='ui-resizable-handle " + hname + "'></div>");
  2000. // Apply zIndex to all handles - see #7960
  2001. axis.css({ zIndex: o.zIndex });
  2002. //TODO : What's going on here?
  2003. if ("se" === handle) {
  2004. axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se");
  2005. }
  2006. //Insert into internal handles object and append to element
  2007. this.handles[handle] = ".ui-resizable-"+handle;
  2008. this.element.append(axis);
  2009. }
  2010. }
  2011. this._renderAxis = function(target) {
  2012. var i, axis, padPos, padWrapper;
  2013. target = target || this.element;
  2014. for(i in this.handles) {
  2015. if(this.handles[i].constructor === String) {
  2016. this.handles[i] = $(this.handles[i], this.element).show();
  2017. }
  2018. //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
  2019. if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
  2020. axis = $(this.handles[i], this.element);
  2021. //Checking the correct pad and border
  2022. padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
  2023. //The padding type i have to apply...
  2024. padPos = [ "padding",
  2025. /ne|nw|n/.test(i) ? "Top" :
  2026. /se|sw|s/.test(i) ? "Bottom" :
  2027. /^e$/.test(i) ? "Right" : "Left" ].join("");
  2028. target.css(padPos, padWrapper);
  2029. this._proportionallyResize();
  2030. }
  2031. //TODO: What's that good for? There's not anything to be executed left
  2032. if(!$(this.handles[i]).length) {
  2033. continue;
  2034. }
  2035. }
  2036. };
  2037. //TODO: make renderAxis a prototype function
  2038. this._renderAxis(this.element);
  2039. this._handles = $(".ui-resizable-handle", this.element)
  2040. .disableSelection();
  2041. //Matching axis name
  2042. this._handles.mouseover(function() {
  2043. if (!that.resizing) {
  2044. if (this.className) {
  2045. axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
  2046. }
  2047. //Axis, default = se
  2048. that.axis = axis && axis[1] ? axis[1] : "se";
  2049. }
  2050. });
  2051. //If we want to auto hide the elements
  2052. if (o.autoHide) {
  2053. this._handles.hide();
  2054. $(this.element)
  2055. .addClass("ui-resizable-autohide")
  2056. .mouseenter(function() {
  2057. if (o.disabled) {
  2058. return;
  2059. }
  2060. $(this).removeClass("ui-resizable-autohide");
  2061. that._handles.show();
  2062. })
  2063. .mouseleave(function(){
  2064. if (o.disabled) {
  2065. return;
  2066. }
  2067. if (!that.resizing) {
  2068. $(this).addClass("ui-resizable-autohide");
  2069. that._handles.hide();
  2070. }
  2071. });
  2072. }
  2073. //Initialize the mouse interaction
  2074. this._mouseInit();
  2075. },
  2076. _destroy: function() {
  2077. this._mouseDestroy();
  2078. var wrapper,
  2079. _destroy = function(exp) {
  2080. $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
  2081. .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove();
  2082. };
  2083. //TODO: Unwrap at same DOM position
  2084. if (this.elementIsWrapper) {
  2085. _destroy(this.element);
  2086. wrapper = this.element;
  2087. this.originalElement.css({
  2088. position: wrapper.css("position"),
  2089. width: wrapper.outerWidth(),
  2090. height: wrapper.outerHeight(),
  2091. top: wrapper.css("top"),
  2092. left: wrapper.css("left")
  2093. }).insertAfter( wrapper );
  2094. wrapper.remove();
  2095. }
  2096. this.originalElement.css("resize", this.originalResizeStyle);
  2097. _destroy(this.originalElement);
  2098. return this;
  2099. },
  2100. _mouseCapture: function(event) {
  2101. var i, handle,
  2102. capture = false;
  2103. for (i in this.handles) {
  2104. handle = $(this.handles[i])[0];
  2105. if (handle === event.target || $.contains(handle, event.target)) {
  2106. capture = true;
  2107. }
  2108. }
  2109. return !this.options.disabled && capture;
  2110. },
  2111. _mouseStart: function(event) {
  2112. var curleft, curtop, cursor,
  2113. o = this.options,
  2114. iniPos = this.element.position(),
  2115. el = this.element;
  2116. this.resizing = true;
  2117. // bugfix for http://dev.jquery.com/ticket/1749
  2118. if ( (/absolute/).test( el.css("position") ) ) {
  2119. el.css({ position: "absolute", top: el.css("top"), left: el.css("left") });
  2120. } else if (el.is(".ui-draggable")) {
  2121. el.css({ position: "absolute", top: iniPos.top, left: iniPos.left });
  2122. }
  2123. this._renderProxy();
  2124. curleft = num(this.helper.css("left"));
  2125. curtop = num(this.helper.css("top"));
  2126. if (o.containment) {
  2127. curleft += $(o.containment).scrollLeft() || 0;
  2128. curtop += $(o.containment).scrollTop() || 0;
  2129. }
  2130. //Store needed variables
  2131. this.offset = this.helper.offset();
  2132. this.position = { left: curleft, top: curtop };
  2133. this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
  2134. this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
  2135. this.originalPosition = { left: curleft, top: curtop };
  2136. this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
  2137. this.originalMousePosition = { left: event.pageX, top: event.pageY };
  2138. //Aspect Ratio
  2139. this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
  2140. cursor = $(".ui-resizable-" + this.axis).css("cursor");
  2141. $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);
  2142. el.addClass("ui-resizable-resizing");
  2143. this._propagate("start", event);
  2144. return true;
  2145. },
  2146. _mouseDrag: function(event) {
  2147. //Increase performance, avoid regex
  2148. var data,
  2149. el = this.helper, props = {},
  2150. smp = this.originalMousePosition,
  2151. a = this.axis,
  2152. prevTop = this.position.top,
  2153. prevLeft = this.position.left,
  2154. prevWidth = this.size.width,
  2155. prevHeight = this.size.height,
  2156. dx = (event.pageX-smp.left)||0,
  2157. dy = (event.pageY-smp.top)||0,
  2158. trigger = this._change[a];
  2159. if (!trigger) {
  2160. return false;
  2161. }
  2162. // Calculate the attrs that will be change
  2163. data = trigger.apply(this, [event, dx, dy]);
  2164. // Put this in the mouseDrag handler since the user can start pressing shift while resizing
  2165. this._updateVirtualBoundaries(event.shiftKey);
  2166. if (this._aspectRatio || event.shiftKey) {
  2167. data = this._updateRatio(data, event);
  2168. }
  2169. data = this._respectSize(data, event);
  2170. this._updateCache(data);
  2171. // plugins callbacks need to be called first
  2172. this._propagate("resize", event);
  2173. if (this.position.top !== prevTop) {
  2174. props.top = this.position.top + "px";
  2175. }
  2176. if (this.position.left !== prevLeft) {
  2177. props.left = this.position.left + "px";
  2178. }
  2179. if (this.size.width !== prevWidth) {
  2180. props.width = this.size.width + "px";
  2181. }
  2182. if (this.size.height !== prevHeight) {
  2183. props.height = this.size.height + "px";
  2184. }
  2185. el.css(props);
  2186. if (!this._helper && this._proportionallyResizeElements.length) {
  2187. this._proportionallyResize();
  2188. }
  2189. // Call the user callback if the element was resized
  2190. if ( ! $.isEmptyObject(props) ) {
  2191. this._trigger("resize", event, this.ui());
  2192. }
  2193. return false;
  2194. },
  2195. _mouseStop: function(event) {
  2196. this.resizing = false;
  2197. var pr, ista, soffseth, soffsetw, s, left, top,
  2198. o = this.options, that = this;
  2199. if(this._helper) {
  2200. pr = this._proportionallyResizeElements;
  2201. ista = pr.length && (/textarea/i).test(pr[0].nodeName);
  2202. soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height;
  2203. soffsetw = ista ? 0 : that.sizeDiff.width;
  2204. s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) };
  2205. left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null;
  2206. top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
  2207. if (!o.animate) {
  2208. this.element.css($.extend(s, { top: top, left: left }));
  2209. }
  2210. that.helper.height(that.size.height);
  2211. that.helper.width(that.size.width);
  2212. if (this._helper && !o.animate) {
  2213. this._proportionallyResize();
  2214. }
  2215. }
  2216. $("body").css("cursor", "auto");
  2217. this.element.removeClass("ui-resizable-resizing");
  2218. this._propagate("stop", event);
  2219. if (this._helper) {
  2220. this.helper.remove();
  2221. }
  2222. return false;
  2223. },
  2224. _updateVirtualBoundaries: function(forceAspectRatio) {
  2225. var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
  2226. o = this.options;
  2227. b = {
  2228. minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
  2229. maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
  2230. minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
  2231. maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
  2232. };
  2233. if(this._aspectRatio || forceAspectRatio) {
  2234. // We want to create an enclosing box whose aspect ration is the requested one
  2235. // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
  2236. pMinWidth = b.minHeight * this.aspectRatio;
  2237. pMinHeight = b.minWidth / this.aspectRatio;
  2238. pMaxWidth = b.maxHeight * this.aspectRatio;
  2239. pMaxHeight = b.maxWidth / this.aspectRatio;
  2240. if(pMinWidth > b.minWidth) {
  2241. b.minWidth = pMinWidth;
  2242. }
  2243. if(pMinHeight > b.minHeight) {
  2244. b.minHeight = pMinHeight;
  2245. }
  2246. if(pMaxWidth < b.maxWidth) {
  2247. b.maxWidth = pMaxWidth;
  2248. }
  2249. if(pMaxHeight < b.maxHeight) {
  2250. b.maxHeight = pMaxHeight;
  2251. }
  2252. }
  2253. this._vBoundaries = b;
  2254. },
  2255. _updateCache: function(data) {
  2256. this.offset = this.helper.offset();
  2257. if (isNumber(data.left)) {
  2258. this.position.left = data.left;
  2259. }
  2260. if (isNumber(data.top)) {
  2261. this.position.top = data.top;
  2262. }
  2263. if (isNumber(data.height)) {
  2264. this.size.height = data.height;
  2265. }
  2266. if (isNumber(data.width)) {
  2267. this.size.width = data.width;
  2268. }
  2269. },
  2270. _updateRatio: function( data ) {
  2271. var cpos = this.position,
  2272. csize = this.size,
  2273. a = this.axis;
  2274. if (isNumber(data.height)) {
  2275. data.width = (data.height * this.aspectRatio);
  2276. } else if (isNumber(data.width)) {
  2277. data.height = (data.width / this.aspectRatio);
  2278. }
  2279. if (a === "sw") {
  2280. data.left = cpos.left + (csize.width - data.width);
  2281. data.top = null;
  2282. }
  2283. if (a === "nw") {
  2284. data.top = cpos.top + (csize.height - data.height);
  2285. data.left = cpos.left + (csize.width - data.width);
  2286. }
  2287. return data;
  2288. },
  2289. _respectSize: function( data ) {
  2290. var o = this._vBoundaries,
  2291. a = this.axis,
  2292. ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
  2293. isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height),
  2294. dw = this.originalPosition.left + this.originalSize.width,
  2295. dh = this.position.top + this.size.height,
  2296. cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
  2297. if (isminw) {
  2298. data.width = o.minWidth;
  2299. }
  2300. if (isminh) {
  2301. data.height = o.minHeight;
  2302. }
  2303. if (ismaxw) {
  2304. data.width = o.maxWidth;
  2305. }
  2306. if (ismaxh) {
  2307. data.height = o.maxHeight;
  2308. }
  2309. if (isminw && cw) {
  2310. data.left = dw - o.minWidth;
  2311. }
  2312. if (ismaxw && cw) {
  2313. data.left = dw - o.maxWidth;
  2314. }
  2315. if (isminh && ch) {
  2316. data.top = dh - o.minHeight;
  2317. }
  2318. if (ismaxh && ch) {
  2319. data.top = dh - o.maxHeight;
  2320. }
  2321. // fixing jump error on top/left - bug #2330
  2322. if (!data.width && !data.height && !data.left && data.top) {
  2323. data.top = null;
  2324. } else if (!data.width && !data.height && !data.top && data.left) {
  2325. data.left = null;
  2326. }
  2327. return data;
  2328. },
  2329. _proportionallyResize: function() {
  2330. if (!this._proportionallyResizeElements.length) {
  2331. return;
  2332. }
  2333. var i, j, borders, paddings, prel,
  2334. element = this.helper || this.element;
  2335. for ( i=0; i < this._proportionallyResizeElements.length; i++) {
  2336. prel = this._proportionallyResizeElements[i];
  2337. if (!this.borderDif) {
  2338. this.borderDif = [];
  2339. borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")];
  2340. paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")];
  2341. for ( j = 0; j < borders.length; j++ ) {
  2342. this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 );
  2343. }
  2344. }
  2345. prel.css({
  2346. height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
  2347. width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
  2348. });
  2349. }
  2350. },
  2351. _renderProxy: function() {
  2352. var el = this.element, o = this.options;
  2353. this.elementOffset = el.offset();
  2354. if(this._helper) {
  2355. this.helper = this.helper || $("<div style='overflow:hidden;'></div>");
  2356. this.helper.addClass(this._helper).css({
  2357. width: this.element.outerWidth() - 1,
  2358. height: this.element.outerHeight() - 1,
  2359. position: "absolute",
  2360. left: this.elementOffset.left +"px",
  2361. top: this.elementOffset.top +"px",
  2362. zIndex: ++o.zIndex //TODO: Don't modify option
  2363. });
  2364. this.helper
  2365. .appendTo("body")
  2366. .disableSelection();
  2367. } else {
  2368. this.helper = this.element;
  2369. }
  2370. },
  2371. _change: {
  2372. e: function(event, dx) {
  2373. return { width: this.originalSize.width + dx };
  2374. },
  2375. w: function(event, dx) {
  2376. var cs = this.originalSize, sp = this.originalPosition;
  2377. return { left: sp.left + dx, width: cs.width - dx };
  2378. },
  2379. n: function(event, dx, dy) {
  2380. var cs = this.originalSize, sp = this.originalPosition;
  2381. return { top: sp.top + dy, height: cs.height - dy };
  2382. },
  2383. s: function(event, dx, dy) {
  2384. return { height: this.originalSize.height + dy };
  2385. },
  2386. se: function(event, dx, dy) {
  2387. return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
  2388. },
  2389. sw: function(event, dx, dy) {
  2390. return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
  2391. },
  2392. ne: function(event, dx, dy) {
  2393. return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
  2394. },
  2395. nw: function(event, dx, dy) {
  2396. return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
  2397. }
  2398. },
  2399. _propagate: function(n, event) {
  2400. $.ui.plugin.call(this, n, [event, this.ui()]);
  2401. (n !== "resize" && this._trigger(n, event, this.ui()));
  2402. },
  2403. plugins: {},
  2404. ui: function() {
  2405. return {
  2406. originalElement: this.originalElement,
  2407. element: this.element,
  2408. helper: this.helper,
  2409. position: this.position,
  2410. size: this.size,
  2411. originalSize: this.originalSize,
  2412. originalPosition: this.originalPosition
  2413. };
  2414. }
  2415. });
  2416. /*
  2417. * Resizable Extensions
  2418. */
  2419. $.ui.plugin.add("resizable", "animate", {
  2420. stop: function( event ) {
  2421. var that = $(this).data("ui-resizable"),
  2422. o = that.options,
  2423. pr = that._proportionallyResizeElements,
  2424. ista = pr.length && (/textarea/i).test(pr[0].nodeName),
  2425. soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height,
  2426. soffsetw = ista ? 0 : that.sizeDiff.width,
  2427. style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
  2428. left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null,
  2429. top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
  2430. that.element.animate(
  2431. $.extend(style, top && left ? { top: top, left: left } : {}), {
  2432. duration: o.animateDuration,
  2433. easing: o.animateEasing,
  2434. step: function() {
  2435. var data = {
  2436. width: parseInt(that.element.css("width"), 10),
  2437. height: parseInt(that.element.css("height"), 10),
  2438. top: parseInt(that.element.css("top"), 10),
  2439. left: parseInt(that.element.css("left"), 10)
  2440. };
  2441. if (pr && pr.length) {
  2442. $(pr[0]).css({ width: data.width, height: data.height });
  2443. }
  2444. // propagating resize, and updating values for each animation step
  2445. that._updateCache(data);
  2446. that._propagate("resize", event);
  2447. }
  2448. }
  2449. );
  2450. }
  2451. });
  2452. $.ui.plugin.add("resizable", "containment", {
  2453. start: function() {
  2454. var element, p, co, ch, cw, width, height,
  2455. that = $(this).data("ui-resizable"),
  2456. o = that.options,
  2457. el = that.element,
  2458. oc = o.containment,
  2459. ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
  2460. if (!ce) {
  2461. return;
  2462. }
  2463. that.containerElement = $(ce);
  2464. if (/document/.test(oc) || oc === document) {
  2465. that.containerOffset = { left: 0, top: 0 };
  2466. that.containerPosition = { left: 0, top: 0 };
  2467. that.parentData = {
  2468. element: $(document), left: 0, top: 0,
  2469. width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
  2470. };
  2471. }
  2472. // i'm a node, so compute top, left, right, bottom
  2473. else {
  2474. element = $(ce);
  2475. p = [];
  2476. $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
  2477. that.containerOffset = element.offset();
  2478. that.containerPosition = element.position();
  2479. that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
  2480. co = that.containerOffset;
  2481. ch = that.containerSize.height;
  2482. cw = that.containerSize.width;
  2483. width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw );
  2484. height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
  2485. that.parentData = {
  2486. element: ce, left: co.left, top: co.top, width: width, height: height
  2487. };
  2488. }
  2489. },
  2490. resize: function( event ) {
  2491. var woset, hoset, isParent, isOffsetRelative,
  2492. that = $(this).data("ui-resizable"),
  2493. o = that.options,
  2494. co = that.containerOffset, cp = that.position,
  2495. pRatio = that._aspectRatio || event.shiftKey,
  2496. cop = { top:0, left:0 }, ce = that.containerElement;
  2497. if (ce[0] !== document && (/static/).test(ce.css("position"))) {
  2498. cop = co;
  2499. }
  2500. if (cp.left < (that._helper ? co.left : 0)) {
  2501. that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left));
  2502. if (pRatio) {
  2503. that.size.height = that.size.width / that.aspectRatio;
  2504. }
  2505. that.position.left = o.helper ? co.left : 0;
  2506. }
  2507. if (cp.top < (that._helper ? co.top : 0)) {
  2508. that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top);
  2509. if (pRatio) {
  2510. that.size.width = that.size.height * that.aspectRatio;
  2511. }
  2512. that.position.top = that._helper ? co.top : 0;
  2513. }
  2514. that.offset.left = that.parentData.left+that.position.left;
  2515. that.offset.top = that.parentData.top+that.position.top;
  2516. woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width );
  2517. hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height );
  2518. isParent = that.containerElement.get(0) === that.element.parent().get(0);
  2519. isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position"));
  2520. if(isParent && isOffsetRelative) {
  2521. woset -= that.parentData.left;
  2522. }
  2523. if (woset + that.size.width >= that.parentData.width) {
  2524. that.size.width = that.parentData.width - woset;
  2525. if (pRatio) {
  2526. that.size.height = that.size.width / that.aspectRatio;
  2527. }
  2528. }
  2529. if (hoset + that.size.height >= that.parentData.height) {
  2530. that.size.height = that.parentData.height - hoset;
  2531. if (pRatio) {
  2532. that.size.width = that.size.height * that.aspectRatio;
  2533. }
  2534. }
  2535. },
  2536. stop: function(){
  2537. var that = $(this).data("ui-resizable"),
  2538. o = that.options,
  2539. co = that.containerOffset,
  2540. cop = that.containerPosition,
  2541. ce = that.containerElement,
  2542. helper = $(that.helper),
  2543. ho = helper.offset(),
  2544. w = helper.outerWidth() - that.sizeDiff.width,
  2545. h = helper.outerHeight() - that.sizeDiff.height;
  2546. if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) {
  2547. $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
  2548. }
  2549. if (that._helper && !o.animate && (/static/).test(ce.css("position"))) {
  2550. $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
  2551. }
  2552. }
  2553. });
  2554. $.ui.plugin.add("resizable", "alsoResize", {
  2555. start: function () {
  2556. var that = $(this).data("ui-resizable"),
  2557. o = that.options,
  2558. _store = function (exp) {
  2559. $(exp).each(function() {
  2560. var el = $(this);
  2561. el.data("ui-resizable-alsoresize", {
  2562. width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
  2563. left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
  2564. });
  2565. });
  2566. };
  2567. if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) {
  2568. if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
  2569. else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
  2570. }else{
  2571. _store(o.alsoResize);
  2572. }
  2573. },
  2574. resize: function (event, ui) {
  2575. var that = $(this).data("ui-resizable"),
  2576. o = that.options,
  2577. os = that.originalSize,
  2578. op = that.originalPosition,
  2579. delta = {
  2580. height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0,
  2581. top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0
  2582. },
  2583. _alsoResize = function (exp, c) {
  2584. $(exp).each(function() {
  2585. var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
  2586. css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"];
  2587. $.each(css, function (i, prop) {
  2588. var sum = (start[prop]||0) + (delta[prop]||0);
  2589. if (sum && sum >= 0) {
  2590. style[prop] = sum || null;
  2591. }
  2592. });
  2593. el.css(style);
  2594. });
  2595. };
  2596. if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) {
  2597. $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
  2598. }else{
  2599. _alsoResize(o.alsoResize);
  2600. }
  2601. },
  2602. stop: function () {
  2603. $(this).removeData("resizable-alsoresize");
  2604. }
  2605. });
  2606. $.ui.plugin.add("resizable", "ghost", {
  2607. start: function() {
  2608. var that = $(this).data("ui-resizable"), o = that.options, cs = that.size;
  2609. that.ghost = that.originalElement.clone();
  2610. that.ghost
  2611. .css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
  2612. .addClass("ui-resizable-ghost")
  2613. .addClass(typeof o.ghost === "string" ? o.ghost : "");
  2614. that.ghost.appendTo(that.helper);
  2615. },
  2616. resize: function(){
  2617. var that = $(this).data("ui-resizable");
  2618. if (that.ghost) {
  2619. that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width });
  2620. }
  2621. },
  2622. stop: function() {
  2623. var that = $(this).data("ui-resizable");
  2624. if (that.ghost && that.helper) {
  2625. that.helper.get(0).removeChild(that.ghost.get(0));
  2626. }
  2627. }
  2628. });
  2629. $.ui.plugin.add("resizable", "grid", {
  2630. resize: function() {
  2631. var that = $(this).data("ui-resizable"),
  2632. o = that.options,
  2633. cs = that.size,
  2634. os = that.originalSize,
  2635. op = that.originalPosition,
  2636. a = that.axis,
  2637. grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid,
  2638. gridX = (grid[0]||1),
  2639. gridY = (grid[1]||1),
  2640. ox = Math.round((cs.width - os.width) / gridX) * gridX,
  2641. oy = Math.round((cs.height - os.height) / gridY) * gridY,
  2642. newWidth = os.width + ox,
  2643. newHeight = os.height + oy,
  2644. isMaxWidth = o.maxWidth && (o.maxWidth < newWidth),
  2645. isMaxHeight = o.maxHeight && (o.maxHeight < newHeight),
  2646. isMinWidth = o.minWidth && (o.minWidth > newWidth),
  2647. isMinHeight = o.minHeight && (o.minHeight > newHeight);
  2648. o.grid = grid;
  2649. if (isMinWidth) {
  2650. newWidth = newWidth + gridX;
  2651. }
  2652. if (isMinHeight) {
  2653. newHeight = newHeight + gridY;
  2654. }
  2655. if (isMaxWidth) {
  2656. newWidth = newWidth - gridX;
  2657. }
  2658. if (isMaxHeight) {
  2659. newHeight = newHeight - gridY;
  2660. }
  2661. if (/^(se|s|e)$/.test(a)) {
  2662. that.size.width = newWidth;
  2663. that.size.height = newHeight;
  2664. } else if (/^(ne)$/.test(a)) {
  2665. that.size.width = newWidth;
  2666. that.size.height = newHeight;
  2667. that.position.top = op.top - oy;
  2668. } else if (/^(sw)$/.test(a)) {
  2669. that.size.width = newWidth;
  2670. that.size.height = newHeight;
  2671. that.position.left = op.left - ox;
  2672. } else {
  2673. that.size.width = newWidth;
  2674. that.size.height = newHeight;
  2675. that.position.top = op.top - oy;
  2676. that.position.left = op.left - ox;
  2677. }
  2678. }
  2679. });
  2680. })(jQuery);
  2681. /**
  2682. * @preserve
  2683. * jquery.layout 1.3.0 - Release Candidate 30.79
  2684. * $Date: 2013-01-12 08:00:00 (Sat, 12 Jan 2013) $
  2685. * $Rev: 303007 $
  2686. *
  2687. * Copyright (c) 2013 Kevin Dalman (http://allpro.net)
  2688. * Based on work by Fabrizio Balliano (http://www.fabrizioballiano.net)
  2689. *
  2690. * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
  2691. * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
  2692. *
  2693. * SEE: http://layout.jquery-dev.net/LICENSE.txt
  2694. *
  2695. * Changelog: http://layout.jquery-dev.net/changelog.cfm#1.3.0.rc30.79
  2696. *
  2697. * Docs: http://layout.jquery-dev.net/documentation.html
  2698. * Tips: http://layout.jquery-dev.net/tips.html
  2699. * Help: http://groups.google.com/group/jquery-ui-layout
  2700. */
  2701. /* JavaDoc Info: http://code.google.com/closure/compiler/docs/js-for-compiler.html
  2702. * {!Object} non-nullable type (never NULL)
  2703. * {?string} nullable type (sometimes NULL) - default for {Object}
  2704. * {number=} optional parameter
  2705. * {*} ALL types
  2706. */
  2707. /* TODO for jQ 2.0
  2708. * change .andSelf() to .addBack()
  2709. * $.fn.disableSelection won't work
  2710. */
  2711. // NOTE: For best readability, view with a fixed-width font and tabs equal to 4-chars
  2712. ;(function ($) {
  2713. // alias Math methods - used a lot!
  2714. var min = Math.min
  2715. , max = Math.max
  2716. , round = Math.floor
  2717. , isStr = function (v) { return $.type(v) === "string"; }
  2718. /**
  2719. * @param {!Object} Instance
  2720. * @param {Array.<string>} a_fn
  2721. */
  2722. , runPluginCallbacks = function (Instance, a_fn) {
  2723. if ($.isArray(a_fn))
  2724. for (var i=0, c=a_fn.length; i<c; i++) {
  2725. var fn = a_fn[i];
  2726. try {
  2727. if (isStr(fn)) // 'name' of a function
  2728. fn = eval(fn);
  2729. if ($.isFunction(fn))
  2730. g(fn)( Instance );
  2731. } catch (ex) {}
  2732. }
  2733. function g (f) { return f; }; // compiler hack
  2734. }
  2735. ;
  2736. /*
  2737. * GENERIC $.layout METHODS - used by all layouts
  2738. */
  2739. $.layout = {
  2740. version: "1.3.rc30.79"
  2741. , revision: 0.033007 // 1.3.0 final = 1.0300 - major(n+).minor(nn)+patch(nn+)
  2742. // $.layout.browser REPLACES $.browser
  2743. , browser: {} // set below
  2744. // *PREDEFINED* EFFECTS & DEFAULTS
  2745. // MUST list effect here - OR MUST set an fxSettings option (can be an empty hash: {})
  2746. , effects: {
  2747. // Pane Open/Close Animations
  2748. slide: {
  2749. all: { duration: "fast" } // eg: duration: 1000, easing: "easeOutBounce"
  2750. , north: { direction: "up" }
  2751. , south: { direction: "down" }
  2752. , east: { direction: "right"}
  2753. , west: { direction: "left" }
  2754. }
  2755. , drop: {
  2756. all: { duration: "slow" }
  2757. , north: { direction: "up" }
  2758. , south: { direction: "down" }
  2759. , east: { direction: "right"}
  2760. , west: { direction: "left" }
  2761. }
  2762. , scale: {
  2763. all: { duration: "fast" }
  2764. }
  2765. // these are not recommended, but can be used
  2766. , blind: {}
  2767. , clip: {}
  2768. , explode: {}
  2769. , fade: {}
  2770. , fold: {}
  2771. , puff: {}
  2772. // Pane Resize Animations
  2773. , size: {
  2774. all: { easing: "swing" }
  2775. }
  2776. }
  2777. // INTERNAL CONFIG DATA - DO NOT CHANGE THIS!
  2778. , config: {
  2779. optionRootKeys: "effects,panes,north,south,west,east,center".split(",")
  2780. , allPanes: "north,south,west,east,center".split(",")
  2781. , borderPanes: "north,south,west,east".split(",")
  2782. , oppositeEdge: {
  2783. north: "south"
  2784. , south: "north"
  2785. , east: "west"
  2786. , west: "east"
  2787. }
  2788. // offscreen data
  2789. , offscreenCSS: { left: "-99999px", right: "auto" } // used by hide/close if useOffscreenClose=true
  2790. , offscreenReset: "offscreenReset" // key used for data
  2791. // CSS used in multiple places
  2792. , hidden: { visibility: "hidden" }
  2793. , visible: { visibility: "visible" }
  2794. // layout element settings
  2795. , resizers: {
  2796. cssReq: {
  2797. position: "absolute"
  2798. , padding: 0
  2799. , margin: 0
  2800. , fontSize: "1px"
  2801. , textAlign: "left" // to counter-act "center" alignment!
  2802. , overflow: "hidden" // prevent toggler-button from overflowing
  2803. // SEE $.layout.defaults.zIndexes.resizer_normal
  2804. }
  2805. , cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
  2806. background: "#DDD"
  2807. , border: "none"
  2808. }
  2809. }
  2810. , togglers: {
  2811. cssReq: {
  2812. position: "absolute"
  2813. , display: "block"
  2814. , padding: 0
  2815. , margin: 0
  2816. , overflow: "hidden"
  2817. , textAlign: "center"
  2818. , fontSize: "1px"
  2819. , cursor: "pointer"
  2820. , zIndex: 1
  2821. }
  2822. , cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
  2823. background: "#AAA"
  2824. }
  2825. }
  2826. , content: {
  2827. cssReq: {
  2828. position: "relative" /* contain floated or positioned elements */
  2829. }
  2830. , cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
  2831. overflow: "auto"
  2832. , padding: "10px"
  2833. }
  2834. , cssDemoPane: { // DEMO CSS - REMOVE scrolling from 'pane' when it has a content-div
  2835. overflow: "hidden"
  2836. , padding: 0
  2837. }
  2838. }
  2839. , panes: { // defaults for ALL panes - overridden by 'per-pane settings' below
  2840. cssReq: {
  2841. position: "absolute"
  2842. , margin: 0
  2843. // $.layout.defaults.zIndexes.pane_normal
  2844. }
  2845. , cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
  2846. padding: "10px"
  2847. , background: "#FFF"
  2848. , border: "1px solid #BBB"
  2849. , overflow: "auto"
  2850. }
  2851. }
  2852. , north: {
  2853. side: "top"
  2854. , sizeType: "Height"
  2855. , dir: "horz"
  2856. , cssReq: {
  2857. top: 0
  2858. , bottom: "auto"
  2859. , left: 0
  2860. , right: 0
  2861. , width: "auto"
  2862. // height: DYNAMIC
  2863. }
  2864. }
  2865. , south: {
  2866. side: "bottom"
  2867. , sizeType: "Height"
  2868. , dir: "horz"
  2869. , cssReq: {
  2870. top: "auto"
  2871. , bottom: 0
  2872. , left: 0
  2873. , right: 0
  2874. , width: "auto"
  2875. // height: DYNAMIC
  2876. }
  2877. }
  2878. , east: {
  2879. side: "right"
  2880. , sizeType: "Width"
  2881. , dir: "vert"
  2882. , cssReq: {
  2883. left: "auto"
  2884. , right: 0
  2885. , top: "auto" // DYNAMIC
  2886. , bottom: "auto" // DYNAMIC
  2887. , height: "auto"
  2888. // width: DYNAMIC
  2889. }
  2890. }
  2891. , west: {
  2892. side: "left"
  2893. , sizeType: "Width"
  2894. , dir: "vert"
  2895. , cssReq: {
  2896. left: 0
  2897. , right: "auto"
  2898. , top: "auto" // DYNAMIC
  2899. , bottom: "auto" // DYNAMIC
  2900. , height: "auto"
  2901. // width: DYNAMIC
  2902. }
  2903. }
  2904. , center: {
  2905. dir: "center"
  2906. , cssReq: {
  2907. left: "auto" // DYNAMIC
  2908. , right: "auto" // DYNAMIC
  2909. , top: "auto" // DYNAMIC
  2910. , bottom: "auto" // DYNAMIC
  2911. , height: "auto"
  2912. , width: "auto"
  2913. }
  2914. }
  2915. }
  2916. // CALLBACK FUNCTION NAMESPACE - used to store reusable callback functions
  2917. , callbacks: {}
  2918. , getParentPaneElem: function (el) {
  2919. // must pass either a container or pane element
  2920. var $el = $(el)
  2921. , layout = $el.data("layout") || $el.data("parentLayout");
  2922. if (layout) {
  2923. var $cont = layout.container;
  2924. // see if this container is directly-nested inside an outer-pane
  2925. if ($cont.data("layoutPane")) return $cont;
  2926. var $pane = $cont.closest("."+ $.layout.defaults.panes.paneClass);
  2927. // if a pane was found, return it
  2928. if ($pane.data("layoutPane")) return $pane;
  2929. }
  2930. return null;
  2931. }
  2932. , getParentPaneInstance: function (el) {
  2933. // must pass either a container or pane element
  2934. var $pane = $.layout.getParentPaneElem(el);
  2935. return $pane ? $pane.data("layoutPane") : null;
  2936. }
  2937. , getParentLayoutInstance: function (el) {
  2938. // must pass either a container or pane element
  2939. var $pane = $.layout.getParentPaneElem(el);
  2940. return $pane ? $pane.data("parentLayout") : null;
  2941. }
  2942. , getEventObject: function (evt) {
  2943. return typeof evt === "object" && evt.stopPropagation ? evt : null;
  2944. }
  2945. , parsePaneName: function (evt_or_pane) {
  2946. var evt = $.layout.getEventObject( evt_or_pane )
  2947. , pane = evt_or_pane;
  2948. if (evt) {
  2949. // ALWAYS stop propagation of events triggered in Layout!
  2950. evt.stopPropagation();
  2951. pane = $(this).data("layoutEdge");
  2952. }
  2953. if (pane && !/^(west|east|north|south|center)$/.test(pane)) {
  2954. $.layout.msg('LAYOUT ERROR - Invalid pane-name: "'+ pane +'"');
  2955. pane = "error";
  2956. }
  2957. return pane;
  2958. }
  2959. // LAYOUT-PLUGIN REGISTRATION
  2960. // more plugins can added beyond this default list
  2961. , plugins: {
  2962. draggable: !!$.fn.draggable // resizing
  2963. , effects: {
  2964. core: !!$.effects // animimations (specific effects tested by initOptions)
  2965. , slide: $.effects && ($.effects.slide || ($.effects.effect && $.effects.effect.slide)) // default effect
  2966. }
  2967. }
  2968. // arrays of plugin or other methods to be triggered for events in *each layout* - will be passed 'Instance'
  2969. , onCreate: [] // runs when layout is just starting to be created - right after options are set
  2970. , onLoad: [] // runs after layout container and global events init, but before initPanes is called
  2971. , onReady: [] // runs after initialization *completes* - ie, after initPanes completes successfully
  2972. , onDestroy: [] // runs after layout is destroyed
  2973. , onUnload: [] // runs after layout is destroyed OR when page unloads
  2974. , afterOpen: [] // runs after setAsOpen() completes
  2975. , afterClose: [] // runs after setAsClosed() completes
  2976. /*
  2977. * GENERIC UTILITY METHODS
  2978. */
  2979. // calculate and return the scrollbar width, as an integer
  2980. , scrollbarWidth: function () { return window.scrollbarWidth || $.layout.getScrollbarSize('width'); }
  2981. , scrollbarHeight: function () { return window.scrollbarHeight || $.layout.getScrollbarSize('height'); }
  2982. , getScrollbarSize: function (dim) {
  2983. var $c = $('<div style="position: absolute; top: -10000px; left: -10000px; width: 100px; height: 100px; overflow: scroll;"></div>').appendTo("body");
  2984. var d = { width: $c.css("width") - $c[0].clientWidth, height: $c.height() - $c[0].clientHeight };
  2985. $c.remove();
  2986. window.scrollbarWidth = d.width;
  2987. window.scrollbarHeight = d.height;
  2988. return dim.match(/^(width|height)$/) ? d[dim] : d;
  2989. }
  2990. /**
  2991. * Returns hash container 'display' and 'visibility'
  2992. *
  2993. * @see $.swap() - swaps CSS, runs callback, resets CSS
  2994. * @param {!Object} $E jQuery element
  2995. * @param {boolean=} [force=false] Run even if display != none
  2996. * @return {!Object} Returns current style props, if applicable
  2997. */
  2998. , showInvisibly: function ($E, force) {
  2999. if ($E && $E.length && (force || $E.css("display") === "none")) { // only if not *already hidden*
  3000. var s = $E[0].style
  3001. // save ONLY the 'style' props because that is what we must restore
  3002. , CSS = { display: s.display || '', visibility: s.visibility || '' };
  3003. // show element 'invisibly' so can be measured
  3004. $E.css({ display: "block", visibility: "hidden" });
  3005. return CSS;
  3006. }
  3007. return {};
  3008. }
  3009. /**
  3010. * Returns data for setting size of an element (container or a pane).
  3011. *
  3012. * @see _create(), onWindowResize() for container, plus others for pane
  3013. * @return JSON Returns a hash of all dimensions: top, bottom, left, right, outerWidth, innerHeight, etc
  3014. */
  3015. , getElementDimensions: function ($E, inset) {
  3016. var
  3017. // dimensions hash - start with current data IF passed
  3018. d = { css: {}, inset: {} }
  3019. , x = d.css // CSS hash
  3020. , i = { bottom: 0 } // TEMP insets (bottom = complier hack)
  3021. , N = $.layout.cssNum
  3022. , off = $E.offset()
  3023. , b, p, ei // TEMP border, padding
  3024. ;
  3025. d.offsetLeft = off.left;
  3026. d.offsetTop = off.top;
  3027. if (!inset) inset = {}; // simplify logic below
  3028. $.each("Left,Right,Top,Bottom".split(","), function (idx, e) { // e = edge
  3029. b = x["border" + e] = $.layout.borderWidth($E, e);
  3030. p = x["padding"+ e] = $.layout.cssNum($E, "padding"+e);
  3031. ei = e.toLowerCase();
  3032. d.inset[ei] = inset[ei] >= 0 ? inset[ei] : p; // any missing insetX value = paddingX
  3033. i[ei] = d.inset[ei] + b; // total offset of content from outer side
  3034. });
  3035. x.width = $E.width();
  3036. x.height = $E.height();
  3037. x.top = N($E,"top",true);
  3038. x.bottom = N($E,"bottom",true);
  3039. x.left = N($E,"left",true);
  3040. x.right = N($E,"right",true);
  3041. d.outerWidth = $E.outerWidth();
  3042. d.outerHeight = $E.outerHeight();
  3043. // calc the TRUE inner-dimensions, even in quirks-mode!
  3044. d.innerWidth = max(0, d.outerWidth - i.left - i.right);
  3045. d.innerHeight = max(0, d.outerHeight - i.top - i.bottom);
  3046. // layoutWidth/Height is used in calcs for manual resizing
  3047. // layoutW/H only differs from innerW/H when in quirks-mode - then is like outerW/H
  3048. d.layoutWidth = $E.innerWidth();
  3049. d.layoutHeight = $E.innerHeight();
  3050. //if ($E.prop('tagName') === 'BODY') { debugData( d, $E.prop('tagName') ); } // DEBUG
  3051. //d.visible = $E.is(":visible");// && x.width > 0 && x.height > 0;
  3052. return d;
  3053. }
  3054. , getElementStyles: function ($E, list) {
  3055. var
  3056. CSS = {}
  3057. , style = $E[0].style
  3058. , props = list.split(",")
  3059. , sides = "Top,Bottom,Left,Right".split(",")
  3060. , attrs = "Color,Style,Width".split(",")
  3061. , p, s, a, i, j, k
  3062. ;
  3063. for (i=0; i < props.length; i++) {
  3064. p = props[i];
  3065. if (p.match(/(border|padding|margin)$/))
  3066. for (j=0; j < 4; j++) {
  3067. s = sides[j];
  3068. if (p === "border")
  3069. for (k=0; k < 3; k++) {
  3070. a = attrs[k];
  3071. CSS[p+s+a] = style[p+s+a];
  3072. }
  3073. else
  3074. CSS[p+s] = style[p+s];
  3075. }
  3076. else
  3077. CSS[p] = style[p];
  3078. };
  3079. return CSS
  3080. }
  3081. /**
  3082. * Return the innerWidth for the current browser/doctype
  3083. *
  3084. * @see initPanes(), sizeMidPanes(), initHandles(), sizeHandles()
  3085. * @param {Array.<Object>} $E Must pass a jQuery object - first element is processed
  3086. * @param {number=} outerWidth (optional) Can pass a width, allowing calculations BEFORE element is resized
  3087. * @return {number} Returns the innerWidth of the elem by subtracting padding and borders
  3088. */
  3089. , cssWidth: function ($E, outerWidth) {
  3090. // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
  3091. if (outerWidth <= 0) return 0;
  3092. var bs = !$.layout.browser.boxModel ? "border-box" : $.support.boxSizing ? $E.css("boxSizing") : "content-box"
  3093. , b = $.layout.borderWidth
  3094. , n = $.layout.cssNum
  3095. , W = outerWidth
  3096. ;
  3097. // strip border and/or padding from outerWidth to get CSS Width
  3098. if (bs !== "border-box")
  3099. W -= (b($E, "Left") + b($E, "Right"));
  3100. if (bs === "content-box")
  3101. W -= (n($E, "paddingLeft") + n($E, "paddingRight"));
  3102. return max(0,W);
  3103. }
  3104. /**
  3105. * Return the innerHeight for the current browser/doctype
  3106. *
  3107. * @see initPanes(), sizeMidPanes(), initHandles(), sizeHandles()
  3108. * @param {Array.<Object>} $E Must pass a jQuery object - first element is processed
  3109. * @param {number=} outerHeight (optional) Can pass a width, allowing calculations BEFORE element is resized
  3110. * @return {number} Returns the innerHeight of the elem by subtracting padding and borders
  3111. */
  3112. , cssHeight: function ($E, outerHeight) {
  3113. // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
  3114. if (outerHeight <= 0) return 0;
  3115. var bs = !$.layout.browser.boxModel ? "border-box" : $.support.boxSizing ? $E.css("boxSizing") : "content-box"
  3116. , b = $.layout.borderWidth
  3117. , n = $.layout.cssNum
  3118. , H = outerHeight
  3119. ;
  3120. // strip border and/or padding from outerHeight to get CSS Height
  3121. if (bs !== "border-box")
  3122. H -= (b($E, "Top") + b($E, "Bottom"));
  3123. if (bs === "content-box")
  3124. H -= (n($E, "paddingTop") + n($E, "paddingBottom"));
  3125. return max(0,H);
  3126. }
  3127. /**
  3128. * Returns the 'current CSS numeric value' for a CSS property - 0 if property does not exist
  3129. *
  3130. * @see Called by many methods
  3131. * @param {Array.<Object>} $E Must pass a jQuery object - first element is processed
  3132. * @param {string} prop The name of the CSS property, eg: top, width, etc.
  3133. * @param {boolean=} [allowAuto=false] true = return 'auto' if that is value; false = return 0
  3134. * @return {(string|number)} Usually used to get an integer value for position (top, left) or size (height, width)
  3135. */
  3136. , cssNum: function ($E, prop, allowAuto) {
  3137. if (!$E.jquery) $E = $($E);
  3138. var CSS = $.layout.showInvisibly($E)
  3139. , p = $.css($E[0], prop, true)
  3140. , v = allowAuto && p=="auto" ? p : Math.round(parseFloat(p) || 0);
  3141. $E.css( CSS ); // RESET
  3142. return v;
  3143. }
  3144. , borderWidth: function (el, side) {
  3145. if (el.jquery) el = el[0];
  3146. var b = "border"+ side.substr(0,1).toUpperCase() + side.substr(1); // left => Left
  3147. return $.css(el, b+"Style", true) === "none" ? 0 : Math.round(parseFloat($.css(el, b+"Width", true)) || 0);
  3148. }
  3149. /**
  3150. * Mouse-tracking utility - FUTURE REFERENCE
  3151. *
  3152. * init: if (!window.mouse) {
  3153. * window.mouse = { x: 0, y: 0 };
  3154. * $(document).mousemove( $.layout.trackMouse );
  3155. * }
  3156. *
  3157. * @param {Object} evt
  3158. *
  3159. , trackMouse: function (evt) {
  3160. window.mouse = { x: evt.clientX, y: evt.clientY };
  3161. }
  3162. */
  3163. /**
  3164. * SUBROUTINE for preventPrematureSlideClose option
  3165. *
  3166. * @param {Object} evt
  3167. * @param {Object=} el
  3168. */
  3169. , isMouseOverElem: function (evt, el) {
  3170. var
  3171. $E = $(el || this)
  3172. , d = $E.offset()
  3173. , T = d.top
  3174. , L = d.left
  3175. , R = L + $E.outerWidth()
  3176. , B = T + $E.outerHeight()
  3177. , x = evt.pageX // evt.clientX ?
  3178. , y = evt.pageY // evt.clientY ?
  3179. ;
  3180. // if X & Y are < 0, probably means is over an open SELECT
  3181. return ($.layout.browser.msie && x < 0 && y < 0) || ((x >= L && x <= R) && (y >= T && y <= B));
  3182. }
  3183. /**
  3184. * Message/Logging Utility
  3185. *
  3186. * @example $.layout.msg("My message"); // log text
  3187. * @example $.layout.msg("My message", true); // alert text
  3188. * @example $.layout.msg({ foo: "bar" }, "Title"); // log hash-data, with custom title
  3189. * @example $.layout.msg({ foo: "bar" }, true, "Title", { sort: false }); -OR-
  3190. * @example $.layout.msg({ foo: "bar" }, "Title", { sort: false, display: true }); // alert hash-data
  3191. *
  3192. * @param {(Object|string)} info String message OR Hash/Array
  3193. * @param {(Boolean|string|Object)=} [popup=false] True means alert-box - can be skipped
  3194. * @param {(Object|string)=} [debugTitle=""] Title for Hash data - can be skipped
  3195. * @param {Object=} [debugOpts] Extra options for debug output
  3196. */
  3197. , msg: function (info, popup, debugTitle, debugOpts) {
  3198. if ($.isPlainObject(info) && window.debugData) {
  3199. if (typeof popup === "string") {
  3200. debugOpts = debugTitle;
  3201. debugTitle = popup;
  3202. }
  3203. else if (typeof debugTitle === "object") {
  3204. debugOpts = debugTitle;
  3205. debugTitle = null;
  3206. }
  3207. var t = debugTitle || "log( <object> )"
  3208. , o = $.extend({ sort: false, returnHTML: false, display: false }, debugOpts);
  3209. if (popup === true || o.display)
  3210. debugData( info, t, o );
  3211. else if (window.console)
  3212. console.log(debugData( info, t, o ));
  3213. }
  3214. else if (popup)
  3215. alert(info);
  3216. else if (window.console)
  3217. console.log(info);
  3218. else {
  3219. var id = "#layoutLogger"
  3220. , $l = $(id);
  3221. if (!$l.length)
  3222. $l = createLog();
  3223. $l.children("ul").append('<li style="padding: 4px 10px; margin: 0; border-top: 1px solid #CCC;">'+ info.replace(/\</g,"&lt;").replace(/\>/g,"&gt;") +'</li>');
  3224. }
  3225. function createLog () {
  3226. var pos = $.support.fixedPosition ? 'fixed' : 'absolute'
  3227. , $e = $('<div id="layoutLogger" style="position: '+ pos +'; top: 5px; z-index: 999999; max-width: 25%; overflow: hidden; border: 1px solid #000; border-radius: 5px; background: #FBFBFB; box-shadow: 0 2px 10px rgba(0,0,0,0.3);">'
  3228. + '<div style="font-size: 13px; font-weight: bold; padding: 5px 10px; background: #F6F6F6; border-radius: 5px 5px 0 0; cursor: move;">'
  3229. + '<span style="float: right; padding-left: 7px; cursor: pointer;" title="Remove Console" onclick="$(this).closest(\'#layoutLogger\').remove()">X</span>Layout console.log</div>'
  3230. + '<ul style="font-size: 13px; font-weight: none; list-style: none; margin: 0; padding: 0 0 2px;"></ul>'
  3231. + '</div>'
  3232. ).appendTo("body");
  3233. $e.css('left', $(window).width() - $e.outerWidth() - 5)
  3234. if ($.ui.draggable) $e.draggable({ handle: ':first-child' });
  3235. return $e;
  3236. };
  3237. }
  3238. };
  3239. /*
  3240. * $.layout.browser REPLACES removed $.browser, with extra data
  3241. * Parsing code here adapted from jQuery 1.8 $.browse
  3242. */
  3243. var u = navigator.userAgent.toLowerCase()
  3244. , m = /(chrome)[ \/]([\w.]+)/.exec( u )
  3245. || /(webkit)[ \/]([\w.]+)/.exec( u )
  3246. || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( u )
  3247. || /(msie) ([\w.]+)/.exec( u )
  3248. || u.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( u )
  3249. || []
  3250. , b = m[1] || ""
  3251. , v = m[2] || 0
  3252. , ie = b === "msie"
  3253. ;
  3254. $.layout.browser = {
  3255. version: v
  3256. , safari: b === "webkit" // webkit (NOT chrome) = safari
  3257. , webkit: b === "chrome" // chrome = webkit
  3258. , msie: ie
  3259. , isIE6: ie && v == 6
  3260. // ONLY IE reverts to old box-model - update for older jQ onReady
  3261. , boxModel: !ie || $.support.boxModel !== false
  3262. };
  3263. if (b) $.layout.browser[b] = true; // set CURRENT browser
  3264. /* OLD versions of jQuery only set $.support.boxModel after page is loaded
  3265. * so if this is IE, use support.boxModel to test for quirks-mode (ONLY IE changes boxModel) */
  3266. if (ie) $(function(){ $.layout.browser.boxModel = $.support.boxModel; });
  3267. // DEFAULT OPTIONS
  3268. $.layout.defaults = {
  3269. /*
  3270. * LAYOUT & LAYOUT-CONTAINER OPTIONS
  3271. * - none of these options are applicable to individual panes
  3272. */
  3273. name: "" // Not required, but useful for buttons and used for the state-cookie
  3274. , containerClass: "ui-layout-container" // layout-container element
  3275. , inset: null // custom container-inset values (override padding)
  3276. , scrollToBookmarkOnLoad: true // after creating a layout, scroll to bookmark in URL (.../page.htm#myBookmark)
  3277. , resizeWithWindow: true // bind thisLayout.resizeAll() to the window.resize event
  3278. , resizeWithWindowDelay: 200 // delay calling resizeAll because makes window resizing very jerky
  3279. , resizeWithWindowMaxDelay: 0 // 0 = none - force resize every XX ms while window is being resized
  3280. , maskPanesEarly: false // true = create pane-masks on resizer.mouseDown instead of waiting for resizer.dragstart
  3281. , onresizeall_start: null // CALLBACK when resizeAll() STARTS - NOT pane-specific
  3282. , onresizeall_end: null // CALLBACK when resizeAll() ENDS - NOT pane-specific
  3283. , onload_start: null // CALLBACK when Layout inits - after options initialized, but before elements
  3284. , onload_end: null // CALLBACK when Layout inits - after EVERYTHING has been initialized
  3285. , onunload_start: null // CALLBACK when Layout is destroyed OR onWindowUnload
  3286. , onunload_end: null // CALLBACK when Layout is destroyed OR onWindowUnload
  3287. , initPanes: true // false = DO NOT initialize the panes onLoad - will init later
  3288. , showErrorMessages: true // enables fatal error messages to warn developers of common errors
  3289. , showDebugMessages: false // display console-and-alert debug msgs - IF this Layout version _has_ debugging code!
  3290. // Changing this zIndex value will cause other zIndex values to automatically change
  3291. , zIndex: null // the PANE zIndex - resizers and masks will be +1
  3292. // DO NOT CHANGE the zIndex values below unless you clearly understand their relationships
  3293. , zIndexes: { // set _default_ z-index values here...
  3294. pane_normal: 0 // normal z-index for panes
  3295. , content_mask: 1 // applied to overlays used to mask content INSIDE panes during resizing
  3296. , resizer_normal: 2 // normal z-index for resizer-bars
  3297. , pane_sliding: 100 // applied to *BOTH* the pane and its resizer when a pane is 'slid open'
  3298. , pane_animate: 1000 // applied to the pane when being animated - not applied to the resizer
  3299. , resizer_drag: 10000 // applied to the CLONED resizer-bar when being 'dragged'
  3300. }
  3301. , errors: {
  3302. pane: "pane" // description of "layout pane element" - used only in error messages
  3303. , selector: "selector" // description of "jQuery-selector" - used only in error messages
  3304. , addButtonError: "Error Adding Button\nInvalid "
  3305. , containerMissing: "UI Layout Initialization Error\nThe specified layout-container does not exist."
  3306. , centerPaneMissing: "UI Layout Initialization Error\nThe center-pane element does not exist.\nThe center-pane is a required element."
  3307. , noContainerHeight: "UI Layout Initialization Warning\nThe layout-container \"CONTAINER\" has no height.\nTherefore the layout is 0-height and hence 'invisible'!"
  3308. , callbackError: "UI Layout Callback Error\nThe EVENT callback is not a valid function."
  3309. }
  3310. /*
  3311. * PANE DEFAULT SETTINGS
  3312. * - settings under the 'panes' key become the default settings for *all panes*
  3313. * - ALL pane-options can also be set specifically for each panes, which will override these 'default values'
  3314. */
  3315. , panes: { // default options for 'all panes' - will be overridden by 'per-pane settings'
  3316. applyDemoStyles: false // NOTE: renamed from applyDefaultStyles for clarity
  3317. , closable: true // pane can open & close
  3318. , resizable: true // when open, pane can be resized
  3319. , slidable: true // when closed, pane can 'slide open' over other panes - closes on mouse-out
  3320. , initClosed: false // true = init pane as 'closed'
  3321. , initHidden: false // true = init pane as 'hidden' - no resizer-bar/spacing
  3322. // SELECTORS
  3323. //, paneSelector: "" // MUST be pane-specific - jQuery selector for pane
  3324. , contentSelector: ".ui-layout-content" // INNER div/element to auto-size so only it scrolls, not the entire pane!
  3325. , contentIgnoreSelector: ".ui-layout-ignore" // element(s) to 'ignore' when measuring 'content'
  3326. , findNestedContent: false // true = $P.find(contentSelector), false = $P.children(contentSelector)
  3327. // GENERIC ROOT-CLASSES - for auto-generated classNames
  3328. , paneClass: "ui-layout-pane" // Layout Pane
  3329. , resizerClass: "ui-layout-resizer" // Resizer Bar
  3330. , togglerClass: "ui-layout-toggler" // Toggler Button
  3331. , buttonClass: "ui-layout-button" // CUSTOM Buttons - eg: '[ui-layout-button]-toggle/-open/-close/-pin'
  3332. // ELEMENT SIZE & SPACING
  3333. //, size: 100 // MUST be pane-specific -initial size of pane
  3334. , minSize: 0 // when manually resizing a pane
  3335. , maxSize: 0 // ditto, 0 = no limit
  3336. , spacing_open: 6 // space between pane and adjacent panes - when pane is 'open'
  3337. , spacing_closed: 6 // ditto - when pane is 'closed'
  3338. , togglerLength_open: 50 // Length = WIDTH of toggler button on north/south sides - HEIGHT on east/west sides
  3339. , togglerLength_closed: 50 // 100% OR -1 means 'full height/width of resizer bar' - 0 means 'hidden'
  3340. , togglerAlign_open: "center" // top/left, bottom/right, center, OR...
  3341. , togglerAlign_closed: "center" // 1 => nn = offset from top/left, -1 => -nn == offset from bottom/right
  3342. , togglerContent_open: "" // text or HTML to put INSIDE the toggler
  3343. , togglerContent_closed: "" // ditto
  3344. // RESIZING OPTIONS
  3345. , resizerDblClickToggle: true //
  3346. , autoResize: true // IF size is 'auto' or a percentage, then recalc 'pixel size' whenever the layout resizes
  3347. , autoReopen: true // IF a pane was auto-closed due to noRoom, reopen it when there is room? False = leave it closed
  3348. , resizerDragOpacity: 1 // option for ui.draggable
  3349. //, resizerCursor: "" // MUST be pane-specific - cursor when over resizer-bar
  3350. , maskContents: false // true = add DIV-mask over-or-inside this pane so can 'drag' over IFRAMES
  3351. , maskObjects: false // true = add IFRAME-mask over-or-inside this pane to cover objects/applets - content-mask will overlay this mask
  3352. , maskZindex: null // will override zIndexes.content_mask if specified - not applicable to iframe-panes
  3353. , resizingGrid: false // grid size that the resizers will snap-to during resizing, eg: [20,20]
  3354. , livePaneResizing: false // true = LIVE Resizing as resizer is dragged
  3355. , liveContentResizing: false // true = re-measure header/footer heights as resizer is dragged
  3356. , liveResizingTolerance: 1 // how many px change before pane resizes, to control performance
  3357. // SLIDING OPTIONS
  3358. , sliderCursor: "pointer" // cursor when resizer-bar will trigger 'sliding'
  3359. , slideTrigger_open: "click" // click, dblclick, mouseenter
  3360. , slideTrigger_close: "mouseleave"// click, mouseleave
  3361. , slideDelay_open: 300 // applies only for mouseenter event - 0 = instant open
  3362. , slideDelay_close: 300 // applies only for mouseleave event (300ms is the minimum!)
  3363. , hideTogglerOnSlide: false // when pane is slid-open, should the toggler show?
  3364. , preventQuickSlideClose: $.layout.browser.webkit // Chrome triggers slideClosed as it is opening
  3365. , preventPrematureSlideClose: false // handle incorrect mouseleave trigger, like when over a SELECT-list in IE
  3366. // PANE-SPECIFIC TIPS & MESSAGES
  3367. , tips: {
  3368. Open: "Open" // eg: "Open Pane"
  3369. , Close: "Close"
  3370. , Resize: "Resize"
  3371. , Slide: "Slide Open"
  3372. , Pin: "Pin"
  3373. , Unpin: "Un-Pin"
  3374. , noRoomToOpen: "Not enough room to show this panel." // alert if user tries to open a pane that cannot
  3375. , minSizeWarning: "Panel has reached its minimum size" // displays in browser statusbar
  3376. , maxSizeWarning: "Panel has reached its maximum size" // ditto
  3377. }
  3378. // HOT-KEYS & MISC
  3379. , showOverflowOnHover: false // will bind allowOverflow() utility to pane.onMouseOver
  3380. , enableCursorHotkey: true // enabled 'cursor' hotkeys
  3381. //, customHotkey: "" // MUST be pane-specific - EITHER a charCode OR a character
  3382. , customHotkeyModifier: "SHIFT" // either 'SHIFT', 'CTRL' or 'CTRL+SHIFT' - NOT 'ALT'
  3383. // PANE ANIMATION
  3384. // NOTE: fxSss_open, fxSss_close & fxSss_size options (eg: fxName_open) are auto-generated if not passed
  3385. , fxName: "slide" // ('none' or blank), slide, drop, scale -- only relevant to 'open' & 'close', NOT 'size'
  3386. , fxSpeed: null // slow, normal, fast, 200, nnn - if passed, will OVERRIDE fxSettings.duration
  3387. , fxSettings: {} // can be passed, eg: { easing: "easeOutBounce", duration: 1500 }
  3388. , fxOpacityFix: true // tries to fix opacity in IE to restore anti-aliasing after animation
  3389. , animatePaneSizing: false // true = animate resizing after dragging resizer-bar OR sizePane() is called
  3390. /* NOTE: Action-specific FX options are auto-generated from the options above if not specifically set:
  3391. fxName_open: "slide" // 'Open' pane animation
  3392. fnName_close: "slide" // 'Close' pane animation
  3393. fxName_size: "slide" // 'Size' pane animation - when animatePaneSizing = true
  3394. fxSpeed_open: null
  3395. fxSpeed_close: null
  3396. fxSpeed_size: null
  3397. fxSettings_open: {}
  3398. fxSettings_close: {}
  3399. fxSettings_size: {}
  3400. */
  3401. // CHILD/NESTED LAYOUTS
  3402. , children: null // Layout-options for nested/child layout - even {} is valid as options
  3403. , containerSelector: '' // if child is NOT 'directly nested', a selector to find it/them (can have more than one child layout!)
  3404. , initChildren: true // true = child layout will be created as soon as _this_ layout completes initialization
  3405. , destroyChildren: true // true = destroy child-layout if this pane is destroyed
  3406. , resizeChildren: true // true = trigger child-layout.resizeAll() when this pane is resized
  3407. // EVENT TRIGGERING
  3408. , triggerEventsOnLoad: false // true = trigger onopen OR onclose callbacks when layout initializes
  3409. , triggerEventsDuringLiveResize: true // true = trigger onresize callback REPEATEDLY if livePaneResizing==true
  3410. // PANE CALLBACKS
  3411. , onshow_start: null // CALLBACK when pane STARTS to Show - BEFORE onopen/onhide_start
  3412. , onshow_end: null // CALLBACK when pane ENDS being Shown - AFTER onopen/onhide_end
  3413. , onhide_start: null // CALLBACK when pane STARTS to Close - BEFORE onclose_start
  3414. , onhide_end: null // CALLBACK when pane ENDS being Closed - AFTER onclose_end
  3415. , onopen_start: null // CALLBACK when pane STARTS to Open
  3416. , onopen_end: null // CALLBACK when pane ENDS being Opened
  3417. , onclose_start: null // CALLBACK when pane STARTS to Close
  3418. , onclose_end: null // CALLBACK when pane ENDS being Closed
  3419. , onresize_start: null // CALLBACK when pane STARTS being Resized ***FOR ANY REASON***
  3420. , onresize_end: null // CALLBACK when pane ENDS being Resized ***FOR ANY REASON***
  3421. , onsizecontent_start: null // CALLBACK when sizing of content-element STARTS
  3422. , onsizecontent_end: null // CALLBACK when sizing of content-element ENDS
  3423. , onswap_start: null // CALLBACK when pane STARTS to Swap
  3424. , onswap_end: null // CALLBACK when pane ENDS being Swapped
  3425. , ondrag_start: null // CALLBACK when pane STARTS being ***MANUALLY*** Resized
  3426. , ondrag_end: null // CALLBACK when pane ENDS being ***MANUALLY*** Resized
  3427. }
  3428. /*
  3429. * PANE-SPECIFIC SETTINGS
  3430. * - options listed below MUST be specified per-pane - they CANNOT be set under 'panes'
  3431. * - all options under the 'panes' key can also be set specifically for any pane
  3432. * - most options under the 'panes' key apply only to 'border-panes' - NOT the the center-pane
  3433. */
  3434. , north: {
  3435. paneSelector: ".ui-layout-north"
  3436. , size: "auto" // eg: "auto", "30%", .30, 200
  3437. , resizerCursor: "n-resize" // custom = url(myCursor.cur)
  3438. , customHotkey: "" // EITHER a charCode (43) OR a character ("o")
  3439. }
  3440. , south: {
  3441. paneSelector: ".ui-layout-south"
  3442. , size: "auto"
  3443. , resizerCursor: "s-resize"
  3444. , customHotkey: ""
  3445. }
  3446. , east: {
  3447. paneSelector: ".ui-layout-east"
  3448. , size: 200
  3449. , resizerCursor: "e-resize"
  3450. , customHotkey: ""
  3451. }
  3452. , west: {
  3453. paneSelector: ".ui-layout-west"
  3454. , size: 200
  3455. , resizerCursor: "w-resize"
  3456. , customHotkey: ""
  3457. }
  3458. , center: {
  3459. paneSelector: ".ui-layout-center"
  3460. , minWidth: 0
  3461. , minHeight: 0
  3462. }
  3463. };
  3464. $.layout.optionsMap = {
  3465. // layout/global options - NOT pane-options
  3466. layout: ("name,instanceKey,stateManagement,effects,inset,zIndexes,errors,"
  3467. + "zIndex,scrollToBookmarkOnLoad,showErrorMessages,maskPanesEarly,"
  3468. + "outset,resizeWithWindow,resizeWithWindowDelay,resizeWithWindowMaxDelay,"
  3469. + "onresizeall,onresizeall_start,onresizeall_end,onload,onload_start,onload_end,onunload,onunload_start,onunload_end").split(",")
  3470. // borderPanes: [ ALL options that are NOT specified as 'layout' ]
  3471. // default.panes options that apply to the center-pane (most options apply _only_ to border-panes)
  3472. , center: ("paneClass,contentSelector,contentIgnoreSelector,findNestedContent,applyDemoStyles,triggerEventsOnLoad,"
  3473. + "showOverflowOnHover,maskContents,maskObjects,liveContentResizing,"
  3474. + "containerSelector,children,initChildren,resizeChildren,destroyChildren,"
  3475. + "onresize,onresize_start,onresize_end,onsizecontent,onsizecontent_start,onsizecontent_end").split(",")
  3476. // options that MUST be specifically set 'per-pane' - CANNOT set in the panes (defaults) key
  3477. , noDefault: ("paneSelector,resizerCursor,customHotkey").split(",")
  3478. };
  3479. /**
  3480. * Processes options passed in converts flat-format data into subkey (JSON) format
  3481. * In flat-format, subkeys are _currently_ separated with 2 underscores, like north__optName
  3482. * Plugins may also call this method so they can transform their own data
  3483. *
  3484. * @param {!Object} hash Data/options passed by user - may be a single level or nested levels
  3485. * @param {boolean=} [addKeys=false] Should the primary layout.options keys be added if they do not exist?
  3486. * @return {Object} Returns hash of minWidth & minHeight
  3487. */
  3488. $.layout.transformData = function (hash, addKeys) {
  3489. var json = addKeys ? { panes: {}, center: {} } : {} // init return object
  3490. , branch, optKey, keys, key, val, i, c;
  3491. if (typeof hash !== "object") return json; // no options passed
  3492. // convert all 'flat-keys' to 'sub-key' format
  3493. for (optKey in hash) {
  3494. branch = json;
  3495. val = hash[ optKey ];
  3496. keys = optKey.split("__"); // eg: west__size or north__fxSettings__duration
  3497. c = keys.length - 1;
  3498. // convert underscore-delimited to subkeys
  3499. for (i=0; i <= c; i++) {
  3500. key = keys[i];
  3501. if (i === c) { // last key = value
  3502. if ($.isPlainObject( val ))
  3503. branch[key] = $.layout.transformData( val ); // RECURSE
  3504. else
  3505. branch[key] = val;
  3506. }
  3507. else {
  3508. if (!branch[key])
  3509. branch[key] = {}; // create the subkey
  3510. // recurse to sub-key for next loop - if not done
  3511. branch = branch[key];
  3512. }
  3513. }
  3514. }
  3515. return json;
  3516. };
  3517. // INTERNAL CONFIG DATA - DO NOT CHANGE THIS!
  3518. $.layout.backwardCompatibility = {
  3519. // data used by renameOldOptions()
  3520. map: {
  3521. // OLD Option Name: NEW Option Name
  3522. applyDefaultStyles: "applyDemoStyles"
  3523. // CHILD/NESTED LAYOUTS
  3524. , childOptions: "children"
  3525. , initChildLayout: "initChildren"
  3526. , destroyChildLayout: "destroyChildren"
  3527. , resizeChildLayout: "resizeChildren"
  3528. , resizeNestedLayout: "resizeChildren"
  3529. // MISC Options
  3530. , resizeWhileDragging: "livePaneResizing"
  3531. , resizeContentWhileDragging: "liveContentResizing"
  3532. , triggerEventsWhileDragging: "triggerEventsDuringLiveResize"
  3533. , maskIframesOnResize: "maskContents"
  3534. // STATE MANAGEMENT
  3535. , useStateCookie: "stateManagement.enabled"
  3536. , "cookie.autoLoad": "stateManagement.autoLoad"
  3537. , "cookie.autoSave": "stateManagement.autoSave"
  3538. , "cookie.keys": "stateManagement.stateKeys"
  3539. , "cookie.name": "stateManagement.cookie.name"
  3540. , "cookie.domain": "stateManagement.cookie.domain"
  3541. , "cookie.path": "stateManagement.cookie.path"
  3542. , "cookie.expires": "stateManagement.cookie.expires"
  3543. , "cookie.secure": "stateManagement.cookie.secure"
  3544. // OLD Language options
  3545. , noRoomToOpenTip: "tips.noRoomToOpen"
  3546. , togglerTip_open: "tips.Close" // open = Close
  3547. , togglerTip_closed: "tips.Open" // closed = Open
  3548. , resizerTip: "tips.Resize"
  3549. , sliderTip: "tips.Slide"
  3550. }
  3551. /**
  3552. * @param {Object} opts
  3553. */
  3554. , renameOptions: function (opts) {
  3555. var map = $.layout.backwardCompatibility.map
  3556. , oldData, newData, value
  3557. ;
  3558. for (var itemPath in map) {
  3559. oldData = getBranch( itemPath );
  3560. value = oldData.branch[ oldData.key ];
  3561. if (value !== undefined) {
  3562. newData = getBranch( map[itemPath], true );
  3563. newData.branch[ newData.key ] = value;
  3564. delete oldData.branch[ oldData.key ];
  3565. }
  3566. }
  3567. /**
  3568. * @param {string} path
  3569. * @param {boolean=} [create=false] Create path if does not exist
  3570. */
  3571. function getBranch (path, create) {
  3572. var a = path.split(".") // split keys into array
  3573. , c = a.length - 1
  3574. , D = { branch: opts, key: a[c] } // init branch at top & set key (last item)
  3575. , i = 0, k, undef;
  3576. for (; i<c; i++) { // skip the last key (data)
  3577. k = a[i];
  3578. if (D.branch[ k ] == undefined) { // child-key does not exist
  3579. if (create) {
  3580. D.branch = D.branch[ k ] = {}; // create child-branch
  3581. }
  3582. else // can't go any farther
  3583. D.branch = {}; // branch is undefined
  3584. }
  3585. else
  3586. D.branch = D.branch[ k ]; // get child-branch
  3587. }
  3588. return D;
  3589. };
  3590. }
  3591. /**
  3592. * @param {Object} opts
  3593. */
  3594. , renameAllOptions: function (opts) {
  3595. var ren = $.layout.backwardCompatibility.renameOptions;
  3596. // rename root (layout) options
  3597. ren( opts );
  3598. // rename 'defaults' to 'panes'
  3599. if (opts.defaults) {
  3600. if (typeof opts.panes !== "object")
  3601. opts.panes = {};
  3602. $.extend(true, opts.panes, opts.defaults);
  3603. delete opts.defaults;
  3604. }
  3605. // rename options in the the options.panes key
  3606. if (opts.panes) ren( opts.panes );
  3607. // rename options inside *each pane key*, eg: options.west
  3608. $.each($.layout.config.allPanes, function (i, pane) {
  3609. if (opts[pane]) ren( opts[pane] );
  3610. });
  3611. return opts;
  3612. }
  3613. };
  3614. /* ============================================================
  3615. * BEGIN WIDGET: $( selector ).layout( {options} );
  3616. * ============================================================
  3617. */
  3618. $.fn.layout = function (opts) {
  3619. var
  3620. // local aliases to global data
  3621. browser = $.layout.browser
  3622. , _c = $.layout.config
  3623. // local aliases to utlity methods
  3624. , cssW = $.layout.cssWidth
  3625. , cssH = $.layout.cssHeight
  3626. , elDims = $.layout.getElementDimensions
  3627. , styles = $.layout.getElementStyles
  3628. , evtObj = $.layout.getEventObject
  3629. , evtPane = $.layout.parsePaneName
  3630. /**
  3631. * options - populated by initOptions()
  3632. */
  3633. , options = $.extend(true, {}, $.layout.defaults)
  3634. , effects = options.effects = $.extend(true, {}, $.layout.effects)
  3635. /**
  3636. * layout-state object
  3637. */
  3638. , state = {
  3639. // generate unique ID to use for event.namespace so can unbind only events added by 'this layout'
  3640. id: "layout"+ $.now() // code uses alias: sID
  3641. , initialized: false
  3642. , paneResizing: false
  3643. , panesSliding: {}
  3644. , container: { // list all keys referenced in code to avoid compiler error msgs
  3645. innerWidth: 0
  3646. , innerHeight: 0
  3647. , outerWidth: 0
  3648. , outerHeight: 0
  3649. , layoutWidth: 0
  3650. , layoutHeight: 0
  3651. }
  3652. , north: { childIdx: 0 }
  3653. , south: { childIdx: 0 }
  3654. , east: { childIdx: 0 }
  3655. , west: { childIdx: 0 }
  3656. , center: { childIdx: 0 }
  3657. }
  3658. /**
  3659. * parent/child-layout pointers
  3660. */
  3661. //, hasParentLayout = false - exists ONLY inside Instance so can be set externally
  3662. , children = {
  3663. north: null
  3664. , south: null
  3665. , east: null
  3666. , west: null
  3667. , center: null
  3668. }
  3669. /*
  3670. * ###########################
  3671. * INTERNAL HELPER FUNCTIONS
  3672. * ###########################
  3673. */
  3674. /**
  3675. * Manages all internal timers
  3676. */
  3677. , timer = {
  3678. data: {}
  3679. , set: function (s, fn, ms) { timer.clear(s); timer.data[s] = setTimeout(fn, ms); }
  3680. , clear: function (s) { var t=timer.data; if (t[s]) {clearTimeout(t[s]); delete t[s];} }
  3681. }
  3682. /**
  3683. * Alert or console.log a message - IF option is enabled.
  3684. *
  3685. * @param {(string|!Object)} msg Message (or debug-data) to display
  3686. * @param {boolean=} [popup=false] True by default, means 'alert', false means use console.log
  3687. * @param {boolean=} [debug=false] True means is a widget debugging message
  3688. */
  3689. , _log = function (msg, popup, debug) {
  3690. var o = options;
  3691. if ((o.showErrorMessages && !debug) || (debug && o.showDebugMessages))
  3692. $.layout.msg( o.name +' / '+ msg, (popup !== false) );
  3693. return false;
  3694. }
  3695. /**
  3696. * Executes a Callback function after a trigger event, like resize, open or close
  3697. *
  3698. * @param {string} evtName Name of the layout callback, eg "onresize_start"
  3699. * @param {(string|boolean)=} [pane=""] This is passed only so we can pass the 'pane object' to the callback
  3700. * @param {(string|boolean)=} [skipBoundEvents=false] True = do not run events bound to the elements - only the callbacks set in options
  3701. */
  3702. , _runCallbacks = function (evtName, pane, skipBoundEvents) {
  3703. var hasPane = pane && isStr(pane)
  3704. , s = hasPane ? state[pane] : state
  3705. , o = hasPane ? options[pane] : options
  3706. , lName = options.name
  3707. // names like onopen and onopen_end separate are interchangeable in options...
  3708. , lng = evtName + (evtName.match(/_/) ? "" : "_end")
  3709. , shrt = lng.match(/_end$/) ? lng.substr(0, lng.length - 4) : ""
  3710. , fn = o[lng] || o[shrt]
  3711. , retVal = "NC" // NC = No Callback
  3712. , args = []
  3713. , $P
  3714. ;
  3715. if ( !hasPane && $.type(pane) === 'boolean' ) {
  3716. skipBoundEvents = pane; // allow pane param to be skipped for Layout callback
  3717. pane = "";
  3718. }
  3719. // first trigger the callback set in the options
  3720. if (fn) {
  3721. try {
  3722. // convert function name (string) to function object
  3723. if (isStr( fn )) {
  3724. if (fn.match(/,/)) {
  3725. // function name cannot contain a comma,
  3726. // so must be a function name AND a parameter to pass
  3727. args = fn.split(",")
  3728. , fn = eval(args[0]);
  3729. }
  3730. else // just the name of an external function?
  3731. fn = eval(fn);
  3732. }
  3733. // execute the callback, if exists
  3734. if ($.isFunction( fn )) {
  3735. if (args.length)
  3736. retVal = g(fn)(args[1]); // pass the argument parsed from 'list'
  3737. else if ( hasPane )
  3738. // pass data: pane-name, pane-element, pane-state, pane-options, and layout-name
  3739. retVal = g(fn)( pane, $Ps[pane], s, o, lName );
  3740. else // must be a layout/container callback - pass suitable info
  3741. retVal = g(fn)( Instance, s, o, lName );
  3742. }
  3743. }
  3744. catch (ex) {
  3745. _log( options.errors.callbackError.replace(/EVENT/, $.trim((pane || "") +" "+ lng)), false );
  3746. if ($.type(ex) === 'string' && string.length)
  3747. _log('Exception: '+ ex, false );
  3748. }
  3749. }
  3750. // trigger additional events bound directly to the pane
  3751. if (!skipBoundEvents && retVal !== false) {
  3752. if ( hasPane ) { // PANE events can be bound to each pane-elements
  3753. $P = $Ps[pane];
  3754. o = options[pane];
  3755. s = state[pane];
  3756. $P.triggerHandler('layoutpane'+ lng, [ pane, $P, s, o, lName ]);
  3757. if (shrt)
  3758. $P.triggerHandler('layoutpane'+ shrt, [ pane, $P, s, o, lName ]);
  3759. }
  3760. else { // LAYOUT events can be bound to the container-element
  3761. $N.triggerHandler('layout'+ lng, [ Instance, s, o, lName ]);
  3762. if (shrt)
  3763. $N.triggerHandler('layout'+ shrt, [ Instance, s, o, lName ]);
  3764. }
  3765. }
  3766. // ALWAYS resizeChildren after an onresize_end event - even during initialization
  3767. // IGNORE onsizecontent_end event because causes child-layouts to resize TWICE
  3768. if (hasPane && evtName === "onresize_end") // BAD: || evtName === "onsizecontent_end"
  3769. resizeChildren(pane+"", true); // compiler hack -force string
  3770. return retVal;
  3771. function g (f) { return f; }; // compiler hack
  3772. }
  3773. /**
  3774. * cure iframe display issues in IE & other browsers
  3775. */
  3776. , _fixIframe = function (pane) {
  3777. if (browser.mozilla) return; // skip FireFox - it auto-refreshes iframes onShow
  3778. var $P = $Ps[pane];
  3779. // if the 'pane' is an iframe, do it
  3780. if (state[pane].tagName === "IFRAME")
  3781. $P.css(_c.hidden).css(_c.visible);
  3782. else // ditto for any iframes INSIDE the pane
  3783. $P.find('IFRAME').css(_c.hidden).css(_c.visible);
  3784. }
  3785. /**
  3786. * @param {string} pane Can accept ONLY a 'pane' (east, west, etc)
  3787. * @param {number=} outerSize (optional) Can pass a width, allowing calculations BEFORE element is resized
  3788. * @return {number} Returns the innerHeight/Width of el by subtracting padding and borders
  3789. */
  3790. , cssSize = function (pane, outerSize) {
  3791. var fn = _c[pane].dir=="horz" ? cssH : cssW;
  3792. return fn($Ps[pane], outerSize);
  3793. }
  3794. /**
  3795. * @param {string} pane Can accept ONLY a 'pane' (east, west, etc)
  3796. * @return {Object} Returns hash of minWidth & minHeight
  3797. */
  3798. , cssMinDims = function (pane) {
  3799. // minWidth/Height means CSS width/height = 1px
  3800. var $P = $Ps[pane]
  3801. , dir = _c[pane].dir
  3802. , d = {
  3803. minWidth: 1001 - cssW($P, 1000)
  3804. , minHeight: 1001 - cssH($P, 1000)
  3805. }
  3806. ;
  3807. if (dir === "horz") d.minSize = d.minHeight;
  3808. if (dir === "vert") d.minSize = d.minWidth;
  3809. return d;
  3810. }
  3811. // TODO: see if these methods can be made more useful...
  3812. // TODO: *maybe* return cssW/H from these so caller can use this info
  3813. /**
  3814. * @param {(string|!Object)} el
  3815. * @param {number=} outerWidth
  3816. * @param {boolean=} [autoHide=false]
  3817. */
  3818. , setOuterWidth = function (el, outerWidth, autoHide) {
  3819. var $E = el, w;
  3820. if (isStr(el)) $E = $Ps[el]; // west
  3821. else if (!el.jquery) $E = $(el);
  3822. w = cssW($E, outerWidth);
  3823. $E.css({ width: w });
  3824. if (w > 0) {
  3825. if (autoHide && $E.data('autoHidden') && $E.innerHeight() > 0) {
  3826. $E.show().data('autoHidden', false);
  3827. if (!browser.mozilla) // FireFox refreshes iframes - IE does not
  3828. // make hidden, then visible to 'refresh' display after animation
  3829. $E.css(_c.hidden).css(_c.visible);
  3830. }
  3831. }
  3832. else if (autoHide && !$E.data('autoHidden'))
  3833. $E.hide().data('autoHidden', true);
  3834. }
  3835. /**
  3836. * @param {(string|!Object)} el
  3837. * @param {number=} outerHeight
  3838. * @param {boolean=} [autoHide=false]
  3839. */
  3840. , setOuterHeight = function (el, outerHeight, autoHide) {
  3841. var $E = el, h;
  3842. if (isStr(el)) $E = $Ps[el]; // west
  3843. else if (!el.jquery) $E = $(el);
  3844. h = cssH($E, outerHeight);
  3845. $E.css({ height: h, visibility: "visible" }); // may have been 'hidden' by sizeContent
  3846. if (h > 0 && $E.innerWidth() > 0) {
  3847. if (autoHide && $E.data('autoHidden')) {
  3848. $E.show().data('autoHidden', false);
  3849. if (!browser.mozilla) // FireFox refreshes iframes - IE does not
  3850. $E.css(_c.hidden).css(_c.visible);
  3851. }
  3852. }
  3853. else if (autoHide && !$E.data('autoHidden'))
  3854. $E.hide().data('autoHidden', true);
  3855. }
  3856. /**
  3857. * Converts any 'size' params to a pixel/integer size, if not already
  3858. * If 'auto' or a decimal/percentage is passed as 'size', a pixel-size is calculated
  3859. *
  3860. /**
  3861. * @param {string} pane
  3862. * @param {(string|number)=} size
  3863. * @param {string=} [dir]
  3864. * @return {number}
  3865. */
  3866. , _parseSize = function (pane, size, dir) {
  3867. if (!dir) dir = _c[pane].dir;
  3868. if (isStr(size) && size.match(/%/))
  3869. size = (size === '100%') ? -1 : parseInt(size, 10) / 100; // convert % to decimal
  3870. if (size === 0)
  3871. return 0;
  3872. else if (size >= 1)
  3873. return parseInt(size, 10);
  3874. var o = options, avail = 0;
  3875. if (dir=="horz") // north or south or center.minHeight
  3876. avail = sC.innerHeight - ($Ps.north ? o.north.spacing_open : 0) - ($Ps.south ? o.south.spacing_open : 0);
  3877. else if (dir=="vert") // east or west or center.minWidth
  3878. avail = sC.innerWidth - ($Ps.west ? o.west.spacing_open : 0) - ($Ps.east ? o.east.spacing_open : 0);
  3879. if (size === -1) // -1 == 100%
  3880. return avail;
  3881. else if (size > 0) // percentage, eg: .25
  3882. return round(avail * size);
  3883. else if (pane=="center")
  3884. return 0;
  3885. else { // size < 0 || size=='auto' || size==Missing || size==Invalid
  3886. // auto-size the pane
  3887. var dim = (dir === "horz" ? "height" : "width")
  3888. , $P = $Ps[pane]
  3889. , $C = dim === 'height' ? $Cs[pane] : false
  3890. , vis = $.layout.showInvisibly($P) // show pane invisibly if hidden
  3891. , szP = $P.css(dim) // SAVE current pane size
  3892. , szC = $C ? $C.css(dim) : 0 // SAVE current content size
  3893. ;
  3894. $P.css(dim, "auto");
  3895. if ($C) $C.css(dim, "auto");
  3896. size = (dim === "height") ? $P.outerHeight() : $P.outerWidth(); // MEASURE
  3897. $P.css(dim, szP).css(vis); // RESET size & visibility
  3898. if ($C) $C.css(dim, szC);
  3899. return size;
  3900. }
  3901. }
  3902. /**
  3903. * Calculates current 'size' (outer-width or outer-height) of a border-pane - optionally with 'pane-spacing' added
  3904. *
  3905. * @param {(string|!Object)} pane
  3906. * @param {boolean=} [inclSpace=false]
  3907. * @return {number} Returns EITHER Width for east/west panes OR Height for north/south panes
  3908. */
  3909. , getPaneSize = function (pane, inclSpace) {
  3910. var
  3911. $P = $Ps[pane]
  3912. , o = options[pane]
  3913. , s = state[pane]
  3914. , oSp = (inclSpace ? o.spacing_open : 0)
  3915. , cSp = (inclSpace ? o.spacing_closed : 0)
  3916. ;
  3917. if (!$P || s.isHidden)
  3918. return 0;
  3919. else if (s.isClosed || (s.isSliding && inclSpace))
  3920. return cSp;
  3921. else if (_c[pane].dir === "horz")
  3922. return $P.outerHeight() + oSp;
  3923. else // dir === "vert"
  3924. return $P.outerWidth() + oSp;
  3925. }
  3926. /**
  3927. * Calculate min/max pane dimensions and limits for resizing
  3928. *
  3929. * @param {string} pane
  3930. * @param {boolean=} [slide=false]
  3931. */
  3932. , setSizeLimits = function (pane, slide) {
  3933. if (!isInitialized()) return;
  3934. var
  3935. o = options[pane]
  3936. , s = state[pane]
  3937. , c = _c[pane]
  3938. , dir = c.dir
  3939. , type = c.sizeType.toLowerCase()
  3940. , isSliding = (slide != undefined ? slide : s.isSliding) // only open() passes 'slide' param
  3941. , $P = $Ps[pane]
  3942. , paneSpacing = o.spacing_open
  3943. // measure the pane on the *opposite side* from this pane
  3944. , altPane = _c.oppositeEdge[pane]
  3945. , altS = state[altPane]
  3946. , $altP = $Ps[altPane]
  3947. , altPaneSize = (!$altP || altS.isVisible===false || altS.isSliding ? 0 : (dir=="horz" ? $altP.outerHeight() : $altP.outerWidth()))
  3948. , altPaneSpacing = ((!$altP || altS.isHidden ? 0 : options[altPane][ altS.isClosed !== false ? "spacing_closed" : "spacing_open" ]) || 0)
  3949. // limitSize prevents this pane from 'overlapping' opposite pane
  3950. , containerSize = (dir=="horz" ? sC.innerHeight : sC.innerWidth)
  3951. , minCenterDims = cssMinDims("center")
  3952. , minCenterSize = dir=="horz" ? max(options.center.minHeight, minCenterDims.minHeight) : max(options.center.minWidth, minCenterDims.minWidth)
  3953. // if pane is 'sliding', then ignore center and alt-pane sizes - because 'overlays' them
  3954. , limitSize = (containerSize - paneSpacing - (isSliding ? 0 : (_parseSize("center", minCenterSize, dir) + altPaneSize + altPaneSpacing)))
  3955. , minSize = s.minSize = max( _parseSize(pane, o.minSize), cssMinDims(pane).minSize )
  3956. , maxSize = s.maxSize = min( (o.maxSize ? _parseSize(pane, o.maxSize) : 100000), limitSize )
  3957. , r = s.resizerPosition = {} // used to set resizing limits
  3958. , top = sC.inset.top
  3959. , left = sC.inset.left
  3960. , W = sC.innerWidth
  3961. , H = sC.innerHeight
  3962. , rW = o.spacing_open // subtract resizer-width to get top/left position for south/east
  3963. ;
  3964. switch (pane) {
  3965. case "north": r.min = top + minSize;
  3966. r.max = top + maxSize;
  3967. break;
  3968. case "west": r.min = left + minSize;
  3969. r.max = left + maxSize;
  3970. break;
  3971. case "south": r.min = top + H - maxSize - rW;
  3972. r.max = top + H - minSize - rW;
  3973. break;
  3974. case "east": r.min = left + W - maxSize - rW;
  3975. r.max = left + W - minSize - rW;
  3976. break;
  3977. };
  3978. }
  3979. /**
  3980. * Returns data for setting the size/position of center pane. Also used to set Height for east/west panes
  3981. *
  3982. * @return JSON Returns a hash of all dimensions: top, bottom, left, right, (outer) width and (outer) height
  3983. */
  3984. , calcNewCenterPaneDims = function () {
  3985. var d = {
  3986. top: getPaneSize("north", true) // true = include 'spacing' value for pane
  3987. , bottom: getPaneSize("south", true)
  3988. , left: getPaneSize("west", true)
  3989. , right: getPaneSize("east", true)
  3990. , width: 0
  3991. , height: 0
  3992. };
  3993. // NOTE: sC = state.container
  3994. // calc center-pane outer dimensions
  3995. d.width = sC.innerWidth - d.left - d.right; // outerWidth
  3996. d.height = sC.innerHeight - d.bottom - d.top; // outerHeight
  3997. // add the 'container border/padding' to get final positions relative to the container
  3998. d.top += sC.inset.top;
  3999. d.bottom += sC.inset.bottom;
  4000. d.left += sC.inset.left;
  4001. d.right += sC.inset.right;
  4002. return d;
  4003. }
  4004. /**
  4005. * @param {!Object} el
  4006. * @param {boolean=} [allStates=false]
  4007. */
  4008. , getHoverClasses = function (el, allStates) {
  4009. var
  4010. $El = $(el)
  4011. , type = $El.data("layoutRole")
  4012. , pane = $El.data("layoutEdge")
  4013. , o = options[pane]
  4014. , root = o[type +"Class"]
  4015. , _pane = "-"+ pane // eg: "-west"
  4016. , _open = "-open"
  4017. , _closed = "-closed"
  4018. , _slide = "-sliding"
  4019. , _hover = "-hover " // NOTE the trailing space
  4020. , _state = $El.hasClass(root+_closed) ? _closed : _open
  4021. , _alt = _state === _closed ? _open : _closed
  4022. , classes = (root+_hover) + (root+_pane+_hover) + (root+_state+_hover) + (root+_pane+_state+_hover)
  4023. ;
  4024. if (allStates) // when 'removing' classes, also remove alternate-state classes
  4025. classes += (root+_alt+_hover) + (root+_pane+_alt+_hover);
  4026. if (type=="resizer" && $El.hasClass(root+_slide))
  4027. classes += (root+_slide+_hover) + (root+_pane+_slide+_hover);
  4028. return $.trim(classes);
  4029. }
  4030. , addHover = function (evt, el) {
  4031. var $E = $(el || this);
  4032. if (evt && $E.data("layoutRole") === "toggler")
  4033. evt.stopPropagation(); // prevent triggering 'slide' on Resizer-bar
  4034. $E.addClass( getHoverClasses($E) );
  4035. }
  4036. , removeHover = function (evt, el) {
  4037. var $E = $(el || this);
  4038. $E.removeClass( getHoverClasses($E, true) );
  4039. }
  4040. , onResizerEnter = function (evt) { // ALSO called by toggler.mouseenter
  4041. var pane = $(this).data("layoutEdge")
  4042. , s = state[pane]
  4043. ;
  4044. // ignore closed-panes and mouse moving back & forth over resizer!
  4045. // also ignore if ANY pane is currently resizing
  4046. if ( s.isClosed || s.isResizing || state.paneResizing ) return;
  4047. if ($.fn.disableSelection)
  4048. $("body").disableSelection();
  4049. if (options.maskPanesEarly)
  4050. showMasks( pane, { resizing: true });
  4051. }
  4052. , onResizerLeave = function (evt, el) {
  4053. var e = el || this // el is only passed when called by the timer
  4054. , pane = $(e).data("layoutEdge")
  4055. , name = pane +"ResizerLeave"
  4056. ;
  4057. timer.clear(pane+"_openSlider"); // cancel slideOpen timer, if set
  4058. timer.clear(name); // cancel enableSelection timer - may re/set below
  4059. // this method calls itself on a timer because it needs to allow
  4060. // enough time for dragging to kick-in and set the isResizing flag
  4061. // dragging has a 100ms delay set, so this delay must be >100
  4062. if (!el) // 1st call - mouseleave event
  4063. timer.set(name, function(){ onResizerLeave(evt, e); }, 200);
  4064. // if user is resizing, then dragStop will enableSelection(), so can skip it here
  4065. else if ( !state.paneResizing ) { // 2nd call - by timer
  4066. if ($.fn.enableSelection)
  4067. $("body").enableSelection();
  4068. if (options.maskPanesEarly)
  4069. hideMasks();
  4070. }
  4071. }
  4072. /*
  4073. * ###########################
  4074. * INITIALIZATION METHODS
  4075. * ###########################
  4076. */
  4077. /**
  4078. * Initialize the layout - called automatically whenever an instance of layout is created
  4079. *
  4080. * @see none - triggered onInit
  4081. * @return mixed true = fully initialized | false = panes not initialized (yet) | 'cancel' = abort
  4082. */
  4083. , _create = function () {
  4084. // initialize config/options
  4085. initOptions();
  4086. var o = options
  4087. , s = state;
  4088. // TEMP state so isInitialized returns true during init process
  4089. s.creatingLayout = true;
  4090. // init plugins for this layout, if there are any (eg: stateManagement)
  4091. runPluginCallbacks( Instance, $.layout.onCreate );
  4092. // options & state have been initialized, so now run beforeLoad callback
  4093. // onload will CANCEL layout creation if it returns false
  4094. if (false === _runCallbacks("onload_start"))
  4095. return 'cancel';
  4096. // initialize the container element
  4097. _initContainer();
  4098. // bind hotkey function - keyDown - if required
  4099. initHotkeys();
  4100. // bind window.onunload
  4101. $(window).bind("unload."+ sID, unload);
  4102. // init plugins for this layout, if there are any (eg: customButtons)
  4103. runPluginCallbacks( Instance, $.layout.onLoad );
  4104. // if layout elements are hidden, then layout WILL NOT complete initialization!
  4105. // initLayoutElements will set initialized=true and run the onload callback IF successful
  4106. if (o.initPanes) _initLayoutElements();
  4107. delete s.creatingLayout;
  4108. return state.initialized;
  4109. }
  4110. /**
  4111. * Initialize the layout IF not already
  4112. *
  4113. * @see All methods in Instance run this test
  4114. * @return boolean true = layoutElements have been initialized | false = panes are not initialized (yet)
  4115. */
  4116. , isInitialized = function () {
  4117. if (state.initialized || state.creatingLayout) return true; // already initialized
  4118. else return _initLayoutElements(); // try to init panes NOW
  4119. }
  4120. /**
  4121. * Initialize the layout - called automatically whenever an instance of layout is created
  4122. *
  4123. * @see _create() & isInitialized
  4124. * @param {boolean=} [retry=false] // indicates this is a 2nd try
  4125. * @return An object pointer to the instance created
  4126. */
  4127. , _initLayoutElements = function (retry) {
  4128. // initialize config/options
  4129. var o = options;
  4130. // CANNOT init panes inside a hidden container!
  4131. if (!$N.is(":visible")) {
  4132. // handle Chrome bug where popup window 'has no height'
  4133. // if layout is BODY element, try again in 50ms
  4134. // SEE: http://layout.jquery-dev.net/samples/test_popup_window.html
  4135. if ( !retry && browser.webkit && $N[0].tagName === "BODY" )
  4136. setTimeout(function(){ _initLayoutElements(true); }, 50);
  4137. return false;
  4138. }
  4139. // a center pane is required, so make sure it exists
  4140. if (!getPane("center").length) {
  4141. return _log( o.errors.centerPaneMissing );
  4142. }
  4143. // TEMP state so isInitialized returns true during init process
  4144. state.creatingLayout = true;
  4145. // update Container dims
  4146. $.extend(sC, elDims( $N, o.inset )); // passing inset means DO NOT include insetX values
  4147. // initialize all layout elements
  4148. initPanes(); // size & position panes - calls initHandles() - which calls initResizable()
  4149. if (o.scrollToBookmarkOnLoad) {
  4150. var l = self.location;
  4151. if (l.hash) l.replace( l.hash ); // scrollTo Bookmark
  4152. }
  4153. // check to see if this layout 'nested' inside a pane
  4154. if (Instance.hasParentLayout)
  4155. o.resizeWithWindow = false;
  4156. // bind resizeAll() for 'this layout instance' to window.resize event
  4157. else if (o.resizeWithWindow)
  4158. $(window).bind("resize."+ sID, windowResize);
  4159. delete state.creatingLayout;
  4160. state.initialized = true;
  4161. // init plugins for this layout, if there are any
  4162. runPluginCallbacks( Instance, $.layout.onReady );
  4163. // now run the onload callback, if exists
  4164. _runCallbacks("onload_end");
  4165. return true; // elements initialized successfully
  4166. }
  4167. /**
  4168. * Initialize nested layouts for a specific pane - can optionally pass layout-options
  4169. *
  4170. * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west
  4171. * @param {Object=} [opts] Layout-options - if passed, will OVERRRIDE options[pane].children
  4172. * @return An object pointer to the layout instance created - or null
  4173. */
  4174. , createChildren = function (evt_or_pane, opts) {
  4175. var pane = evtPane.call(this, evt_or_pane)
  4176. , $P = $Ps[pane]
  4177. ;
  4178. if (!$P) return;
  4179. var $C = $Cs[pane]
  4180. , s = state[pane]
  4181. , o = options[pane]
  4182. , sm = options.stateManagement || {}
  4183. , cos = opts ? (o.children = opts) : o.children
  4184. ;
  4185. if ( $.isPlainObject( cos ) )
  4186. cos = [ cos ]; // convert a hash to a 1-elem array
  4187. else if (!cos || !$.isArray( cos ))
  4188. return;
  4189. $.each( cos, function (idx, co) {
  4190. if ( !$.isPlainObject( co ) ) return;
  4191. // determine which element is supposed to be the 'child container'
  4192. // if pane has a 'containerSelector' OR a 'content-div', use those instead of the pane
  4193. var $containers = co.containerSelector ? $P.find( co.containerSelector ) : ($C || $P);
  4194. $containers.each(function(){
  4195. var $cont = $(this)
  4196. , child = $cont.data("layout") // see if a child-layout ALREADY exists on this element
  4197. ;
  4198. // if no layout exists, but children are set, try to create the layout now
  4199. if (!child) {
  4200. // TODO: see about moving this to the stateManagement plugin, as a method
  4201. // set a unique child-instance key for this layout, if not already set
  4202. setInstanceKey({ container: $cont, options: co }, s );
  4203. // If THIS layout has a hash in stateManagement.autoLoad,
  4204. // then see if it also contains state-data for this child-layout
  4205. // If so, copy the stateData to child.options.stateManagement.autoLoad
  4206. if ( sm.includeChildren && state.stateData[pane] ) {
  4207. // THIS layout's state was cached when its state was loaded
  4208. var paneChildren = state.stateData[pane].children || {}
  4209. , childState = paneChildren[ co.instanceKey ]
  4210. , co_sm = co.stateManagement || (co.stateManagement = { autoLoad: true })
  4211. ;
  4212. // COPY the stateData into the autoLoad key
  4213. if ( co_sm.autoLoad === true && childState ) {
  4214. co_sm.autoSave = false; // disable autoSave because saving handled by parent-layout
  4215. co_sm.includeChildren = true; // cascade option - FOR NOW
  4216. co_sm.autoLoad = $.extend(true, {}, childState); // COPY the state-hash
  4217. }
  4218. }
  4219. // create the layout
  4220. child = $cont.layout( co );
  4221. // if successful, update data
  4222. if (child) {
  4223. // add the child and update all layout-pointers
  4224. // MAY have already been done by child-layout calling parent.refreshChildren()
  4225. refreshChildren( pane, child );
  4226. }
  4227. }
  4228. });
  4229. });
  4230. }
  4231. , setInstanceKey = function (child, parentPaneState) {
  4232. // create a named key for use in state and instance branches
  4233. var $c = child.container
  4234. , o = child.options
  4235. , sm = o.stateManagement
  4236. , key = o.instanceKey || $c.data("layoutInstanceKey")
  4237. ;
  4238. if (!key) key = (sm && sm.cookie ? sm.cookie.name : '') || o.name; // look for a name/key
  4239. if (!key) key = "layout"+ (++parentPaneState.childIdx); // if no name/key found, generate one
  4240. else key = key.replace(/[^\w-]/gi, '_').replace(/_{2,}/g, '_'); // ensure is valid as a hash key
  4241. o.instanceKey = key;
  4242. $c.data("layoutInstanceKey", key); // useful if layout is destroyed and then recreated
  4243. return key;
  4244. }
  4245. /**
  4246. * @param {string} pane The pane being opened, ie: north, south, east, or west
  4247. * @param {Object=} newChild New child-layout Instance to add to this pane
  4248. */
  4249. , refreshChildren = function (pane, newChild) {
  4250. var $P = $Ps[pane]
  4251. , pC = children[pane]
  4252. , s = state[pane]
  4253. , o
  4254. ;
  4255. // check for destroy()ed layouts and update the child pointers & arrays
  4256. if ($.isPlainObject( pC )) {
  4257. $.each( pC, function (key, child) {
  4258. if (child.destroyed) delete pC[key]
  4259. });
  4260. // if no more children, remove the children hash
  4261. if ($.isEmptyObject( pC ))
  4262. pC = children[pane] = null; // clear children hash
  4263. }
  4264. // see if there is a directly-nested layout inside this pane
  4265. // if there is, then there can be only ONE child-layout, so check that...
  4266. if (!newChild && !pC) {
  4267. newChild = $P.data("layout");
  4268. }
  4269. // if a newChild instance was passed, add it to children[pane]
  4270. if (newChild) {
  4271. // update child.state
  4272. newChild.hasParentLayout = true; // set parent-flag in child
  4273. // instanceKey is a key-name used in both state and children
  4274. o = newChild.options;
  4275. // set a unique child-instance key for this layout, if not already set
  4276. setInstanceKey( newChild, s );
  4277. // add pointer to pane.children hash
  4278. if (!pC) pC = children[pane] = {}; // create an empty children hash
  4279. pC[ o.instanceKey ] = newChild.container.data("layout"); // add childLayout instance
  4280. }
  4281. // ALWAYS refresh the pane.children alias, even if null
  4282. Instance[pane].children = children[pane];
  4283. // if newChild was NOT passed - see if there is a child layout NOW
  4284. if (!newChild) {
  4285. createChildren(pane); // MAY create a child and re-call this method
  4286. }
  4287. }
  4288. , windowResize = function () {
  4289. var o = options
  4290. , delay = Number(o.resizeWithWindowDelay);
  4291. if (delay < 10) delay = 100; // MUST have a delay!
  4292. // resizing uses a delay-loop because the resize event fires repeatly - except in FF, but delay anyway
  4293. timer.clear("winResize"); // if already running
  4294. timer.set("winResize", function(){
  4295. timer.clear("winResize");
  4296. timer.clear("winResizeRepeater");
  4297. var dims = elDims( $N, o.inset );
  4298. // only trigger resizeAll() if container has changed size
  4299. if (dims.innerWidth !== sC.innerWidth || dims.innerHeight !== sC.innerHeight)
  4300. resizeAll();
  4301. }, delay);
  4302. // ALSO set fixed-delay timer, if not already running
  4303. if (!timer.data["winResizeRepeater"]) setWindowResizeRepeater();
  4304. }
  4305. , setWindowResizeRepeater = function () {
  4306. var delay = Number(options.resizeWithWindowMaxDelay);
  4307. if (delay > 0)
  4308. timer.set("winResizeRepeater", function(){ setWindowResizeRepeater(); resizeAll(); }, delay);
  4309. }
  4310. , unload = function () {
  4311. var o = options;
  4312. _runCallbacks("onunload_start");
  4313. // trigger plugin callabacks for this layout (eg: stateManagement)
  4314. runPluginCallbacks( Instance, $.layout.onUnload );
  4315. _runCallbacks("onunload_end");
  4316. }
  4317. /**
  4318. * Validate and initialize container CSS and events
  4319. *
  4320. * @see _create()
  4321. */
  4322. , _initContainer = function () {
  4323. var
  4324. N = $N[0]
  4325. , $H = $("html")
  4326. , tag = sC.tagName = N.tagName
  4327. , id = sC.id = N.id
  4328. , cls = sC.className = N.className
  4329. , o = options
  4330. , name = o.name
  4331. , props = "position,margin,padding,border"
  4332. , css = "layoutCSS"
  4333. , CSS = {}
  4334. , hid = "hidden" // used A LOT!
  4335. // see if this container is a 'pane' inside an outer-layout
  4336. , parent = $N.data("parentLayout") // parent-layout Instance
  4337. , pane = $N.data("layoutEdge") // pane-name in parent-layout
  4338. , isChild = parent && pane
  4339. , num = $.layout.cssNum
  4340. , $parent, n
  4341. ;
  4342. // sC = state.container
  4343. sC.selector = $N.selector.split(".slice")[0];
  4344. sC.ref = (o.name ? o.name +' layout / ' : '') + tag + (id ? "#"+id : cls ? '.['+cls+']' : ''); // used in messages
  4345. sC.isBody = (tag === "BODY");
  4346. // try to find a parent-layout
  4347. if (!isChild && !sC.isBody) {
  4348. $parent = $N.closest("."+ $.layout.defaults.panes.paneClass);
  4349. parent = $parent.data("parentLayout");
  4350. pane = $parent.data("layoutEdge");
  4351. isChild = parent && pane;
  4352. }
  4353. $N .data({
  4354. layout: Instance
  4355. , layoutContainer: sID // FLAG to indicate this is a layout-container - contains unique internal ID
  4356. })
  4357. .addClass(o.containerClass)
  4358. ;
  4359. var layoutMethods = {
  4360. destroy: ''
  4361. , initPanes: ''
  4362. , resizeAll: 'resizeAll'
  4363. , resize: 'resizeAll'
  4364. };
  4365. // loop hash and bind all methods - include layoutID namespacing
  4366. for (name in layoutMethods) {
  4367. $N.bind("layout"+ name.toLowerCase() +"."+ sID, Instance[ layoutMethods[name] || name ]);
  4368. }
  4369. // if this container is another layout's 'pane', then set child/parent pointers
  4370. if (isChild) {
  4371. // update parent flag
  4372. Instance.hasParentLayout = true;
  4373. // set pointers to THIS child-layout (Instance) in parent-layout
  4374. parent.refreshChildren( pane, Instance );
  4375. }
  4376. // SAVE original container CSS for use in destroy()
  4377. if (!$N.data(css)) {
  4378. // handle props like overflow different for BODY & HTML - has 'system default' values
  4379. if (sC.isBody) {
  4380. // SAVE <BODY> CSS
  4381. $N.data(css, $.extend( styles($N, props), {
  4382. height: $N.css("height")
  4383. , overflow: $N.css("overflow")
  4384. , overflowX: $N.css("overflowX")
  4385. , overflowY: $N.css("overflowY")
  4386. }));
  4387. // ALSO SAVE <HTML> CSS
  4388. $H.data(css, $.extend( styles($H, 'padding'), {
  4389. height: "auto" // FF would return a fixed px-size!
  4390. , overflow: $H.css("overflow")
  4391. , overflowX: $H.css("overflowX")
  4392. , overflowY: $H.css("overflowY")
  4393. }));
  4394. }
  4395. else // handle props normally for non-body elements
  4396. $N.data(css, styles($N, props+",top,bottom,left,right,width,height,overflow,overflowX,overflowY") );
  4397. }
  4398. try {
  4399. // common container CSS
  4400. CSS = {
  4401. overflow: hid
  4402. , overflowX: hid
  4403. , overflowY: hid
  4404. };
  4405. $N.css( CSS );
  4406. if (o.inset && !$.isPlainObject(o.inset)) {
  4407. // can specify a single number for equal outset all-around
  4408. n = parseInt(o.inset, 10) || 0
  4409. o.inset = {
  4410. top: n
  4411. , bottom: n
  4412. , left: n
  4413. , right: n
  4414. };
  4415. }
  4416. // format html & body if this is a full page layout
  4417. if (sC.isBody) {
  4418. // if HTML has padding, use this as an outer-spacing around BODY
  4419. if (!o.outset) {
  4420. // use padding from parent-elem (HTML) as outset
  4421. o.outset = {
  4422. top: num($H, "paddingTop")
  4423. , bottom: num($H, "paddingBottom")
  4424. , left: num($H, "paddingLeft")
  4425. , right: num($H, "paddingRight")
  4426. };
  4427. }
  4428. else if (!$.isPlainObject(o.outset)) {
  4429. // can specify a single number for equal outset all-around
  4430. n = parseInt(o.outset, 10) || 0
  4431. o.outset = {
  4432. top: n
  4433. , bottom: n
  4434. , left: n
  4435. , right: n
  4436. };
  4437. }
  4438. // HTML
  4439. $H.css( CSS ).css({
  4440. height: "100%"
  4441. , border: "none" // no border or padding allowed when using height = 100%
  4442. , padding: 0 // ditto
  4443. , margin: 0
  4444. });
  4445. // BODY
  4446. if (browser.isIE6) {
  4447. // IE6 CANNOT use the trick of setting absolute positioning on all 4 sides - must have 'height'
  4448. $N.css({
  4449. width: "100%"
  4450. , height: "100%"
  4451. , border: "none" // no border or padding allowed when using height = 100%
  4452. , padding: 0 // ditto
  4453. , margin: 0
  4454. , position: "relative"
  4455. });
  4456. // convert body padding to an inset option - the border cannot be measured in IE6!
  4457. if (!o.inset) o.inset = elDims( $N ).inset;
  4458. }
  4459. else { // use absolute positioning for BODY to allow borders & padding without overflow
  4460. $N.css({
  4461. width: "auto"
  4462. , height: "auto"
  4463. , margin: 0
  4464. , position: "absolute" // allows for border and padding on BODY
  4465. });
  4466. // apply edge-positioning created above
  4467. $N.css( o.outset );
  4468. }
  4469. // set current layout-container dimensions
  4470. $.extend(sC, elDims( $N, o.inset )); // passing inset means DO NOT include insetX values
  4471. }
  4472. else {
  4473. // container MUST have 'position'
  4474. var p = $N.css("position");
  4475. if (!p || !p.match(/(fixed|absolute|relative)/))
  4476. $N.css("position","relative");
  4477. // set current layout-container dimensions
  4478. if ( $N.is(":visible") ) {
  4479. $.extend(sC, elDims( $N, o.inset )); // passing inset means DO NOT change insetX (padding) values
  4480. if (sC.innerHeight < 1) // container has no 'height' - warn developer
  4481. _log( o.errors.noContainerHeight.replace(/CONTAINER/, sC.ref) );
  4482. }
  4483. }
  4484. // if container has min-width/height, then enable scrollbar(s)
  4485. if ( num($N, "minWidth") ) $N.parent().css("overflowX","auto");
  4486. if ( num($N, "minHeight") ) $N.parent().css("overflowY","auto");
  4487. } catch (ex) {}
  4488. }
  4489. /**
  4490. * Bind layout hotkeys - if options enabled
  4491. *
  4492. * @see _create() and addPane()
  4493. * @param {string=} [panes=""] The edge(s) to process
  4494. */
  4495. , initHotkeys = function (panes) {
  4496. panes = panes ? panes.split(",") : _c.borderPanes;
  4497. // bind keyDown to capture hotkeys, if option enabled for ANY pane
  4498. $.each(panes, function (i, pane) {
  4499. var o = options[pane];
  4500. if (o.enableCursorHotkey || o.customHotkey) {
  4501. $(document).bind("keydown."+ sID, keyDown); // only need to bind this ONCE
  4502. return false; // BREAK - binding was done
  4503. }
  4504. });
  4505. }
  4506. /**
  4507. * Build final OPTIONS data
  4508. *
  4509. * @see _create()
  4510. */
  4511. , initOptions = function () {
  4512. var data, d, pane, key, val, i, c, o;
  4513. // reprocess user's layout-options to have correct options sub-key structure
  4514. opts = $.layout.transformData( opts, true ); // panes = default subkey
  4515. // auto-rename old options for backward compatibility
  4516. opts = $.layout.backwardCompatibility.renameAllOptions( opts );
  4517. // if user-options has 'panes' key (pane-defaults), clean it...
  4518. if (!$.isEmptyObject(opts.panes)) {
  4519. // REMOVE any pane-defaults that MUST be set per-pane
  4520. data = $.layout.optionsMap.noDefault;
  4521. for (i=0, c=data.length; i<c; i++) {
  4522. key = data[i];
  4523. delete opts.panes[key]; // OK if does not exist
  4524. }
  4525. // REMOVE any layout-options specified under opts.panes
  4526. data = $.layout.optionsMap.layout;
  4527. for (i=0, c=data.length; i<c; i++) {
  4528. key = data[i];
  4529. delete opts.panes[key]; // OK if does not exist
  4530. }
  4531. }
  4532. // MOVE any NON-layout-options from opts-root to opts.panes
  4533. data = $.layout.optionsMap.layout;
  4534. var rootKeys = $.layout.config.optionRootKeys;
  4535. for (key in opts) {
  4536. val = opts[key];
  4537. if ($.inArray(key, rootKeys) < 0 && $.inArray(key, data) < 0) {
  4538. if (!opts.panes[key])
  4539. opts.panes[key] = $.isPlainObject(val) ? $.extend(true, {}, val) : val;
  4540. delete opts[key]
  4541. }
  4542. }
  4543. // START by updating ALL options from opts
  4544. $.extend(true, options, opts);
  4545. // CREATE final options (and config) for EACH pane
  4546. $.each(_c.allPanes, function (i, pane) {
  4547. // apply 'pane-defaults' to CONFIG.[PANE]
  4548. _c[pane] = $.extend(true, {}, _c.panes, _c[pane]);
  4549. d = options.panes;
  4550. o = options[pane];
  4551. // center-pane uses SOME keys in defaults.panes branch
  4552. if (pane === 'center') {
  4553. // ONLY copy keys from opts.panes listed in: $.layout.optionsMap.center
  4554. data = $.layout.optionsMap.center; // list of 'center-pane keys'
  4555. for (i=0, c=data.length; i<c; i++) { // loop the list...
  4556. key = data[i];
  4557. // only need to use pane-default if pane-specific value not set
  4558. if (!opts.center[key] && (opts.panes[key] || !o[key]))
  4559. o[key] = d[key]; // pane-default
  4560. }
  4561. }
  4562. else {
  4563. // border-panes use ALL keys in defaults.panes branch
  4564. o = options[pane] = $.extend(true, {}, d, o); // re-apply pane-specific opts AFTER pane-defaults
  4565. createFxOptions( pane );
  4566. // ensure all border-pane-specific base-classes exist
  4567. if (!o.resizerClass) o.resizerClass = "ui-layout-resizer";
  4568. if (!o.togglerClass) o.togglerClass = "ui-layout-toggler";
  4569. }
  4570. // ensure we have base pane-class (ALL panes)
  4571. if (!o.paneClass) o.paneClass = "ui-layout-pane";
  4572. });
  4573. // update options.zIndexes if a zIndex-option specified
  4574. var zo = opts.zIndex
  4575. , z = options.zIndexes;
  4576. if (zo > 0) {
  4577. z.pane_normal = zo;
  4578. z.content_mask = max(zo+1, z.content_mask); // MIN = +1
  4579. z.resizer_normal = max(zo+2, z.resizer_normal); // MIN = +2
  4580. }
  4581. // DELETE 'panes' key now that we are done - values were copied to EACH pane
  4582. delete options.panes;
  4583. function createFxOptions ( pane ) {
  4584. var o = options[pane]
  4585. , d = options.panes;
  4586. // ensure fxSettings key to avoid errors
  4587. if (!o.fxSettings) o.fxSettings = {};
  4588. if (!d.fxSettings) d.fxSettings = {};
  4589. $.each(["_open","_close","_size"], function (i,n) {
  4590. var
  4591. sName = "fxName"+ n
  4592. , sSpeed = "fxSpeed"+ n
  4593. , sSettings = "fxSettings"+ n
  4594. // recalculate fxName according to specificity rules
  4595. , fxName = o[sName] =
  4596. o[sName] // options.west.fxName_open
  4597. || d[sName] // options.panes.fxName_open
  4598. || o.fxName // options.west.fxName
  4599. || d.fxName // options.panes.fxName
  4600. || "none" // MEANS $.layout.defaults.panes.fxName == "" || false || null || 0
  4601. , fxExists = $.effects && ($.effects[fxName] || ($.effects.effect && $.effects.effect[fxName]))
  4602. ;
  4603. // validate fxName to ensure is valid effect - MUST have effect-config data in options.effects
  4604. if (fxName === "none" || !options.effects[fxName] || !fxExists)
  4605. fxName = o[sName] = "none"; // effect not loaded OR unrecognized fxName
  4606. // set vars for effects subkeys to simplify logic
  4607. var fx = options.effects[fxName] || {} // effects.slide
  4608. , fx_all = fx.all || null // effects.slide.all
  4609. , fx_pane = fx[pane] || null // effects.slide.west
  4610. ;
  4611. // create fxSpeed[_open|_close|_size]
  4612. o[sSpeed] =
  4613. o[sSpeed] // options.west.fxSpeed_open
  4614. || d[sSpeed] // options.west.fxSpeed_open
  4615. || o.fxSpeed // options.west.fxSpeed
  4616. || d.fxSpeed // options.panes.fxSpeed
  4617. || null // DEFAULT - let fxSetting.duration control speed
  4618. ;
  4619. // create fxSettings[_open|_close|_size]
  4620. o[sSettings] = $.extend(
  4621. true
  4622. , {}
  4623. , fx_all // effects.slide.all
  4624. , fx_pane // effects.slide.west
  4625. , d.fxSettings // options.panes.fxSettings
  4626. , o.fxSettings // options.west.fxSettings
  4627. , d[sSettings] // options.panes.fxSettings_open
  4628. , o[sSettings] // options.west.fxSettings_open
  4629. );
  4630. });
  4631. // DONE creating action-specific-settings for this pane,
  4632. // so DELETE generic options - are no longer meaningful
  4633. delete o.fxName;
  4634. delete o.fxSpeed;
  4635. delete o.fxSettings;
  4636. }
  4637. }
  4638. /**
  4639. * Initialize module objects, styling, size and position for all panes
  4640. *
  4641. * @see _initElements()
  4642. * @param {string} pane The pane to process
  4643. */
  4644. , getPane = function (pane) {
  4645. var sel = options[pane].paneSelector
  4646. if (sel.substr(0,1)==="#") // ID selector
  4647. // NOTE: elements selected 'by ID' DO NOT have to be 'children'
  4648. return $N.find(sel).eq(0);
  4649. else { // class or other selector
  4650. var $P = $N.children(sel).eq(0);
  4651. // look for the pane nested inside a 'form' element
  4652. return $P.length ? $P : $N.children("form:first").children(sel).eq(0);
  4653. }
  4654. }
  4655. /**
  4656. * @param {Object=} evt
  4657. */
  4658. , initPanes = function (evt) {
  4659. // stopPropagation if called by trigger("layoutinitpanes") - use evtPane utility
  4660. evtPane(evt);
  4661. // NOTE: do north & south FIRST so we can measure their height - do center LAST
  4662. $.each(_c.allPanes, function (idx, pane) {
  4663. addPane( pane, true );
  4664. });
  4665. // init the pane-handles NOW in case we have to hide or close the pane below
  4666. initHandles();
  4667. // now that all panes have been initialized and initially-sized,
  4668. // make sure there is really enough space available for each pane
  4669. $.each(_c.borderPanes, function (i, pane) {
  4670. if ($Ps[pane] && state[pane].isVisible) { // pane is OPEN
  4671. setSizeLimits(pane);
  4672. makePaneFit(pane); // pane may be Closed, Hidden or Resized by makePaneFit()
  4673. }
  4674. });
  4675. // size center-pane AGAIN in case we 'closed' a border-pane in loop above
  4676. sizeMidPanes("center");
  4677. // Chrome/Webkit sometimes fires callbacks BEFORE it completes resizing!
  4678. // Before RC30.3, there was a 10ms delay here, but that caused layout
  4679. // to load asynchrously, which is BAD, so try skipping delay for now
  4680. // process pane contents and callbacks, and init/resize child-layout if exists
  4681. $.each(_c.allPanes, function (idx, pane) {
  4682. afterInitPane(pane);
  4683. });
  4684. }
  4685. /**
  4686. * Add a pane to the layout - subroutine of initPanes()
  4687. *
  4688. * @see initPanes()
  4689. * @param {string} pane The pane to process
  4690. * @param {boolean=} [force=false] Size content after init
  4691. */
  4692. , addPane = function (pane, force) {
  4693. if (!force && !isInitialized()) return;
  4694. var
  4695. o = options[pane]
  4696. , s = state[pane]
  4697. , c = _c[pane]
  4698. , dir = c.dir
  4699. , fx = s.fx
  4700. , spacing = o.spacing_open || 0
  4701. , isCenter = (pane === "center")
  4702. , CSS = {}
  4703. , $P = $Ps[pane]
  4704. , size, minSize, maxSize, child
  4705. ;
  4706. // if pane-pointer already exists, remove the old one first
  4707. if ($P)
  4708. removePane( pane, false, true, false );
  4709. else
  4710. $Cs[pane] = false; // init
  4711. $P = $Ps[pane] = getPane(pane);
  4712. if (!$P.length) {
  4713. $Ps[pane] = false; // logic
  4714. return;
  4715. }
  4716. // SAVE original Pane CSS
  4717. if (!$P.data("layoutCSS")) {
  4718. var props = "position,top,left,bottom,right,width,height,overflow,zIndex,display,backgroundColor,padding,margin,border";
  4719. $P.data("layoutCSS", styles($P, props));
  4720. }
  4721. // create alias for pane data in Instance - initHandles will add more
  4722. Instance[pane] = {
  4723. name: pane
  4724. , pane: $Ps[pane]
  4725. , content: $Cs[pane]
  4726. , options: options[pane]
  4727. , state: state[pane]
  4728. , children: children[pane]
  4729. };
  4730. // add classes, attributes & events
  4731. $P .data({
  4732. parentLayout: Instance // pointer to Layout Instance
  4733. , layoutPane: Instance[pane] // NEW pointer to pane-alias-object
  4734. , layoutEdge: pane
  4735. , layoutRole: "pane"
  4736. })
  4737. .css(c.cssReq).css("zIndex", options.zIndexes.pane_normal)
  4738. .css(o.applyDemoStyles ? c.cssDemo : {}) // demo styles
  4739. .addClass( o.paneClass +" "+ o.paneClass+"-"+pane ) // default = "ui-layout-pane ui-layout-pane-west" - may be a dupe of 'paneSelector'
  4740. .bind("mouseenter."+ sID, addHover )
  4741. .bind("mouseleave."+ sID, removeHover )
  4742. ;
  4743. var paneMethods = {
  4744. hide: ''
  4745. , show: ''
  4746. , toggle: ''
  4747. , close: ''
  4748. , open: ''
  4749. , slideOpen: ''
  4750. , slideClose: ''
  4751. , slideToggle: ''
  4752. , size: 'sizePane'
  4753. , sizePane: 'sizePane'
  4754. , sizeContent: ''
  4755. , sizeHandles: ''
  4756. , enableClosable: ''
  4757. , disableClosable: ''
  4758. , enableSlideable: ''
  4759. , disableSlideable: ''
  4760. , enableResizable: ''
  4761. , disableResizable: ''
  4762. , swapPanes: 'swapPanes'
  4763. , swap: 'swapPanes'
  4764. , move: 'swapPanes'
  4765. , removePane: 'removePane'
  4766. , remove: 'removePane'
  4767. , createChildren: ''
  4768. , resizeChildren: ''
  4769. , resizeAll: 'resizeAll'
  4770. , resizeLayout: 'resizeAll'
  4771. }
  4772. , name;
  4773. // loop hash and bind all methods - include layoutID namespacing
  4774. for (name in paneMethods) {
  4775. $P.bind("layoutpane"+ name.toLowerCase() +"."+ sID, Instance[ paneMethods[name] || name ]);
  4776. }
  4777. // see if this pane has a 'scrolling-content element'
  4778. initContent(pane, false); // false = do NOT sizeContent() - called later
  4779. if (!isCenter) {
  4780. // call _parseSize AFTER applying pane classes & styles - but before making visible (if hidden)
  4781. // if o.size is auto or not valid, then MEASURE the pane and use that as its 'size'
  4782. size = s.size = _parseSize(pane, o.size);
  4783. minSize = _parseSize(pane,o.minSize) || 1;
  4784. maxSize = _parseSize(pane,o.maxSize) || 100000;
  4785. if (size > 0) size = max(min(size, maxSize), minSize);
  4786. s.autoResize = o.autoResize; // used with percentage sizes
  4787. // state for border-panes
  4788. s.isClosed = false; // true = pane is closed
  4789. s.isSliding = false; // true = pane is currently open by 'sliding' over adjacent panes
  4790. s.isResizing= false; // true = pane is in process of being resized
  4791. s.isHidden = false; // true = pane is hidden - no spacing, resizer or toggler is visible!
  4792. // array for 'pin buttons' whose classNames are auto-updated on pane-open/-close
  4793. if (!s.pins) s.pins = [];
  4794. }
  4795. // states common to ALL panes
  4796. s.tagName = $P[0].tagName;
  4797. s.edge = pane; // useful if pane is (or about to be) 'swapped' - easy find out where it is (or is going)
  4798. s.noRoom = false; // true = pane 'automatically' hidden due to insufficient room - will unhide automatically
  4799. s.isVisible = true; // false = pane is invisible - closed OR hidden - simplify logic
  4800. // init pane positioning
  4801. setPanePosition( pane );
  4802. // if pane is not visible,
  4803. if (dir === "horz") // north or south pane
  4804. CSS.height = cssH($P, size);
  4805. else if (dir === "vert") // east or west pane
  4806. CSS.width = cssW($P, size);
  4807. //else if (isCenter) {}
  4808. $P.css(CSS); // apply size -- top, bottom & height will be set by sizeMidPanes
  4809. if (dir != "horz") sizeMidPanes(pane, true); // true = skipCallback
  4810. // if manually adding a pane AFTER layout initialization, then...
  4811. if (state.initialized) {
  4812. initHandles( pane );
  4813. initHotkeys( pane );
  4814. }
  4815. // close or hide the pane if specified in settings
  4816. if (o.initClosed && o.closable && !o.initHidden)
  4817. close(pane, true, true); // true, true = force, noAnimation
  4818. else if (o.initHidden || o.initClosed)
  4819. hide(pane); // will be completely invisible - no resizer or spacing
  4820. else if (!s.noRoom)
  4821. // make the pane visible - in case was initially hidden
  4822. $P.css("display","block");
  4823. // ELSE setAsOpen() - called later by initHandles()
  4824. // RESET visibility now - pane will appear IF display:block
  4825. $P.css("visibility","visible");
  4826. // check option for auto-handling of pop-ups & drop-downs
  4827. if (o.showOverflowOnHover)
  4828. $P.hover( allowOverflow, resetOverflow );
  4829. // if manually adding a pane AFTER layout initialization, then...
  4830. if (state.initialized) {
  4831. afterInitPane( pane );
  4832. }
  4833. }
  4834. , afterInitPane = function (pane) {
  4835. var $P = $Ps[pane]
  4836. , s = state[pane]
  4837. , o = options[pane]
  4838. ;
  4839. if (!$P) return;
  4840. // see if there is a directly-nested layout inside this pane
  4841. if ($P.data("layout"))
  4842. refreshChildren( pane, $P.data("layout") );
  4843. // process pane contents and callbacks, and init/resize child-layout if exists
  4844. if (s.isVisible) { // pane is OPEN
  4845. if (state.initialized) // this pane was added AFTER layout was created
  4846. resizeAll(); // will also sizeContent
  4847. else
  4848. sizeContent(pane);
  4849. if (o.triggerEventsOnLoad)
  4850. _runCallbacks("onresize_end", pane);
  4851. else // automatic if onresize called, otherwise call it specifically
  4852. // resize child - IF inner-layout already exists (created before this layout)
  4853. resizeChildren(pane, true); // a previously existing childLayout
  4854. }
  4855. // init childLayouts - even if pane is not visible
  4856. if (o.initChildren && o.children)
  4857. createChildren(pane);
  4858. }
  4859. /**
  4860. * @param {string=} panes The pane(s) to process
  4861. */
  4862. , setPanePosition = function (panes) {
  4863. panes = panes ? panes.split(",") : _c.borderPanes;
  4864. // create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV
  4865. $.each(panes, function (i, pane) {
  4866. var $P = $Ps[pane]
  4867. , $R = $Rs[pane]
  4868. , o = options[pane]
  4869. , s = state[pane]
  4870. , side = _c[pane].side
  4871. , CSS = {}
  4872. ;
  4873. if (!$P) return; // pane does not exist - skip
  4874. // set css-position to account for container borders & padding
  4875. switch (pane) {
  4876. case "north": CSS.top = sC.inset.top;
  4877. CSS.left = sC.inset.left;
  4878. CSS.right = sC.inset.right;
  4879. break;
  4880. case "south": CSS.bottom = sC.inset.bottom;
  4881. CSS.left = sC.inset.left;
  4882. CSS.right = sC.inset.right;
  4883. break;
  4884. case "west": CSS.left = sC.inset.left; // top, bottom & height set by sizeMidPanes()
  4885. break;
  4886. case "east": CSS.right = sC.inset.right; // ditto
  4887. break;
  4888. case "center": // top, left, width & height set by sizeMidPanes()
  4889. }
  4890. // apply position
  4891. $P.css(CSS);
  4892. // update resizer position
  4893. if ($R && s.isClosed)
  4894. $R.css(side, sC.inset[side]);
  4895. else if ($R && !s.isHidden)
  4896. $R.css(side, sC.inset[side] + getPaneSize(pane));
  4897. });
  4898. }
  4899. /**
  4900. * Initialize module objects, styling, size and position for all resize bars and toggler buttons
  4901. *
  4902. * @see _create()
  4903. * @param {string=} [panes=""] The edge(s) to process
  4904. */
  4905. , initHandles = function (panes) {
  4906. panes = panes ? panes.split(",") : _c.borderPanes;
  4907. // create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV
  4908. $.each(panes, function (i, pane) {
  4909. var $P = $Ps[pane];
  4910. $Rs[pane] = false; // INIT
  4911. $Ts[pane] = false;
  4912. if (!$P) return; // pane does not exist - skip
  4913. var o = options[pane]
  4914. , s = state[pane]
  4915. , c = _c[pane]
  4916. , paneId = o.paneSelector.substr(0,1) === "#" ? o.paneSelector.substr(1) : ""
  4917. , rClass = o.resizerClass
  4918. , tClass = o.togglerClass
  4919. , spacing = (s.isVisible ? o.spacing_open : o.spacing_closed)
  4920. , _pane = "-"+ pane // used for classNames
  4921. , _state = (s.isVisible ? "-open" : "-closed") // used for classNames
  4922. , I = Instance[pane]
  4923. // INIT RESIZER BAR
  4924. , $R = I.resizer = $Rs[pane] = $("<div></div>")
  4925. // INIT TOGGLER BUTTON
  4926. , $T = I.toggler = (o.closable ? $Ts[pane] = $("<div></div>") : false)
  4927. ;
  4928. //if (s.isVisible && o.resizable) ... handled by initResizable
  4929. if (!s.isVisible && o.slidable)
  4930. $R.attr("title", o.tips.Slide).css("cursor", o.sliderCursor);
  4931. $R // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-resizer"
  4932. .attr("id", paneId ? paneId +"-resizer" : "" )
  4933. .data({
  4934. parentLayout: Instance
  4935. , layoutPane: Instance[pane] // NEW pointer to pane-alias-object
  4936. , layoutEdge: pane
  4937. , layoutRole: "resizer"
  4938. })
  4939. .css(_c.resizers.cssReq).css("zIndex", options.zIndexes.resizer_normal)
  4940. .css(o.applyDemoStyles ? _c.resizers.cssDemo : {}) // add demo styles
  4941. .addClass(rClass +" "+ rClass+_pane)
  4942. .hover(addHover, removeHover) // ALWAYS add hover-classes, even if resizing is not enabled - handle with CSS instead
  4943. .hover(onResizerEnter, onResizerLeave) // ALWAYS NEED resizer.mouseleave to balance toggler.mouseenter
  4944. .appendTo($N) // append DIV to container
  4945. ;
  4946. if (o.resizerDblClickToggle)
  4947. $R.bind("dblclick."+ sID, toggle );
  4948. if ($T) {
  4949. $T // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "#paneLeft-toggler"
  4950. .attr("id", paneId ? paneId +"-toggler" : "" )
  4951. .data({
  4952. parentLayout: Instance
  4953. , layoutPane: Instance[pane] // NEW pointer to pane-alias-object
  4954. , layoutEdge: pane
  4955. , layoutRole: "toggler"
  4956. })
  4957. .css(_c.togglers.cssReq) // add base/required styles
  4958. .css(o.applyDemoStyles ? _c.togglers.cssDemo : {}) // add demo styles
  4959. .addClass(tClass +" "+ tClass+_pane)
  4960. .hover(addHover, removeHover) // ALWAYS add hover-classes, even if toggling is not enabled - handle with CSS instead
  4961. .bind("mouseenter", onResizerEnter) // NEED toggler.mouseenter because mouseenter MAY NOT fire on resizer
  4962. .appendTo($R) // append SPAN to resizer DIV
  4963. ;
  4964. // ADD INNER-SPANS TO TOGGLER
  4965. if (o.togglerContent_open) // ui-layout-open
  4966. $("<span>"+ o.togglerContent_open +"</span>")
  4967. .data({
  4968. layoutEdge: pane
  4969. , layoutRole: "togglerContent"
  4970. })
  4971. .data("layoutRole", "togglerContent")
  4972. .data("layoutEdge", pane)
  4973. .addClass("content content-open")
  4974. .css("display","none")
  4975. .appendTo( $T )
  4976. //.hover( addHover, removeHover ) // use ui-layout-toggler-west-hover .content-open instead!
  4977. ;
  4978. if (o.togglerContent_closed) // ui-layout-closed
  4979. $("<span>"+ o.togglerContent_closed +"</span>")
  4980. .data({
  4981. layoutEdge: pane
  4982. , layoutRole: "togglerContent"
  4983. })
  4984. .addClass("content content-closed")
  4985. .css("display","none")
  4986. .appendTo( $T )
  4987. //.hover( addHover, removeHover ) // use ui-layout-toggler-west-hover .content-closed instead!
  4988. ;
  4989. // ADD TOGGLER.click/.hover
  4990. enableClosable(pane);
  4991. }
  4992. // add Draggable events
  4993. initResizable(pane);
  4994. // ADD CLASSNAMES & SLIDE-BINDINGS - eg: class="resizer resizer-west resizer-open"
  4995. if (s.isVisible)
  4996. setAsOpen(pane); // onOpen will be called, but NOT onResize
  4997. else {
  4998. setAsClosed(pane); // onClose will be called
  4999. bindStartSlidingEvents(pane, true); // will enable events IF option is set
  5000. }
  5001. });
  5002. // SET ALL HANDLE DIMENSIONS
  5003. sizeHandles();
  5004. }
  5005. /**
  5006. * Initialize scrolling ui-layout-content div - if exists
  5007. *
  5008. * @see initPane() - or externally after an Ajax injection
  5009. * @param {string} pane The pane to process
  5010. * @param {boolean=} [resize=true] Size content after init
  5011. */
  5012. , initContent = function (pane, resize) {
  5013. if (!isInitialized()) return;
  5014. var
  5015. o = options[pane]
  5016. , sel = o.contentSelector
  5017. , I = Instance[pane]
  5018. , $P = $Ps[pane]
  5019. , $C
  5020. ;
  5021. if (sel) $C = I.content = $Cs[pane] = (o.findNestedContent)
  5022. ? $P.find(sel).eq(0) // match 1-element only
  5023. : $P.children(sel).eq(0)
  5024. ;
  5025. if ($C && $C.length) {
  5026. $C.data("layoutRole", "content");
  5027. // SAVE original Content CSS
  5028. if (!$C.data("layoutCSS"))
  5029. $C.data("layoutCSS", styles($C, "height"));
  5030. $C.css( _c.content.cssReq );
  5031. if (o.applyDemoStyles) {
  5032. $C.css( _c.content.cssDemo ); // add padding & overflow: auto to content-div
  5033. $P.css( _c.content.cssDemoPane ); // REMOVE padding/scrolling from pane
  5034. }
  5035. // ensure no vertical scrollbar on pane - will mess up measurements
  5036. if ($P.css("overflowX").match(/(scroll|auto)/)) {
  5037. $P.css("overflow", "hidden");
  5038. }
  5039. state[pane].content = {}; // init content state
  5040. if (resize !== false) sizeContent(pane);
  5041. // sizeContent() is called AFTER init of all elements
  5042. }
  5043. else
  5044. I.content = $Cs[pane] = false;
  5045. }
  5046. /**
  5047. * Add resize-bars to all panes that specify it in options
  5048. * -dependancy: $.fn.resizable - will skip if not found
  5049. *
  5050. * @see _create()
  5051. * @param {string=} [panes=""] The edge(s) to process
  5052. */
  5053. , initResizable = function (panes) {
  5054. var draggingAvailable = $.layout.plugins.draggable
  5055. , side // set in start()
  5056. ;
  5057. panes = panes ? panes.split(",") : _c.borderPanes;
  5058. $.each(panes, function (idx, pane) {
  5059. var o = options[pane];
  5060. if (!draggingAvailable || !$Ps[pane] || !o.resizable) {
  5061. o.resizable = false;
  5062. return true; // skip to next
  5063. }
  5064. var s = state[pane]
  5065. , z = options.zIndexes
  5066. , c = _c[pane]
  5067. , side = c.dir=="horz" ? "top" : "left"
  5068. , $P = $Ps[pane]
  5069. , $R = $Rs[pane]
  5070. , base = o.resizerClass
  5071. , lastPos = 0 // used when live-resizing
  5072. , r, live // set in start because may change
  5073. // 'drag' classes are applied to the ORIGINAL resizer-bar while dragging is in process
  5074. , resizerClass = base+"-drag" // resizer-drag
  5075. , resizerPaneClass = base+"-"+pane+"-drag" // resizer-north-drag
  5076. // 'helper' class is applied to the CLONED resizer-bar while it is being dragged
  5077. , helperClass = base+"-dragging" // resizer-dragging
  5078. , helperPaneClass = base+"-"+pane+"-dragging" // resizer-north-dragging
  5079. , helperLimitClass = base+"-dragging-limit" // resizer-drag
  5080. , helperPaneLimitClass = base+"-"+pane+"-dragging-limit" // resizer-north-drag
  5081. , helperClassesSet = false // logic var
  5082. ;
  5083. if (!s.isClosed)
  5084. $R.attr("title", o.tips.Resize)
  5085. .css("cursor", o.resizerCursor); // n-resize, s-resize, etc
  5086. $R.draggable({
  5087. containment: $N[0] // limit resizing to layout container
  5088. , axis: (c.dir=="horz" ? "y" : "x") // limit resizing to horz or vert axis
  5089. , delay: 0
  5090. , distance: 1
  5091. , grid: o.resizingGrid
  5092. // basic format for helper - style it using class: .ui-draggable-dragging
  5093. , helper: "clone"
  5094. , opacity: o.resizerDragOpacity
  5095. , addClasses: false // avoid ui-state-disabled class when disabled
  5096. //, iframeFix: o.draggableIframeFix // TODO: consider using when bug is fixed
  5097. , zIndex: z.resizer_drag
  5098. , start: function (e, ui) {
  5099. // REFRESH options & state pointers in case we used swapPanes
  5100. o = options[pane];
  5101. s = state[pane];
  5102. // re-read options
  5103. live = o.livePaneResizing;
  5104. // ondrag_start callback - will CANCEL hide if returns false
  5105. // TODO: dragging CANNOT be cancelled like this, so see if there is a way?
  5106. if (false === _runCallbacks("ondrag_start", pane)) return false;
  5107. s.isResizing = true; // prevent pane from closing while resizing
  5108. state.paneResizing = pane; // easy to see if ANY pane is resizing
  5109. timer.clear(pane+"_closeSlider"); // just in case already triggered
  5110. // SET RESIZER LIMITS - used in drag()
  5111. setSizeLimits(pane); // update pane/resizer state
  5112. r = s.resizerPosition;
  5113. lastPos = ui.position[ side ]
  5114. $R.addClass( resizerClass +" "+ resizerPaneClass ); // add drag classes
  5115. helperClassesSet = false; // reset logic var - see drag()
  5116. // DISABLE TEXT SELECTION (probably already done by resizer.mouseOver)
  5117. $('body').disableSelection();
  5118. // MASK PANES CONTAINING IFRAMES, APPLETS OR OTHER TROUBLESOME ELEMENTS
  5119. showMasks( pane, { resizing: true });
  5120. }
  5121. , drag: function (e, ui) {
  5122. if (!helperClassesSet) { // can only add classes after clone has been added to the DOM
  5123. //$(".ui-draggable-dragging")
  5124. ui.helper
  5125. .addClass( helperClass +" "+ helperPaneClass ) // add helper classes
  5126. .css({ right: "auto", bottom: "auto" }) // fix dir="rtl" issue
  5127. .children().css("visibility","hidden") // hide toggler inside dragged resizer-bar
  5128. ;
  5129. helperClassesSet = true;
  5130. // draggable bug!? RE-SET zIndex to prevent E/W resize-bar showing through N/S pane!
  5131. if (s.isSliding) $Ps[pane].css("zIndex", z.pane_sliding);
  5132. }
  5133. // CONTAIN RESIZER-BAR TO RESIZING LIMITS
  5134. var limit = 0;
  5135. if (ui.position[side] < r.min) {
  5136. ui.position[side] = r.min;
  5137. limit = -1;
  5138. }
  5139. else if (ui.position[side] > r.max) {
  5140. ui.position[side] = r.max;
  5141. limit = 1;
  5142. }
  5143. // ADD/REMOVE dragging-limit CLASS
  5144. if (limit) {
  5145. ui.helper.addClass( helperLimitClass +" "+ helperPaneLimitClass ); // at dragging-limit
  5146. window.defaultStatus = (limit>0 && pane.match(/(north|west)/)) || (limit<0 && pane.match(/(south|east)/)) ? o.tips.maxSizeWarning : o.tips.minSizeWarning;
  5147. }
  5148. else {
  5149. ui.helper.removeClass( helperLimitClass +" "+ helperPaneLimitClass ); // not at dragging-limit
  5150. window.defaultStatus = "";
  5151. }
  5152. // DYNAMICALLY RESIZE PANES IF OPTION ENABLED
  5153. // won't trigger unless resizer has actually moved!
  5154. if (live && Math.abs(ui.position[side] - lastPos) >= o.liveResizingTolerance) {
  5155. lastPos = ui.position[side];
  5156. resizePanes(e, ui, pane)
  5157. }
  5158. }
  5159. , stop: function (e, ui) {
  5160. $('body').enableSelection(); // RE-ENABLE TEXT SELECTION
  5161. window.defaultStatus = ""; // clear 'resizing limit' message from statusbar
  5162. $R.removeClass( resizerClass +" "+ resizerPaneClass ); // remove drag classes from Resizer
  5163. s.isResizing = false;
  5164. state.paneResizing = false; // easy to see if ANY pane is resizing
  5165. resizePanes(e, ui, pane, true); // true = resizingDone
  5166. }
  5167. });
  5168. });
  5169. /**
  5170. * resizePanes
  5171. *
  5172. * Sub-routine called from stop() - and drag() if livePaneResizing
  5173. *
  5174. * @param {!Object} evt
  5175. * @param {!Object} ui
  5176. * @param {string} pane
  5177. * @param {boolean=} [resizingDone=false]
  5178. */
  5179. var resizePanes = function (evt, ui, pane, resizingDone) {
  5180. var dragPos = ui.position
  5181. , c = _c[pane]
  5182. , o = options[pane]
  5183. , s = state[pane]
  5184. , resizerPos
  5185. ;
  5186. switch (pane) {
  5187. case "north": resizerPos = dragPos.top; break;
  5188. case "west": resizerPos = dragPos.left; break;
  5189. case "south": resizerPos = sC.layoutHeight - dragPos.top - o.spacing_open; break;
  5190. case "east": resizerPos = sC.layoutWidth - dragPos.left - o.spacing_open; break;
  5191. };
  5192. // remove container margin from resizer position to get the pane size
  5193. var newSize = resizerPos - sC.inset[c.side];
  5194. // Disable OR Resize Mask(s) created in drag.start
  5195. if (!resizingDone) {
  5196. // ensure we meet liveResizingTolerance criteria
  5197. if (Math.abs(newSize - s.size) < o.liveResizingTolerance)
  5198. return; // SKIP resize this time
  5199. // resize the pane
  5200. manualSizePane(pane, newSize, false, true); // true = noAnimation
  5201. sizeMasks(); // resize all visible masks
  5202. }
  5203. else { // resizingDone
  5204. // ondrag_end callback
  5205. if (false !== _runCallbacks("ondrag_end", pane))
  5206. manualSizePane(pane, newSize, false, true); // true = noAnimation
  5207. hideMasks(true); // true = force hiding all masks even if one is 'sliding'
  5208. if (s.isSliding) // RE-SHOW 'object-masks' so objects won't show through sliding pane
  5209. showMasks( pane, { resizing: true });
  5210. }
  5211. };
  5212. }
  5213. /**
  5214. * sizeMask
  5215. *
  5216. * Needed to overlay a DIV over an IFRAME-pane because mask CANNOT be *inside* the pane
  5217. * Called when mask created, and during livePaneResizing
  5218. */
  5219. , sizeMask = function () {
  5220. var $M = $(this)
  5221. , pane = $M.data("layoutMask") // eg: "west"
  5222. , s = state[pane]
  5223. ;
  5224. // only masks over an IFRAME-pane need manual resizing
  5225. if (s.tagName == "IFRAME" && s.isVisible) // no need to mask closed/hidden panes
  5226. $M.css({
  5227. top: s.offsetTop
  5228. , left: s.offsetLeft
  5229. , width: s.outerWidth
  5230. , height: s.outerHeight
  5231. });
  5232. /* ALT Method...
  5233. var $P = $Ps[pane];
  5234. $M.css( $P.position() ).css({ width: $P[0].offsetWidth, height: $P[0].offsetHeight });
  5235. */
  5236. }
  5237. , sizeMasks = function () {
  5238. $Ms.each( sizeMask ); // resize all 'visible' masks
  5239. }
  5240. /**
  5241. * @param {string} pane The pane being resized, animated or isSliding
  5242. * @param {Object=} [args] (optional) Options: which masks to apply, and to which panes
  5243. */
  5244. , showMasks = function (pane, args) {
  5245. var c = _c[pane]
  5246. , panes = ["center"]
  5247. , z = options.zIndexes
  5248. , a = $.extend({
  5249. objectsOnly: false
  5250. , animation: false
  5251. , resizing: true
  5252. , sliding: state[pane].isSliding
  5253. }, args )
  5254. , o, s
  5255. ;
  5256. if (a.resizing)
  5257. panes.push( pane );
  5258. if (a.sliding)
  5259. panes.push( _c.oppositeEdge[pane] ); // ADD the oppositeEdge-pane
  5260. if (c.dir === "horz") {
  5261. panes.push("west");
  5262. panes.push("east");
  5263. }
  5264. $.each(panes, function(i,p){
  5265. s = state[p];
  5266. o = options[p];
  5267. if (s.isVisible && ( o.maskObjects || (!a.objectsOnly && o.maskContents) )) {
  5268. getMasks(p).each(function(){
  5269. sizeMask.call(this);
  5270. this.style.zIndex = s.isSliding ? z.pane_sliding+1 : z.pane_normal+1
  5271. this.style.display = "block";
  5272. });
  5273. }
  5274. });
  5275. }
  5276. /**
  5277. * @param {boolean=} force Hide masks even if a pane is sliding
  5278. */
  5279. , hideMasks = function (force) {
  5280. // ensure no pane is resizing - could be a timing issue
  5281. if (force || !state.paneResizing) {
  5282. $Ms.hide(); // hide ALL masks
  5283. }
  5284. // if ANY pane is sliding, then DO NOT remove masks from panes with maskObjects enabled
  5285. else if (!force && !$.isEmptyObject( state.panesSliding )) {
  5286. var i = $Ms.length - 1
  5287. , p, $M;
  5288. for (; i >= 0; i--) {
  5289. $M = $Ms.eq(i);
  5290. p = $M.data("layoutMask");
  5291. if (!options[p].maskObjects) {
  5292. $M.hide();
  5293. }
  5294. }
  5295. }
  5296. }
  5297. /**
  5298. * @param {string} pane
  5299. */
  5300. , getMasks = function (pane) {
  5301. var $Masks = $([])
  5302. , $M, i = 0, c = $Ms.length
  5303. ;
  5304. for (; i<c; i++) {
  5305. $M = $Ms.eq(i);
  5306. if ($M.data("layoutMask") === pane)
  5307. $Masks = $Masks.add( $M );
  5308. }
  5309. if ($Masks.length)
  5310. return $Masks;
  5311. else
  5312. return createMasks(pane);
  5313. }
  5314. /**
  5315. * createMasks
  5316. *
  5317. * Generates both DIV (ALWAYS used) and IFRAME (optional) elements as masks
  5318. * An IFRAME mask is created *under* the DIV when maskObjects=true, because a DIV cannot mask an applet
  5319. *
  5320. * @param {string} pane
  5321. */
  5322. , createMasks = function (pane) {
  5323. var
  5324. $P = $Ps[pane]
  5325. , s = state[pane]
  5326. , o = options[pane]
  5327. , z = options.zIndexes
  5328. //, objMask = o.maskObjects && s.tagName != "IFRAME" // check for option
  5329. , $Masks = $([])
  5330. , isIframe, el, $M, css, i
  5331. ;
  5332. if (!o.maskContents && !o.maskObjects) return $Masks;
  5333. // if o.maskObjects=true, then loop TWICE to create BOTH kinds of mask, else only create a DIV
  5334. for (i=0; i < (o.maskObjects ? 2 : 1); i++) {
  5335. isIframe = o.maskObjects && i==0;
  5336. el = document.createElement( isIframe ? "iframe" : "div" );
  5337. $M = $(el).data("layoutMask", pane); // add data to relate mask to pane
  5338. el.className = "ui-layout-mask ui-layout-mask-"+ pane; // for user styling
  5339. css = el.style;
  5340. // styles common to both DIVs and IFRAMES
  5341. css.display = "block";
  5342. css.position = "absolute";
  5343. css.background = "#FFF";
  5344. if (isIframe) { // IFRAME-only props
  5345. el.frameborder = 0;
  5346. el.src = "about:blank";
  5347. //el.allowTransparency = true; - for IE, but breaks masking ability!
  5348. css.opacity = 0;
  5349. css.filter = "Alpha(Opacity='0')";
  5350. css.border = 0;
  5351. }
  5352. // if pane is an IFRAME, then must mask the pane itself
  5353. if (s.tagName == "IFRAME") {
  5354. // NOTE sizing done by a subroutine so can be called during live-resizing
  5355. css.zIndex = z.pane_normal+1; // 1-higher than pane
  5356. $N.append( el ); // append to LAYOUT CONTAINER
  5357. }
  5358. // otherwise put masks *inside the pane* to mask its contents
  5359. else {
  5360. $M.addClass("ui-layout-mask-inside-pane");
  5361. css.zIndex = o.maskZindex || z.content_mask; // usually 1, but customizable
  5362. css.top = 0;
  5363. css.left = 0;
  5364. css.width = "100%";
  5365. css.height = "100%";
  5366. $P.append( el ); // append INSIDE pane element
  5367. }
  5368. // add to return object
  5369. $Masks = $Masks.add( el );
  5370. // add Mask to cached array so can be resized & reused
  5371. $Ms = $Ms.add( el );
  5372. }
  5373. return $Masks;
  5374. }
  5375. /**
  5376. * Destroy this layout and reset all elements
  5377. *
  5378. * @param {boolean=} [destroyChildren=false] Destory Child-Layouts first?
  5379. */
  5380. , destroy = function (evt_or_destroyChildren, destroyChildren) {
  5381. // UNBIND layout events and remove global object
  5382. $(window).unbind("."+ sID); // resize & unload
  5383. $(document).unbind("."+ sID); // keyDown (hotkeys)
  5384. if (typeof evt_or_destroyChildren === "object")
  5385. // stopPropagation if called by trigger("layoutdestroy") - use evtPane utility
  5386. evtPane(evt_or_destroyChildren);
  5387. else // no event, so transfer 1st param to destroyChildren param
  5388. destroyChildren = evt_or_destroyChildren;
  5389. // need to look for parent layout BEFORE we remove the container data, else skips a level
  5390. //var parentPane = Instance.hasParentLayout ? $.layout.getParentPaneInstance( $N ) : null;
  5391. // reset layout-container
  5392. $N .clearQueue()
  5393. .removeData("layout")
  5394. .removeData("layoutContainer")
  5395. .removeClass(options.containerClass)
  5396. .unbind("."+ sID) // remove ALL Layout events
  5397. ;
  5398. // remove all mask elements that have been created
  5399. $Ms.remove();
  5400. // loop all panes to remove layout classes, attributes and bindings
  5401. $.each(_c.allPanes, function (i, pane) {
  5402. removePane( pane, false, true, destroyChildren ); // true = skipResize
  5403. });
  5404. // do NOT reset container CSS if is a 'pane' (or 'content') in an outer-layout - ie, THIS layout is 'nested'
  5405. var css = "layoutCSS";
  5406. if ($N.data(css) && !$N.data("layoutRole")) // RESET CSS
  5407. $N.css( $N.data(css) ).removeData(css);
  5408. // for full-page layouts, also reset the <HTML> CSS
  5409. if (sC.tagName === "BODY" && ($N = $("html")).data(css)) // RESET <HTML> CSS
  5410. $N.css( $N.data(css) ).removeData(css);
  5411. // trigger plugins for this layout, if there are any
  5412. runPluginCallbacks( Instance, $.layout.onDestroy );
  5413. // trigger state-management and onunload callback
  5414. unload();
  5415. // clear the Instance of everything except for container & options (so could recreate)
  5416. // RE-CREATE: myLayout = myLayout.container.layout( myLayout.options );
  5417. for (var n in Instance)
  5418. if (!n.match(/^(container|options)$/)) delete Instance[ n ];
  5419. // add a 'destroyed' flag to make it easy to check
  5420. Instance.destroyed = true;
  5421. // if this is a child layout, CLEAR the child-pointer in the parent
  5422. /* for now the pointer REMAINS, but with only container, options and destroyed keys
  5423. if (parentPane) {
  5424. var layout = parentPane.pane.data("parentLayout")
  5425. , key = layout.options.instanceKey || 'error';
  5426. // THIS SYNTAX MAY BE WRONG!
  5427. parentPane.children[key] = layout.children[ parentPane.name ].children[key] = null;
  5428. }
  5429. */
  5430. return Instance; // for coding convenience
  5431. }
  5432. /**
  5433. * Remove a pane from the layout - subroutine of destroy()
  5434. *
  5435. * @see destroy()
  5436. * @param {(string|Object)} evt_or_pane The pane to process
  5437. * @param {boolean=} [remove=false] Remove the DOM element?
  5438. * @param {boolean=} [skipResize=false] Skip calling resizeAll()?
  5439. * @param {boolean=} [destroyChild=true] Destroy Child-layouts? If not passed, obeys options setting
  5440. */
  5441. , removePane = function (evt_or_pane, remove, skipResize, destroyChild) {
  5442. if (!isInitialized()) return;
  5443. var pane = evtPane.call(this, evt_or_pane)
  5444. , $P = $Ps[pane]
  5445. , $C = $Cs[pane]
  5446. , $R = $Rs[pane]
  5447. , $T = $Ts[pane]
  5448. ;
  5449. // NOTE: elements can still exist even after remove()
  5450. // so check for missing data(), which is cleared by removed()
  5451. if ($P && $.isEmptyObject( $P.data() )) $P = false;
  5452. if ($C && $.isEmptyObject( $C.data() )) $C = false;
  5453. if ($R && $.isEmptyObject( $R.data() )) $R = false;
  5454. if ($T && $.isEmptyObject( $T.data() )) $T = false;
  5455. if ($P) $P.stop(true, true);
  5456. var o = options[pane]
  5457. , s = state[pane]
  5458. , d = "layout"
  5459. , css = "layoutCSS"
  5460. , pC = children[pane]
  5461. , hasChildren = $.isPlainObject( pC ) && !$.isEmptyObject( pC )
  5462. , destroy = destroyChild !== undefined ? destroyChild : o.destroyChildren
  5463. ;
  5464. // FIRST destroy the child-layout(s)
  5465. if (hasChildren && destroy) {
  5466. $.each( pC, function (key, child) {
  5467. if (!child.destroyed)
  5468. child.destroy(true);// tell child-layout to destroy ALL its child-layouts too
  5469. if (child.destroyed) // destroy was successful
  5470. delete pC[key];
  5471. });
  5472. // if no more children, remove the children hash
  5473. if ($.isEmptyObject( pC )) {
  5474. pC = children[pane] = null; // clear children hash
  5475. hasChildren = false;
  5476. }
  5477. }
  5478. // Note: can't 'remove' a pane element with non-destroyed children
  5479. if ($P && remove && !hasChildren)
  5480. $P.remove(); // remove the pane-element and everything inside it
  5481. else if ($P && $P[0]) {
  5482. // create list of ALL pane-classes that need to be removed
  5483. var root = o.paneClass // default="ui-layout-pane"
  5484. , pRoot = root +"-"+ pane // eg: "ui-layout-pane-west"
  5485. , _open = "-open"
  5486. , _sliding= "-sliding"
  5487. , _closed = "-closed"
  5488. , classes = [ root, root+_open, root+_closed, root+_sliding, // generic classes
  5489. pRoot, pRoot+_open, pRoot+_closed, pRoot+_sliding ] // pane-specific classes
  5490. ;
  5491. $.merge(classes, getHoverClasses($P, true)); // ADD hover-classes
  5492. // remove all Layout classes from pane-element
  5493. $P .removeClass( classes.join(" ") ) // remove ALL pane-classes
  5494. .removeData("parentLayout")
  5495. .removeData("layoutPane")
  5496. .removeData("layoutRole")
  5497. .removeData("layoutEdge")
  5498. .removeData("autoHidden") // in case set
  5499. .unbind("."+ sID) // remove ALL Layout events
  5500. // TODO: remove these extra unbind commands when jQuery is fixed
  5501. //.unbind("mouseenter"+ sID)
  5502. //.unbind("mouseleave"+ sID)
  5503. ;
  5504. // do NOT reset CSS if this pane/content is STILL the container of a nested layout!
  5505. // the nested layout will reset its 'container' CSS when/if it is destroyed
  5506. if (hasChildren && $C) {
  5507. // a content-div may not have a specific width, so give it one to contain the Layout
  5508. $C.width( $C.width() );
  5509. $.each( pC, function (key, child) {
  5510. child.resizeAll(); // resize the Layout
  5511. });
  5512. }
  5513. else if ($C)
  5514. $C.css( $C.data(css) ).removeData(css).removeData("layoutRole");
  5515. // remove pane AFTER content in case there was a nested layout
  5516. if (!$P.data(d))
  5517. $P.css( $P.data(css) ).removeData(css);
  5518. }
  5519. // REMOVE pane resizer and toggler elements
  5520. if ($T) $T.remove();
  5521. if ($R) $R.remove();
  5522. // CLEAR all pointers and state data
  5523. Instance[pane] = $Ps[pane] = $Cs[pane] = $Rs[pane] = $Ts[pane] = false;
  5524. s = { removed: true };
  5525. if (!skipResize)
  5526. resizeAll();
  5527. }
  5528. /*
  5529. * ###########################
  5530. * ACTION METHODS
  5531. * ###########################
  5532. */
  5533. /**
  5534. * @param {string} pane
  5535. */
  5536. , _hidePane = function (pane) {
  5537. var $P = $Ps[pane]
  5538. , o = options[pane]
  5539. , s = $P[0].style
  5540. ;
  5541. if (o.useOffscreenClose) {
  5542. if (!$P.data(_c.offscreenReset))
  5543. $P.data(_c.offscreenReset, { left: s.left, right: s.right });
  5544. $P.css( _c.offscreenCSS );
  5545. }
  5546. else
  5547. $P.hide().removeData(_c.offscreenReset);
  5548. }
  5549. /**
  5550. * @param {string} pane
  5551. */
  5552. , _showPane = function (pane) {
  5553. var $P = $Ps[pane]
  5554. , o = options[pane]
  5555. , off = _c.offscreenCSS
  5556. , old = $P.data(_c.offscreenReset)
  5557. , s = $P[0].style
  5558. ;
  5559. $P .show() // ALWAYS show, just in case
  5560. .removeData(_c.offscreenReset);
  5561. if (o.useOffscreenClose && old) {
  5562. if (s.left == off.left)
  5563. s.left = old.left;
  5564. if (s.right == off.right)
  5565. s.right = old.right;
  5566. }
  5567. }
  5568. /**
  5569. * Completely 'hides' a pane, including its spacing - as if it does not exist
  5570. * The pane is not actually 'removed' from the source, so can use 'show' to un-hide it
  5571. *
  5572. * @param {(string|Object)} evt_or_pane The pane being hidden, ie: north, south, east, or west
  5573. * @param {boolean=} [noAnimation=false]
  5574. */
  5575. , hide = function (evt_or_pane, noAnimation) {
  5576. if (!isInitialized()) return;
  5577. var pane = evtPane.call(this, evt_or_pane)
  5578. , o = options[pane]
  5579. , s = state[pane]
  5580. , $P = $Ps[pane]
  5581. , $R = $Rs[pane]
  5582. ;
  5583. if (!$P || s.isHidden) return; // pane does not exist OR is already hidden
  5584. // onhide_start callback - will CANCEL hide if returns false
  5585. if (state.initialized && false === _runCallbacks("onhide_start", pane)) return;
  5586. s.isSliding = false; // just in case
  5587. delete state.panesSliding[pane];
  5588. // now hide the elements
  5589. if ($R) $R.hide(); // hide resizer-bar
  5590. if (!state.initialized || s.isClosed) {
  5591. s.isClosed = true; // to trigger open-animation on show()
  5592. s.isHidden = true;
  5593. s.isVisible = false;
  5594. if (!state.initialized)
  5595. _hidePane(pane); // no animation when loading page
  5596. sizeMidPanes(_c[pane].dir === "horz" ? "" : "center");
  5597. if (state.initialized || o.triggerEventsOnLoad)
  5598. _runCallbacks("onhide_end", pane);
  5599. }
  5600. else {
  5601. s.isHiding = true; // used by onclose
  5602. close(pane, false, noAnimation); // adjust all panes to fit
  5603. }
  5604. }
  5605. /**
  5606. * Show a hidden pane - show as 'closed' by default unless openPane = true
  5607. *
  5608. * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west
  5609. * @param {boolean=} [openPane=false]
  5610. * @param {boolean=} [noAnimation=false]
  5611. * @param {boolean=} [noAlert=false]
  5612. */
  5613. , show = function (evt_or_pane, openPane, noAnimation, noAlert) {
  5614. if (!isInitialized()) return;
  5615. var pane = evtPane.call(this, evt_or_pane)
  5616. , o = options[pane]
  5617. , s = state[pane]
  5618. , $P = $Ps[pane]
  5619. , $R = $Rs[pane]
  5620. ;
  5621. if (!$P || !s.isHidden) return; // pane does not exist OR is not hidden
  5622. // onshow_start callback - will CANCEL show if returns false
  5623. if (false === _runCallbacks("onshow_start", pane)) return;
  5624. s.isShowing = true; // used by onopen/onclose
  5625. //s.isHidden = false; - will be set by open/close - if not cancelled
  5626. s.isSliding = false; // just in case
  5627. delete state.panesSliding[pane];
  5628. // now show the elements
  5629. //if ($R) $R.show(); - will be shown by open/close
  5630. if (openPane === false)
  5631. close(pane, true); // true = force
  5632. else
  5633. open(pane, false, noAnimation, noAlert); // adjust all panes to fit
  5634. }
  5635. /**
  5636. * Toggles a pane open/closed by calling either open or close
  5637. *
  5638. * @param {(string|Object)} evt_or_pane The pane being toggled, ie: north, south, east, or west
  5639. * @param {boolean=} [slide=false]
  5640. */
  5641. , toggle = function (evt_or_pane, slide) {
  5642. if (!isInitialized()) return;
  5643. var evt = evtObj(evt_or_pane)
  5644. , pane = evtPane.call(this, evt_or_pane)
  5645. , s = state[pane]
  5646. ;
  5647. if (evt) // called from to $R.dblclick OR triggerPaneEvent
  5648. evt.stopImmediatePropagation();
  5649. if (s.isHidden)
  5650. show(pane); // will call 'open' after unhiding it
  5651. else if (s.isClosed)
  5652. open(pane, !!slide);
  5653. else
  5654. close(pane);
  5655. }
  5656. /**
  5657. * Utility method used during init or other auto-processes
  5658. *
  5659. * @param {string} pane The pane being closed
  5660. * @param {boolean=} [setHandles=false]
  5661. */
  5662. , _closePane = function (pane, setHandles) {
  5663. var
  5664. $P = $Ps[pane]
  5665. , s = state[pane]
  5666. ;
  5667. _hidePane(pane);
  5668. s.isClosed = true;
  5669. s.isVisible = false;
  5670. if (setHandles) setAsClosed(pane);
  5671. }
  5672. /**
  5673. * Close the specified pane (animation optional), and resize all other panes as needed
  5674. *
  5675. * @param {(string|Object)} evt_or_pane The pane being closed, ie: north, south, east, or west
  5676. * @param {boolean=} [force=false]
  5677. * @param {boolean=} [noAnimation=false]
  5678. * @param {boolean=} [skipCallback=false]
  5679. */
  5680. , close = function (evt_or_pane, force, noAnimation, skipCallback) {
  5681. var pane = evtPane.call(this, evt_or_pane);
  5682. // if pane has been initialized, but NOT the complete layout, close pane instantly
  5683. if (!state.initialized && $Ps[pane]) {
  5684. _closePane(pane, true); // INIT pane as closed
  5685. return;
  5686. }
  5687. if (!isInitialized()) return;
  5688. var
  5689. $P = $Ps[pane]
  5690. , $R = $Rs[pane]
  5691. , $T = $Ts[pane]
  5692. , o = options[pane]
  5693. , s = state[pane]
  5694. , c = _c[pane]
  5695. , doFX, isShowing, isHiding, wasSliding;
  5696. // QUEUE in case another action/animation is in progress
  5697. $N.queue(function( queueNext ){
  5698. if ( !$P
  5699. || (!o.closable && !s.isShowing && !s.isHiding) // invalid request // (!o.resizable && !o.closable) ???
  5700. || (!force && s.isClosed && !s.isShowing) // already closed
  5701. ) return queueNext();
  5702. // onclose_start callback - will CANCEL hide if returns false
  5703. // SKIP if just 'showing' a hidden pane as 'closed'
  5704. var abort = !s.isShowing && false === _runCallbacks("onclose_start", pane);
  5705. // transfer logic vars to temp vars
  5706. isShowing = s.isShowing;
  5707. isHiding = s.isHiding;
  5708. wasSliding = s.isSliding;
  5709. // now clear the logic vars (REQUIRED before aborting)
  5710. delete s.isShowing;
  5711. delete s.isHiding;
  5712. if (abort) return queueNext();
  5713. doFX = !noAnimation && !s.isClosed && (o.fxName_close != "none");
  5714. s.isMoving = true;
  5715. s.isClosed = true;
  5716. s.isVisible = false;
  5717. // update isHidden BEFORE sizing panes
  5718. if (isHiding) s.isHidden = true;
  5719. else if (isShowing) s.isHidden = false;
  5720. if (s.isSliding) // pane is being closed, so UNBIND trigger events
  5721. bindStopSlidingEvents(pane, false); // will set isSliding=false
  5722. else // resize panes adjacent to this one
  5723. sizeMidPanes(_c[pane].dir === "horz" ? "" : "center", false); // false = NOT skipCallback
  5724. // if this pane has a resizer bar, move it NOW - before animation
  5725. setAsClosed(pane);
  5726. // CLOSE THE PANE
  5727. if (doFX) { // animate the close
  5728. lockPaneForFX(pane, true); // need to set left/top so animation will work
  5729. $P.hide( o.fxName_close, o.fxSettings_close, o.fxSpeed_close, function () {
  5730. lockPaneForFX(pane, false); // undo
  5731. if (s.isClosed) close_2();
  5732. queueNext();
  5733. });
  5734. }
  5735. else { // hide the pane without animation
  5736. _hidePane(pane);
  5737. close_2();
  5738. queueNext();
  5739. };
  5740. });
  5741. // SUBROUTINE
  5742. function close_2 () {
  5743. s.isMoving = false;
  5744. bindStartSlidingEvents(pane, true); // will enable if o.slidable = true
  5745. // if opposite-pane was autoClosed, see if it can be autoOpened now
  5746. var altPane = _c.oppositeEdge[pane];
  5747. if (state[ altPane ].noRoom) {
  5748. setSizeLimits( altPane );
  5749. makePaneFit( altPane );
  5750. }
  5751. if (!skipCallback && (state.initialized || o.triggerEventsOnLoad)) {
  5752. // onclose callback - UNLESS just 'showing' a hidden pane as 'closed'
  5753. if (!isShowing) _runCallbacks("onclose_end", pane);
  5754. // onhide OR onshow callback
  5755. if (isShowing) _runCallbacks("onshow_end", pane);
  5756. if (isHiding) _runCallbacks("onhide_end", pane);
  5757. }
  5758. }
  5759. }
  5760. /**
  5761. * @param {string} pane The pane just closed, ie: north, south, east, or west
  5762. */
  5763. , setAsClosed = function (pane) {
  5764. if (!$Rs[pane]) return; // handles not initialized yet!
  5765. var
  5766. $P = $Ps[pane]
  5767. , $R = $Rs[pane]
  5768. , $T = $Ts[pane]
  5769. , o = options[pane]
  5770. , s = state[pane]
  5771. , side = _c[pane].side
  5772. , rClass = o.resizerClass
  5773. , tClass = o.togglerClass
  5774. , _pane = "-"+ pane // used for classNames
  5775. , _open = "-open"
  5776. , _sliding= "-sliding"
  5777. , _closed = "-closed"
  5778. ;
  5779. $R
  5780. .css(side, sC.inset[side]) // move the resizer
  5781. .removeClass( rClass+_open +" "+ rClass+_pane+_open )
  5782. .removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
  5783. .addClass( rClass+_closed +" "+ rClass+_pane+_closed )
  5784. ;
  5785. // DISABLE 'resizing' when closed - do this BEFORE bindStartSlidingEvents?
  5786. if (o.resizable && $.layout.plugins.draggable)
  5787. $R
  5788. .draggable("disable")
  5789. .removeClass("ui-state-disabled") // do NOT apply disabled styling - not suitable here
  5790. .css("cursor", "default")
  5791. .attr("title","")
  5792. ;
  5793. // if pane has a toggler button, adjust that too
  5794. if ($T) {
  5795. $T
  5796. .removeClass( tClass+_open +" "+ tClass+_pane+_open )
  5797. .addClass( tClass+_closed +" "+ tClass+_pane+_closed )
  5798. .attr("title", o.tips.Open) // may be blank
  5799. ;
  5800. // toggler-content - if exists
  5801. $T.children(".content-open").hide();
  5802. $T.children(".content-closed").css("display","block");
  5803. }
  5804. // sync any 'pin buttons'
  5805. syncPinBtns(pane, false);
  5806. if (state.initialized) {
  5807. // resize 'length' and position togglers for adjacent panes
  5808. sizeHandles();
  5809. }
  5810. }
  5811. /**
  5812. * Open the specified pane (animation optional), and resize all other panes as needed
  5813. *
  5814. * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west
  5815. * @param {boolean=} [slide=false]
  5816. * @param {boolean=} [noAnimation=false]
  5817. * @param {boolean=} [noAlert=false]
  5818. */
  5819. , open = function (evt_or_pane, slide, noAnimation, noAlert) {
  5820. if (!isInitialized()) return;
  5821. var pane = evtPane.call(this, evt_or_pane)
  5822. , $P = $Ps[pane]
  5823. , $R = $Rs[pane]
  5824. , $T = $Ts[pane]
  5825. , o = options[pane]
  5826. , s = state[pane]
  5827. , c = _c[pane]
  5828. , doFX, isShowing
  5829. ;
  5830. // QUEUE in case another action/animation is in progress
  5831. $N.queue(function( queueNext ){
  5832. if ( !$P
  5833. || (!o.resizable && !o.closable && !s.isShowing) // invalid request
  5834. || (s.isVisible && !s.isSliding) // already open
  5835. ) return queueNext();
  5836. // pane can ALSO be unhidden by just calling show(), so handle this scenario
  5837. if (s.isHidden && !s.isShowing) {
  5838. queueNext(); // call before show() because it needs the queue free
  5839. show(pane, true);
  5840. return;
  5841. }
  5842. if (s.autoResize && s.size != o.size) // resize pane to original size set in options
  5843. sizePane(pane, o.size, true, true, true); // true=skipCallback/noAnimation/forceResize
  5844. else
  5845. // make sure there is enough space available to open the pane
  5846. setSizeLimits(pane, slide);
  5847. // onopen_start callback - will CANCEL open if returns false
  5848. var cbReturn = _runCallbacks("onopen_start", pane);
  5849. if (cbReturn === "abort")
  5850. return queueNext();
  5851. // update pane-state again in case options were changed in onopen_start
  5852. if (cbReturn !== "NC") // NC = "No Callback"
  5853. setSizeLimits(pane, slide);
  5854. if (s.minSize > s.maxSize) { // INSUFFICIENT ROOM FOR PANE TO OPEN!
  5855. syncPinBtns(pane, false); // make sure pin-buttons are reset
  5856. if (!noAlert && o.tips.noRoomToOpen)
  5857. alert(o.tips.noRoomToOpen);
  5858. return queueNext(); // ABORT
  5859. }
  5860. if (slide) // START Sliding - will set isSliding=true
  5861. bindStopSlidingEvents(pane, true); // BIND trigger events to close sliding-pane
  5862. else if (s.isSliding) // PIN PANE (stop sliding) - open pane 'normally' instead
  5863. bindStopSlidingEvents(pane, false); // UNBIND trigger events - will set isSliding=false
  5864. else if (o.slidable)
  5865. bindStartSlidingEvents(pane, false); // UNBIND trigger events
  5866. s.noRoom = false; // will be reset by makePaneFit if 'noRoom'
  5867. makePaneFit(pane);
  5868. // transfer logic var to temp var
  5869. isShowing = s.isShowing;
  5870. // now clear the logic var
  5871. delete s.isShowing;
  5872. doFX = !noAnimation && s.isClosed && (o.fxName_open != "none");
  5873. s.isMoving = true;
  5874. s.isVisible = true;
  5875. s.isClosed = false;
  5876. // update isHidden BEFORE sizing panes - WHY??? Old?
  5877. if (isShowing) s.isHidden = false;
  5878. if (doFX) { // ANIMATE
  5879. // mask adjacent panes with objects
  5880. lockPaneForFX(pane, true); // need to set left/top so animation will work
  5881. $P.show( o.fxName_open, o.fxSettings_open, o.fxSpeed_open, function() {
  5882. lockPaneForFX(pane, false); // undo
  5883. if (s.isVisible) open_2(); // continue
  5884. queueNext();
  5885. });
  5886. }
  5887. else { // no animation
  5888. _showPane(pane);// just show pane and...
  5889. open_2(); // continue
  5890. queueNext();
  5891. };
  5892. });
  5893. // SUBROUTINE
  5894. function open_2 () {
  5895. s.isMoving = false;
  5896. // cure iframe display issues
  5897. _fixIframe(pane);
  5898. // NOTE: if isSliding, then other panes are NOT 'resized'
  5899. if (!s.isSliding) { // resize all panes adjacent to this one
  5900. sizeMidPanes(_c[pane].dir=="vert" ? "center" : "", false); // false = NOT skipCallback
  5901. }
  5902. // set classes, position handles and execute callbacks...
  5903. setAsOpen(pane);
  5904. };
  5905. }
  5906. /**
  5907. * @param {string} pane The pane just opened, ie: north, south, east, or west
  5908. * @param {boolean=} [skipCallback=false]
  5909. */
  5910. , setAsOpen = function (pane, skipCallback) {
  5911. var
  5912. $P = $Ps[pane]
  5913. , $R = $Rs[pane]
  5914. , $T = $Ts[pane]
  5915. , o = options[pane]
  5916. , s = state[pane]
  5917. , side = _c[pane].side
  5918. , rClass = o.resizerClass
  5919. , tClass = o.togglerClass
  5920. , _pane = "-"+ pane // used for classNames
  5921. , _open = "-open"
  5922. , _closed = "-closed"
  5923. , _sliding= "-sliding"
  5924. ;
  5925. $R
  5926. .css(side, sC.inset[side] + getPaneSize(pane)) // move the resizer
  5927. .removeClass( rClass+_closed +" "+ rClass+_pane+_closed )
  5928. .addClass( rClass+_open +" "+ rClass+_pane+_open )
  5929. ;
  5930. if (s.isSliding)
  5931. $R.addClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
  5932. else // in case 'was sliding'
  5933. $R.removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
  5934. removeHover( 0, $R ); // remove hover classes
  5935. if (o.resizable && $.layout.plugins.draggable)
  5936. $R .draggable("enable")
  5937. .css("cursor", o.resizerCursor)
  5938. .attr("title", o.tips.Resize);
  5939. else if (!s.isSliding)
  5940. $R.css("cursor", "default"); // n-resize, s-resize, etc
  5941. // if pane also has a toggler button, adjust that too
  5942. if ($T) {
  5943. $T .removeClass( tClass+_closed +" "+ tClass+_pane+_closed )
  5944. .addClass( tClass+_open +" "+ tClass+_pane+_open )
  5945. .attr("title", o.tips.Close); // may be blank
  5946. removeHover( 0, $T ); // remove hover classes
  5947. // toggler-content - if exists
  5948. $T.children(".content-closed").hide();
  5949. $T.children(".content-open").css("display","block");
  5950. }
  5951. // sync any 'pin buttons'
  5952. syncPinBtns(pane, !s.isSliding);
  5953. // update pane-state dimensions - BEFORE resizing content
  5954. $.extend(s, elDims($P));
  5955. if (state.initialized) {
  5956. // resize resizer & toggler sizes for all panes
  5957. sizeHandles();
  5958. // resize content every time pane opens - to be sure
  5959. sizeContent(pane, true); // true = remeasure headers/footers, even if 'pane.isMoving'
  5960. }
  5961. if (!skipCallback && (state.initialized || o.triggerEventsOnLoad) && $P.is(":visible")) {
  5962. // onopen callback
  5963. _runCallbacks("onopen_end", pane);
  5964. // onshow callback - TODO: should this be here?
  5965. if (s.isShowing) _runCallbacks("onshow_end", pane);
  5966. // ALSO call onresize because layout-size *may* have changed while pane was closed
  5967. if (state.initialized)
  5968. _runCallbacks("onresize_end", pane);
  5969. }
  5970. // TODO: Somehow sizePane("north") is being called after this point???
  5971. }
  5972. /**
  5973. * slideOpen / slideClose / slideToggle
  5974. *
  5975. * Pass-though methods for sliding
  5976. */
  5977. , slideOpen = function (evt_or_pane) {
  5978. if (!isInitialized()) return;
  5979. var evt = evtObj(evt_or_pane)
  5980. , pane = evtPane.call(this, evt_or_pane)
  5981. , s = state[pane]
  5982. , delay = options[pane].slideDelay_open
  5983. ;
  5984. // prevent event from triggering on NEW resizer binding created below
  5985. if (evt) evt.stopImmediatePropagation();
  5986. if (s.isClosed && evt && evt.type === "mouseenter" && delay > 0)
  5987. // trigger = mouseenter - use a delay
  5988. timer.set(pane+"_openSlider", open_NOW, delay);
  5989. else
  5990. open_NOW(); // will unbind events if is already open
  5991. /**
  5992. * SUBROUTINE for timed open
  5993. */
  5994. function open_NOW () {
  5995. if (!s.isClosed) // skip if no longer closed!
  5996. bindStopSlidingEvents(pane, true); // BIND trigger events to close sliding-pane
  5997. else if (!s.isMoving)
  5998. open(pane, true); // true = slide - open() will handle binding
  5999. };
  6000. }
  6001. , slideClose = function (evt_or_pane) {
  6002. if (!isInitialized()) return;
  6003. var evt = evtObj(evt_or_pane)
  6004. , pane = evtPane.call(this, evt_or_pane)
  6005. , o = options[pane]
  6006. , s = state[pane]
  6007. , delay = s.isMoving ? 1000 : 300 // MINIMUM delay - option may override
  6008. ;
  6009. if (s.isClosed || s.isResizing)
  6010. return; // skip if already closed OR in process of resizing
  6011. else if (o.slideTrigger_close === "click")
  6012. close_NOW(); // close immediately onClick
  6013. else if (o.preventQuickSlideClose && s.isMoving)
  6014. return; // handle Chrome quick-close on slide-open
  6015. else if (o.preventPrematureSlideClose && evt && $.layout.isMouseOverElem(evt, $Ps[pane]))
  6016. return; // handle incorrect mouseleave trigger, like when over a SELECT-list in IE
  6017. else if (evt) // trigger = mouseleave - use a delay
  6018. // 1 sec delay if 'opening', else .3 sec
  6019. timer.set(pane+"_closeSlider", close_NOW, max(o.slideDelay_close, delay));
  6020. else // called programically
  6021. close_NOW();
  6022. /**
  6023. * SUBROUTINE for timed close
  6024. */
  6025. function close_NOW () {
  6026. if (s.isClosed) // skip 'close' if already closed!
  6027. bindStopSlidingEvents(pane, false); // UNBIND trigger events - TODO: is this needed here?
  6028. else if (!s.isMoving)
  6029. close(pane); // close will handle unbinding
  6030. };
  6031. }
  6032. /**
  6033. * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west
  6034. */
  6035. , slideToggle = function (evt_or_pane) {
  6036. var pane = evtPane.call(this, evt_or_pane);
  6037. toggle(pane, true);
  6038. }
  6039. /**
  6040. * Must set left/top on East/South panes so animation will work properly
  6041. *
  6042. * @param {string} pane The pane to lock, 'east' or 'south' - any other is ignored!
  6043. * @param {boolean} doLock true = set left/top, false = remove
  6044. */
  6045. , lockPaneForFX = function (pane, doLock) {
  6046. var $P = $Ps[pane]
  6047. , s = state[pane]
  6048. , o = options[pane]
  6049. , z = options.zIndexes
  6050. ;
  6051. if (doLock) {
  6052. showMasks( pane, { animation: true, objectsOnly: true });
  6053. $P.css({ zIndex: z.pane_animate }); // overlay all elements during animation
  6054. if (pane=="south")
  6055. $P.css({ top: sC.inset.top + sC.innerHeight - $P.outerHeight() });
  6056. else if (pane=="east")
  6057. $P.css({ left: sC.inset.left + sC.innerWidth - $P.outerWidth() });
  6058. }
  6059. else { // animation DONE - RESET CSS
  6060. hideMasks();
  6061. $P.css({ zIndex: (s.isSliding ? z.pane_sliding : z.pane_normal) });
  6062. if (pane=="south")
  6063. $P.css({ top: "auto" });
  6064. // if pane is positioned 'off-screen', then DO NOT screw with it!
  6065. else if (pane=="east" && !$P.css("left").match(/\-99999/))
  6066. $P.css({ left: "auto" });
  6067. // fix anti-aliasing in IE - only needed for animations that change opacity
  6068. if (browser.msie && o.fxOpacityFix && o.fxName_open != "slide" && $P.css("filter") && $P.css("opacity") == 1)
  6069. $P[0].style.removeAttribute('filter');
  6070. }
  6071. }
  6072. /**
  6073. * Toggle sliding functionality of a specific pane on/off by adding removing 'slide open' trigger
  6074. *
  6075. * @see open(), close()
  6076. * @param {string} pane The pane to enable/disable, 'north', 'south', etc.
  6077. * @param {boolean} enable Enable or Disable sliding?
  6078. */
  6079. , bindStartSlidingEvents = function (pane, enable) {
  6080. var o = options[pane]
  6081. , $P = $Ps[pane]
  6082. , $R = $Rs[pane]
  6083. , evtName = o.slideTrigger_open.toLowerCase()
  6084. ;
  6085. if (!$R || (enable && !o.slidable)) return;
  6086. // make sure we have a valid event
  6087. if (evtName.match(/mouseover/))
  6088. evtName = o.slideTrigger_open = "mouseenter";
  6089. else if (!evtName.match(/(click|dblclick|mouseenter)/))
  6090. evtName = o.slideTrigger_open = "click";
  6091. // must remove double-click-toggle when using dblclick-slide
  6092. if (o.resizerDblClickToggle && evtName.match(/click/)) {
  6093. $R[enable ? "unbind" : "bind"]('dblclick.'+ sID, toggle)
  6094. }
  6095. $R
  6096. // add or remove event
  6097. [enable ? "bind" : "unbind"](evtName +'.'+ sID, slideOpen)
  6098. // set the appropriate cursor & title/tip
  6099. .css("cursor", enable ? o.sliderCursor : "default")
  6100. .attr("title", enable ? o.tips.Slide : "")
  6101. ;
  6102. }
  6103. /**
  6104. * Add or remove 'mouseleave' events to 'slide close' when pane is 'sliding' open or closed
  6105. * Also increases zIndex when pane is sliding open
  6106. * See bindStartSlidingEvents for code to control 'slide open'
  6107. *
  6108. * @see slideOpen(), slideClose()
  6109. * @param {string} pane The pane to process, 'north', 'south', etc.
  6110. * @param {boolean} enable Enable or Disable events?
  6111. */
  6112. , bindStopSlidingEvents = function (pane, enable) {
  6113. var o = options[pane]
  6114. , s = state[pane]
  6115. , c = _c[pane]
  6116. , z = options.zIndexes
  6117. , evtName = o.slideTrigger_close.toLowerCase()
  6118. , action = (enable ? "bind" : "unbind")
  6119. , $P = $Ps[pane]
  6120. , $R = $Rs[pane]
  6121. ;
  6122. timer.clear(pane+"_closeSlider"); // just in case
  6123. if (enable) {
  6124. s.isSliding = true;
  6125. state.panesSliding[pane] = true;
  6126. // remove 'slideOpen' event from resizer
  6127. // ALSO will raise the zIndex of the pane & resizer
  6128. bindStartSlidingEvents(pane, false);
  6129. }
  6130. else {
  6131. s.isSliding = false;
  6132. delete state.panesSliding[pane];
  6133. }
  6134. // RE/SET zIndex - increases when pane is sliding-open, resets to normal when not
  6135. $P.css("zIndex", enable ? z.pane_sliding : z.pane_normal);
  6136. $R.css("zIndex", enable ? z.pane_sliding+2 : z.resizer_normal); // NOTE: mask = pane_sliding+1
  6137. // make sure we have a valid event
  6138. if (!evtName.match(/(click|mouseleave)/))
  6139. evtName = o.slideTrigger_close = "mouseleave"; // also catches 'mouseout'
  6140. // add/remove slide triggers
  6141. $R[action](evtName, slideClose); // base event on resize
  6142. // need extra events for mouseleave
  6143. if (evtName === "mouseleave") {
  6144. // also close on pane.mouseleave
  6145. $P[action]("mouseleave."+ sID, slideClose);
  6146. // cancel timer when mouse moves between 'pane' and 'resizer'
  6147. $R[action]("mouseenter."+ sID, cancelMouseOut);
  6148. $P[action]("mouseenter."+ sID, cancelMouseOut);
  6149. }
  6150. if (!enable)
  6151. timer.clear(pane+"_closeSlider");
  6152. else if (evtName === "click" && !o.resizable) {
  6153. // IF pane is not resizable (which already has a cursor and tip)
  6154. // then set the a cursor & title/tip on resizer when sliding
  6155. $R.css("cursor", enable ? o.sliderCursor : "default");
  6156. $R.attr("title", enable ? o.tips.Close : ""); // use Toggler-tip, eg: "Close Pane"
  6157. }
  6158. // SUBROUTINE for mouseleave timer clearing
  6159. function cancelMouseOut (evt) {
  6160. timer.clear(pane+"_closeSlider");
  6161. evt.stopPropagation();
  6162. }
  6163. }
  6164. /**
  6165. * Hides/closes a pane if there is insufficient room - reverses this when there is room again
  6166. * MUST have already called setSizeLimits() before calling this method
  6167. *
  6168. * @param {string} pane The pane being resized
  6169. * @param {boolean=} [isOpening=false] Called from onOpen?
  6170. * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
  6171. * @param {boolean=} [force=false]
  6172. */
  6173. , makePaneFit = function (pane, isOpening, skipCallback, force) {
  6174. var o = options[pane]
  6175. , s = state[pane]
  6176. , c = _c[pane]
  6177. , $P = $Ps[pane]
  6178. , $R = $Rs[pane]
  6179. , isSidePane = c.dir==="vert"
  6180. , hasRoom = false
  6181. ;
  6182. // special handling for center & east/west panes
  6183. if (pane === "center" || (isSidePane && s.noVerticalRoom)) {
  6184. // see if there is enough room to display the pane
  6185. // ERROR: hasRoom = s.minHeight <= s.maxHeight && (isSidePane || s.minWidth <= s.maxWidth);
  6186. hasRoom = (s.maxHeight >= 0);
  6187. if (hasRoom && s.noRoom) { // previously hidden due to noRoom, so show now
  6188. _showPane(pane);
  6189. if ($R) $R.show();
  6190. s.isVisible = true;
  6191. s.noRoom = false;
  6192. if (isSidePane) s.noVerticalRoom = false;
  6193. _fixIframe(pane);
  6194. }
  6195. else if (!hasRoom && !s.noRoom) { // not currently hidden, so hide now
  6196. _hidePane(pane);
  6197. if ($R) $R.hide();
  6198. s.isVisible = false;
  6199. s.noRoom = true;
  6200. }
  6201. }
  6202. // see if there is enough room to fit the border-pane
  6203. if (pane === "center") {
  6204. // ignore center in this block
  6205. }
  6206. else if (s.minSize <= s.maxSize) { // pane CAN fit
  6207. hasRoom = true;
  6208. if (s.size > s.maxSize) // pane is too big - shrink it
  6209. sizePane(pane, s.maxSize, skipCallback, true, force); // true = noAnimation
  6210. else if (s.size < s.minSize) // pane is too small - enlarge it
  6211. sizePane(pane, s.minSize, skipCallback, true, force); // true = noAnimation
  6212. // need s.isVisible because new pseudoClose method keeps pane visible, but off-screen
  6213. else if ($R && s.isVisible && $P.is(":visible")) {
  6214. // make sure resizer-bar is positioned correctly
  6215. // handles situation where nested layout was 'hidden' when initialized
  6216. var pos = s.size + sC.inset[c.side];
  6217. if ($.layout.cssNum( $R, c.side ) != pos) $R.css( c.side, pos );
  6218. }
  6219. // if was previously hidden due to noRoom, then RESET because NOW there is room
  6220. if (s.noRoom) {
  6221. // s.noRoom state will be set by open or show
  6222. if (s.wasOpen && o.closable) {
  6223. if (o.autoReopen)
  6224. open(pane, false, true, true); // true = noAnimation, true = noAlert
  6225. else // leave the pane closed, so just update state
  6226. s.noRoom = false;
  6227. }
  6228. else
  6229. show(pane, s.wasOpen, true, true); // true = noAnimation, true = noAlert
  6230. }
  6231. }
  6232. else { // !hasRoom - pane CANNOT fit
  6233. if (!s.noRoom) { // pane not set as noRoom yet, so hide or close it now...
  6234. s.noRoom = true; // update state
  6235. s.wasOpen = !s.isClosed && !s.isSliding;
  6236. if (s.isClosed){} // SKIP
  6237. else if (o.closable) // 'close' if possible
  6238. close(pane, true, true); // true = force, true = noAnimation
  6239. else // 'hide' pane if cannot just be closed
  6240. hide(pane, true); // true = noAnimation
  6241. }
  6242. }
  6243. }
  6244. /**
  6245. * manualSizePane is an exposed flow-through method allowing extra code when pane is 'manually resized'
  6246. *
  6247. * @param {(string|Object)} evt_or_pane The pane being resized
  6248. * @param {number} size The *desired* new size for this pane - will be validated
  6249. * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
  6250. * @param {boolean=} [noAnimation=false]
  6251. * @param {boolean=} [force=false] Force resizing even if does not seem necessary
  6252. */
  6253. , manualSizePane = function (evt_or_pane, size, skipCallback, noAnimation, force) {
  6254. if (!isInitialized()) return;
  6255. var pane = evtPane.call(this, evt_or_pane)
  6256. , o = options[pane]
  6257. , s = state[pane]
  6258. // if resizing callbacks have been delayed and resizing is now DONE, force resizing to complete...
  6259. , forceResize = force || (o.livePaneResizing && !s.isResizing)
  6260. ;
  6261. // ANY call to manualSizePane disables autoResize - ie, percentage sizing
  6262. s.autoResize = false;
  6263. // flow-through...
  6264. sizePane(pane, size, skipCallback, noAnimation, forceResize); // will animate resize if option enabled
  6265. }
  6266. /**
  6267. * sizePane is called only by internal methods whenever a pane needs to be resized
  6268. *
  6269. * @param {(string|Object)} evt_or_pane The pane being resized
  6270. * @param {number} size The *desired* new size for this pane - will be validated
  6271. * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
  6272. * @param {boolean=} [noAnimation=false]
  6273. * @param {boolean=} [force=false] Force resizing even if does not seem necessary
  6274. */
  6275. , sizePane = function (evt_or_pane, size, skipCallback, noAnimation, force) {
  6276. if (!isInitialized()) return;
  6277. var pane = evtPane.call(this, evt_or_pane) // probably NEVER called from event?
  6278. , o = options[pane]
  6279. , s = state[pane]
  6280. , $P = $Ps[pane]
  6281. , $R = $Rs[pane]
  6282. , side = _c[pane].side
  6283. , dimName = _c[pane].sizeType.toLowerCase()
  6284. , skipResizeWhileDragging = s.isResizing && !o.triggerEventsDuringLiveResize
  6285. , doFX = noAnimation !== true && o.animatePaneSizing
  6286. , oldSize, newSize
  6287. ;
  6288. // QUEUE in case another action/animation is in progress
  6289. $N.queue(function( queueNext ){
  6290. // calculate 'current' min/max sizes
  6291. setSizeLimits(pane); // update pane-state
  6292. oldSize = s.size;
  6293. size = _parseSize(pane, size); // handle percentages & auto
  6294. size = max(size, _parseSize(pane, o.minSize));
  6295. size = min(size, s.maxSize);
  6296. if (size < s.minSize) { // not enough room for pane!
  6297. queueNext(); // call before makePaneFit() because it needs the queue free
  6298. makePaneFit(pane, false, skipCallback); // will hide or close pane
  6299. return;
  6300. }
  6301. // IF newSize is same as oldSize, then nothing to do - abort
  6302. if (!force && size === oldSize)
  6303. return queueNext();
  6304. s.newSize = size;
  6305. // onresize_start callback CANNOT cancel resizing because this would break the layout!
  6306. if (!skipCallback && state.initialized && s.isVisible)
  6307. _runCallbacks("onresize_start", pane);
  6308. // resize the pane, and make sure its visible
  6309. newSize = cssSize(pane, size);
  6310. if (doFX && $P.is(":visible")) { // ANIMATE
  6311. var fx = $.layout.effects.size[pane] || $.layout.effects.size.all
  6312. , easing = o.fxSettings_size.easing || fx.easing
  6313. , z = options.zIndexes
  6314. , props = {};
  6315. props[ dimName ] = newSize +'px';
  6316. s.isMoving = true;
  6317. // overlay all elements during animation
  6318. $P.css({ zIndex: z.pane_animate })
  6319. .show().animate( props, o.fxSpeed_size, easing, function(){
  6320. // reset zIndex after animation
  6321. $P.css({ zIndex: (s.isSliding ? z.pane_sliding : z.pane_normal) });
  6322. s.isMoving = false;
  6323. delete s.newSize;
  6324. sizePane_2(); // continue
  6325. queueNext();
  6326. });
  6327. }
  6328. else { // no animation
  6329. $P.css( dimName, newSize ); // resize pane
  6330. delete s.newSize;
  6331. // if pane is visible, then
  6332. if ($P.is(":visible"))
  6333. sizePane_2(); // continue
  6334. else {
  6335. // pane is NOT VISIBLE, so just update state data...
  6336. // when pane is *next opened*, it will have the new size
  6337. s.size = size; // update state.size
  6338. $.extend(s, elDims($P)); // update state dimensions
  6339. }
  6340. queueNext();
  6341. };
  6342. });
  6343. // SUBROUTINE
  6344. function sizePane_2 () {
  6345. /* Panes are sometimes not sized precisely in some browsers!?
  6346. * This code will resize the pane up to 3 times to nudge the pane to the correct size
  6347. */
  6348. var actual = dimName==='width' ? $P.outerWidth() : $P.outerHeight()
  6349. , tries = [{
  6350. pane: pane
  6351. , count: 1
  6352. , target: size
  6353. , actual: actual
  6354. , correct: (size === actual)
  6355. , attempt: size
  6356. , cssSize: newSize
  6357. }]
  6358. , lastTry = tries[0]
  6359. , thisTry = {}
  6360. , msg = 'Inaccurate size after resizing the '+ pane +'-pane.'
  6361. ;
  6362. while ( !lastTry.correct ) {
  6363. thisTry = { pane: pane, count: lastTry.count+1, target: size };
  6364. if (lastTry.actual > size)
  6365. thisTry.attempt = max(0, lastTry.attempt - (lastTry.actual - size));
  6366. else // lastTry.actual < size
  6367. thisTry.attempt = max(0, lastTry.attempt + (size - lastTry.actual));
  6368. thisTry.cssSize = cssSize(pane, thisTry.attempt);
  6369. $P.css( dimName, thisTry.cssSize );
  6370. thisTry.actual = dimName=='width' ? $P.outerWidth() : $P.outerHeight();
  6371. thisTry.correct = (size === thisTry.actual);
  6372. // log attempts and alert the user of this *non-fatal error* (if showDebugMessages)
  6373. if ( tries.length === 1) {
  6374. _log(msg, false, true);
  6375. _log(lastTry, false, true);
  6376. }
  6377. _log(thisTry, false, true);
  6378. // after 4 tries, is as close as its gonna get!
  6379. if (tries.length > 3) break;
  6380. tries.push( thisTry );
  6381. lastTry = tries[ tries.length - 1 ];
  6382. }
  6383. // END TESTING CODE
  6384. // update pane-state dimensions
  6385. s.size = size;
  6386. $.extend(s, elDims($P));
  6387. if (s.isVisible && $P.is(":visible")) {
  6388. // reposition the resizer-bar
  6389. if ($R) $R.css( side, size + sC.inset[side] );
  6390. // resize the content-div
  6391. sizeContent(pane);
  6392. }
  6393. if (!skipCallback && !skipResizeWhileDragging && state.initialized && s.isVisible)
  6394. _runCallbacks("onresize_end", pane);
  6395. // resize all the adjacent panes, and adjust their toggler buttons
  6396. // when skipCallback passed, it means the controlling method will handle 'other panes'
  6397. if (!skipCallback) {
  6398. // also no callback if live-resize is in progress and NOT triggerEventsDuringLiveResize
  6399. if (!s.isSliding) sizeMidPanes(_c[pane].dir=="horz" ? "" : "center", skipResizeWhileDragging, force);
  6400. sizeHandles();
  6401. }
  6402. // if opposite-pane was autoClosed, see if it can be autoOpened now
  6403. var altPane = _c.oppositeEdge[pane];
  6404. if (size < oldSize && state[ altPane ].noRoom) {
  6405. setSizeLimits( altPane );
  6406. makePaneFit( altPane, false, skipCallback );
  6407. }
  6408. // DEBUG - ALERT user/developer so they know there was a sizing problem
  6409. if (tries.length > 1)
  6410. _log(msg +'\nSee the Error Console for details.', true, true);
  6411. }
  6412. }
  6413. /**
  6414. * @see initPanes(), sizePane(), resizeAll(), open(), close(), hide()
  6415. * @param {(Array.<string>|string)} panes The pane(s) being resized, comma-delmited string
  6416. * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
  6417. * @param {boolean=} [force=false]
  6418. */
  6419. , sizeMidPanes = function (panes, skipCallback, force) {
  6420. panes = (panes ? panes : "east,west,center").split(",");
  6421. $.each(panes, function (i, pane) {
  6422. if (!$Ps[pane]) return; // NO PANE - skip
  6423. var
  6424. o = options[pane]
  6425. , s = state[pane]
  6426. , $P = $Ps[pane]
  6427. , $R = $Rs[pane]
  6428. , isCenter= (pane=="center")
  6429. , hasRoom = true
  6430. , CSS = {}
  6431. // if pane is not visible, show it invisibly NOW rather than for *each call* in this script
  6432. , visCSS = $.layout.showInvisibly($P)
  6433. , newCenter = calcNewCenterPaneDims()
  6434. ;
  6435. // update pane-state dimensions
  6436. $.extend(s, elDims($P));
  6437. if (pane === "center") {
  6438. if (!force && s.isVisible && newCenter.width === s.outerWidth && newCenter.height === s.outerHeight) {
  6439. $P.css(visCSS);
  6440. return true; // SKIP - pane already the correct size
  6441. }
  6442. // set state for makePaneFit() logic
  6443. $.extend(s, cssMinDims(pane), {
  6444. maxWidth: newCenter.width
  6445. , maxHeight: newCenter.height
  6446. });
  6447. CSS = newCenter;
  6448. s.newWidth = CSS.width;
  6449. s.newHeight = CSS.height;
  6450. // convert OUTER width/height to CSS width/height
  6451. CSS.width = cssW($P, CSS.width);
  6452. // NEW - allow pane to extend 'below' visible area rather than hide it
  6453. CSS.height = cssH($P, CSS.height);
  6454. hasRoom = CSS.width >= 0 && CSS.height >= 0; // height >= 0 = ALWAYS TRUE NOW
  6455. // during layout init, try to shrink east/west panes to make room for center
  6456. if (!state.initialized && o.minWidth > newCenter.width) {
  6457. var
  6458. reqPx = o.minWidth - s.outerWidth
  6459. , minE = options.east.minSize || 0
  6460. , minW = options.west.minSize || 0
  6461. , sizeE = state.east.size
  6462. , sizeW = state.west.size
  6463. , newE = sizeE
  6464. , newW = sizeW
  6465. ;
  6466. if (reqPx > 0 && state.east.isVisible && sizeE > minE) {
  6467. newE = max( sizeE-minE, sizeE-reqPx );
  6468. reqPx -= sizeE-newE;
  6469. }
  6470. if (reqPx > 0 && state.west.isVisible && sizeW > minW) {
  6471. newW = max( sizeW-minW, sizeW-reqPx );
  6472. reqPx -= sizeW-newW;
  6473. }
  6474. // IF we found enough extra space, then resize the border panes as calculated
  6475. if (reqPx === 0) {
  6476. if (sizeE && sizeE != minE)
  6477. sizePane('east', newE, true, true, force); // true = skipCallback/noAnimation - initPanes will handle when done
  6478. if (sizeW && sizeW != minW)
  6479. sizePane('west', newW, true, true, force); // true = skipCallback/noAnimation
  6480. // now start over!
  6481. sizeMidPanes('center', skipCallback, force);
  6482. $P.css(visCSS);
  6483. return; // abort this loop
  6484. }
  6485. }
  6486. }
  6487. else { // for east and west, set only the height, which is same as center height
  6488. // set state.min/maxWidth/Height for makePaneFit() logic
  6489. if (s.isVisible && !s.noVerticalRoom)
  6490. $.extend(s, elDims($P), cssMinDims(pane))
  6491. if (!force && !s.noVerticalRoom && newCenter.height === s.outerHeight) {
  6492. $P.css(visCSS);
  6493. return true; // SKIP - pane already the correct size
  6494. }
  6495. // east/west have same top, bottom & height as center
  6496. CSS.top = newCenter.top;
  6497. CSS.bottom = newCenter.bottom;
  6498. s.newSize = newCenter.height
  6499. // NEW - allow pane to extend 'below' visible area rather than hide it
  6500. CSS.height = cssH($P, newCenter.height);
  6501. s.maxHeight = CSS.height;
  6502. hasRoom = (s.maxHeight >= 0); // ALWAYS TRUE NOW
  6503. if (!hasRoom) s.noVerticalRoom = true; // makePaneFit() logic
  6504. }
  6505. if (hasRoom) {
  6506. // resizeAll passes skipCallback because it triggers callbacks after ALL panes are resized
  6507. if (!skipCallback && state.initialized)
  6508. _runCallbacks("onresize_start", pane);
  6509. $P.css(CSS); // apply the CSS to pane
  6510. if (pane !== "center")
  6511. sizeHandles(pane); // also update resizer length
  6512. if (s.noRoom && !s.isClosed && !s.isHidden)
  6513. makePaneFit(pane); // will re-open/show auto-closed/hidden pane
  6514. if (s.isVisible) {
  6515. $.extend(s, elDims($P)); // update pane dimensions
  6516. if (state.initialized) sizeContent(pane); // also resize the contents, if exists
  6517. }
  6518. }
  6519. else if (!s.noRoom && s.isVisible) // no room for pane
  6520. makePaneFit(pane); // will hide or close pane
  6521. // reset visibility, if necessary
  6522. $P.css(visCSS);
  6523. delete s.newSize;
  6524. delete s.newWidth;
  6525. delete s.newHeight;
  6526. if (!s.isVisible)
  6527. return true; // DONE - next pane
  6528. /*
  6529. * Extra CSS for IE6 or IE7 in Quirks-mode - add 'width' to NORTH/SOUTH panes
  6530. * Normally these panes have only 'left' & 'right' positions so pane auto-sizes
  6531. * ALSO required when pane is an IFRAME because will NOT default to 'full width'
  6532. * TODO: Can I use width:100% for a north/south iframe?
  6533. * TODO: Sounds like a job for $P.outerWidth( sC.innerWidth ) SETTER METHOD
  6534. */
  6535. if (pane === "center") { // finished processing midPanes
  6536. var fix = browser.isIE6 || !browser.boxModel;
  6537. if ($Ps.north && (fix || state.north.tagName=="IFRAME"))
  6538. $Ps.north.css("width", cssW($Ps.north, sC.innerWidth));
  6539. if ($Ps.south && (fix || state.south.tagName=="IFRAME"))
  6540. $Ps.south.css("width", cssW($Ps.south, sC.innerWidth));
  6541. }
  6542. // resizeAll passes skipCallback because it triggers callbacks after ALL panes are resized
  6543. if (!skipCallback && state.initialized)
  6544. _runCallbacks("onresize_end", pane);
  6545. });
  6546. }
  6547. /**
  6548. * @see window.onresize(), callbacks or custom code
  6549. * @param {(Object|boolean)=} evt_or_refresh If 'true', then also reset pane-positioning
  6550. */
  6551. , resizeAll = function (evt_or_refresh) {
  6552. var oldW = sC.innerWidth
  6553. , oldH = sC.innerHeight
  6554. ;
  6555. // stopPropagation if called by trigger("layoutdestroy") - use evtPane utility
  6556. evtPane(evt_or_refresh);
  6557. // cannot size layout when 'container' is hidden or collapsed
  6558. if (!$N.is(":visible")) return;
  6559. if (!state.initialized) {
  6560. _initLayoutElements();
  6561. return; // no need to resize since we just initialized!
  6562. }
  6563. if (evt_or_refresh === true && $.isPlainObject(options.outset)) {
  6564. // update container CSS in case outset option has changed
  6565. $N.css( options.outset );
  6566. }
  6567. // UPDATE container dimensions
  6568. $.extend(sC, elDims( $N, options.inset ));
  6569. if (!sC.outerHeight) return;
  6570. // if 'true' passed, refresh pane & handle positioning too
  6571. if (evt_or_refresh === true) {
  6572. setPanePosition();
  6573. }
  6574. // onresizeall_start will CANCEL resizing if returns false
  6575. // state.container has already been set, so user can access this info for calcuations
  6576. if (false === _runCallbacks("onresizeall_start")) return false;
  6577. var // see if container is now 'smaller' than before
  6578. shrunkH = (sC.innerHeight < oldH)
  6579. , shrunkW = (sC.innerWidth < oldW)
  6580. , $P, o, s
  6581. ;
  6582. // NOTE special order for sizing: S-N-E-W
  6583. $.each(["south","north","east","west"], function (i, pane) {
  6584. if (!$Ps[pane]) return; // no pane - SKIP
  6585. o = options[pane];
  6586. s = state[pane];
  6587. if (s.autoResize && s.size != o.size) // resize pane to original size set in options
  6588. sizePane(pane, o.size, true, true, true); // true=skipCallback/noAnimation/forceResize
  6589. else {
  6590. setSizeLimits(pane);
  6591. makePaneFit(pane, false, true, true); // true=skipCallback/forceResize
  6592. }
  6593. });
  6594. sizeMidPanes("", true, true); // true=skipCallback/forceResize
  6595. sizeHandles(); // reposition the toggler elements
  6596. // trigger all individual pane callbacks AFTER layout has finished resizing
  6597. $.each(_c.allPanes, function (i, pane) {
  6598. $P = $Ps[pane];
  6599. if (!$P) return; // SKIP
  6600. if (state[pane].isVisible) // undefined for non-existent panes
  6601. _runCallbacks("onresize_end", pane); // callback - if exists
  6602. });
  6603. _runCallbacks("onresizeall_end");
  6604. //_triggerLayoutEvent(pane, 'resizeall');
  6605. }
  6606. /**
  6607. * Whenever a pane resizes or opens that has a nested layout, trigger resizeAll
  6608. *
  6609. * @param {(string|Object)} evt_or_pane The pane just resized or opened
  6610. */
  6611. , resizeChildren = function (evt_or_pane, skipRefresh) {
  6612. var pane = evtPane.call(this, evt_or_pane);
  6613. if (!options[pane].resizeChildren) return;
  6614. // ensure the pane-children are up-to-date
  6615. if (!skipRefresh) refreshChildren( pane );
  6616. var pC = children[pane];
  6617. if ($.isPlainObject( pC )) {
  6618. // resize one or more children
  6619. $.each( pC, function (key, child) {
  6620. if (!child.destroyed) child.resizeAll();
  6621. });
  6622. }
  6623. }
  6624. /**
  6625. * IF pane has a content-div, then resize all elements inside pane to fit pane-height
  6626. *
  6627. * @param {(string|Object)} evt_or_panes The pane(s) being resized
  6628. * @param {boolean=} [remeasure=false] Should the content (header/footer) be remeasured?
  6629. */
  6630. , sizeContent = function (evt_or_panes, remeasure) {
  6631. if (!isInitialized()) return;
  6632. var panes = evtPane.call(this, evt_or_panes);
  6633. panes = panes ? panes.split(",") : _c.allPanes;
  6634. $.each(panes, function (idx, pane) {
  6635. var
  6636. $P = $Ps[pane]
  6637. , $C = $Cs[pane]
  6638. , o = options[pane]
  6639. , s = state[pane]
  6640. , m = s.content // m = measurements
  6641. ;
  6642. if (!$P || !$C || !$P.is(":visible")) return true; // NOT VISIBLE - skip
  6643. // if content-element was REMOVED, update OR remove the pointer
  6644. if (!$C.length) {
  6645. initContent(pane, false); // false = do NOT sizeContent() - already there!
  6646. if (!$C) return; // no replacement element found - pointer have been removed
  6647. }
  6648. // onsizecontent_start will CANCEL resizing if returns false
  6649. if (false === _runCallbacks("onsizecontent_start", pane)) return;
  6650. // skip re-measuring offsets if live-resizing
  6651. if ((!s.isMoving && !s.isResizing) || o.liveContentResizing || remeasure || m.top == undefined) {
  6652. _measure();
  6653. // if any footers are below pane-bottom, they may not measure correctly,
  6654. // so allow pane overflow and re-measure
  6655. if (m.hiddenFooters > 0 && $P.css("overflow") === "hidden") {
  6656. $P.css("overflow", "visible");
  6657. _measure(); // remeasure while overflowing
  6658. $P.css("overflow", "hidden");
  6659. }
  6660. }
  6661. // NOTE: spaceAbove/Below *includes* the pane paddingTop/Bottom, but not pane.borders
  6662. var newH = s.innerHeight - (m.spaceAbove - s.css.paddingTop) - (m.spaceBelow - s.css.paddingBottom);
  6663. if (!$C.is(":visible") || m.height != newH) {
  6664. // size the Content element to fit new pane-size - will autoHide if not enough room
  6665. setOuterHeight($C, newH, true); // true=autoHide
  6666. m.height = newH; // save new height
  6667. };
  6668. if (state.initialized)
  6669. _runCallbacks("onsizecontent_end", pane);
  6670. function _below ($E) {
  6671. return max(s.css.paddingBottom, (parseInt($E.css("marginBottom"), 10) || 0));
  6672. };
  6673. function _measure () {
  6674. var
  6675. ignore = options[pane].contentIgnoreSelector
  6676. , $Fs = $C.nextAll().not(".ui-layout-mask").not(ignore || ":lt(0)") // not :lt(0) = ALL
  6677. , $Fs_vis = $Fs.filter(':visible')
  6678. , $F = $Fs_vis.filter(':last')
  6679. ;
  6680. m = {
  6681. top: $C[0].offsetTop
  6682. , height: $C.outerHeight()
  6683. , numFooters: $Fs.length
  6684. , hiddenFooters: $Fs.length - $Fs_vis.length
  6685. , spaceBelow: 0 // correct if no content footer ($E)
  6686. }
  6687. m.spaceAbove = m.top; // just for state - not used in calc
  6688. m.bottom = m.top + m.height;
  6689. if ($F.length)
  6690. //spaceBelow = (LastFooter.top + LastFooter.height) [footerBottom] - Content.bottom + max(LastFooter.marginBottom, pane.paddingBotom)
  6691. m.spaceBelow = ($F[0].offsetTop + $F.outerHeight()) - m.bottom + _below($F);
  6692. else // no footer - check marginBottom on Content element itself
  6693. m.spaceBelow = _below($C);
  6694. };
  6695. });
  6696. }
  6697. /**
  6698. * Called every time a pane is opened, closed, or resized to slide the togglers to 'center' and adjust their length if necessary
  6699. *
  6700. * @see initHandles(), open(), close(), resizeAll()
  6701. * @param {(string|Object)=} evt_or_panes The pane(s) being resized
  6702. */
  6703. , sizeHandles = function (evt_or_panes) {
  6704. var panes = evtPane.call(this, evt_or_panes)
  6705. panes = panes ? panes.split(",") : _c.borderPanes;
  6706. $.each(panes, function (i, pane) {
  6707. var
  6708. o = options[pane]
  6709. , s = state[pane]
  6710. , $P = $Ps[pane]
  6711. , $R = $Rs[pane]
  6712. , $T = $Ts[pane]
  6713. , $TC
  6714. ;
  6715. if (!$P || !$R) return;
  6716. var
  6717. dir = _c[pane].dir
  6718. , _state = (s.isClosed ? "_closed" : "_open")
  6719. , spacing = o["spacing"+ _state]
  6720. , togAlign = o["togglerAlign"+ _state]
  6721. , togLen = o["togglerLength"+ _state]
  6722. , paneLen
  6723. , left
  6724. , offset
  6725. , CSS = {}
  6726. ;
  6727. if (spacing === 0) {
  6728. $R.hide();
  6729. return;
  6730. }
  6731. else if (!s.noRoom && !s.isHidden) // skip if resizer was hidden for any reason
  6732. $R.show(); // in case was previously hidden
  6733. // Resizer Bar is ALWAYS same width/height of pane it is attached to
  6734. if (dir === "horz") { // north/south
  6735. //paneLen = $P.outerWidth(); // s.outerWidth ||
  6736. paneLen = sC.innerWidth; // handle offscreen-panes
  6737. s.resizerLength = paneLen;
  6738. left = $.layout.cssNum($P, "left")
  6739. $R.css({
  6740. width: cssW($R, paneLen) // account for borders & padding
  6741. , height: cssH($R, spacing) // ditto
  6742. , left: left > -9999 ? left : sC.inset.left // handle offscreen-panes
  6743. });
  6744. }
  6745. else { // east/west
  6746. paneLen = $P.outerHeight(); // s.outerHeight ||
  6747. s.resizerLength = paneLen;
  6748. $R.css({
  6749. height: cssH($R, paneLen) // account for borders & padding
  6750. , width: cssW($R, spacing) // ditto
  6751. , top: sC.inset.top + getPaneSize("north", true) // TODO: what if no North pane?
  6752. //, top: $.layout.cssNum($Ps["center"], "top")
  6753. });
  6754. }
  6755. // remove hover classes
  6756. removeHover( o, $R );
  6757. if ($T) {
  6758. if (togLen === 0 || (s.isSliding && o.hideTogglerOnSlide)) {
  6759. $T.hide(); // always HIDE the toggler when 'sliding'
  6760. return;
  6761. }
  6762. else
  6763. $T.show(); // in case was previously hidden
  6764. if (!(togLen > 0) || togLen === "100%" || togLen > paneLen) {
  6765. togLen = paneLen;
  6766. offset = 0;
  6767. }
  6768. else { // calculate 'offset' based on options.PANE.togglerAlign_open/closed
  6769. if (isStr(togAlign)) {
  6770. switch (togAlign) {
  6771. case "top":
  6772. case "left": offset = 0;
  6773. break;
  6774. case "bottom":
  6775. case "right": offset = paneLen - togLen;
  6776. break;
  6777. case "middle":
  6778. case "center":
  6779. default: offset = round((paneLen - togLen) / 2); // 'default' catches typos
  6780. }
  6781. }
  6782. else { // togAlign = number
  6783. var x = parseInt(togAlign, 10); //
  6784. if (togAlign >= 0) offset = x;
  6785. else offset = paneLen - togLen + x; // NOTE: x is negative!
  6786. }
  6787. }
  6788. if (dir === "horz") { // north/south
  6789. var width = cssW($T, togLen);
  6790. $T.css({
  6791. width: width // account for borders & padding
  6792. , height: cssH($T, spacing) // ditto
  6793. , left: offset // TODO: VERIFY that toggler positions correctly for ALL values
  6794. , top: 0
  6795. });
  6796. // CENTER the toggler content SPAN
  6797. $T.children(".content").each(function(){
  6798. $TC = $(this);
  6799. $TC.css("marginLeft", round((width-$TC.outerWidth())/2)); // could be negative
  6800. });
  6801. }
  6802. else { // east/west
  6803. var height = cssH($T, togLen);
  6804. $T.css({
  6805. height: height // account for borders & padding
  6806. , width: cssW($T, spacing) // ditto
  6807. , top: offset // POSITION the toggler
  6808. , left: 0
  6809. });
  6810. // CENTER the toggler content SPAN
  6811. $T.children(".content").each(function(){
  6812. $TC = $(this);
  6813. $TC.css("marginTop", round((height-$TC.outerHeight())/2)); // could be negative
  6814. });
  6815. }
  6816. // remove ALL hover classes
  6817. removeHover( 0, $T );
  6818. }
  6819. // DONE measuring and sizing this resizer/toggler, so can be 'hidden' now
  6820. if (!state.initialized && (o.initHidden || s.isHidden)) {
  6821. $R.hide();
  6822. if ($T) $T.hide();
  6823. }
  6824. });
  6825. }
  6826. /**
  6827. * @param {(string|Object)} evt_or_pane
  6828. */
  6829. , enableClosable = function (evt_or_pane) {
  6830. if (!isInitialized()) return;
  6831. var pane = evtPane.call(this, evt_or_pane)
  6832. , $T = $Ts[pane]
  6833. , o = options[pane]
  6834. ;
  6835. if (!$T) return;
  6836. o.closable = true;
  6837. $T .bind("click."+ sID, function(evt){ evt.stopPropagation(); toggle(pane); })
  6838. .css("visibility", "visible")
  6839. .css("cursor", "pointer")
  6840. .attr("title", state[pane].isClosed ? o.tips.Open : o.tips.Close) // may be blank
  6841. .show();
  6842. }
  6843. /**
  6844. * @param {(string|Object)} evt_or_pane
  6845. * @param {boolean=} [hide=false]
  6846. */
  6847. , disableClosable = function (evt_or_pane, hide) {
  6848. if (!isInitialized()) return;
  6849. var pane = evtPane.call(this, evt_or_pane)
  6850. , $T = $Ts[pane]
  6851. ;
  6852. if (!$T) return;
  6853. options[pane].closable = false;
  6854. // is closable is disable, then pane MUST be open!
  6855. if (state[pane].isClosed) open(pane, false, true);
  6856. $T .unbind("."+ sID)
  6857. .css("visibility", hide ? "hidden" : "visible") // instead of hide(), which creates logic issues
  6858. .css("cursor", "default")
  6859. .attr("title", "");
  6860. }
  6861. /**
  6862. * @param {(string|Object)} evt_or_pane
  6863. */
  6864. , enableSlidable = function (evt_or_pane) {
  6865. if (!isInitialized()) return;
  6866. var pane = evtPane.call(this, evt_or_pane)
  6867. , $R = $Rs[pane]
  6868. ;
  6869. if (!$R || !$R.data('draggable')) return;
  6870. options[pane].slidable = true;
  6871. if (state[pane].isClosed)
  6872. bindStartSlidingEvents(pane, true);
  6873. }
  6874. /**
  6875. * @param {(string|Object)} evt_or_pane
  6876. */
  6877. , disableSlidable = function (evt_or_pane) {
  6878. if (!isInitialized()) return;
  6879. var pane = evtPane.call(this, evt_or_pane)
  6880. , $R = $Rs[pane]
  6881. ;
  6882. if (!$R) return;
  6883. options[pane].slidable = false;
  6884. if (state[pane].isSliding)
  6885. close(pane, false, true);
  6886. else {
  6887. bindStartSlidingEvents(pane, false);
  6888. $R .css("cursor", "default")
  6889. .attr("title", "");
  6890. removeHover(null, $R[0]); // in case currently hovered
  6891. }
  6892. }
  6893. /**
  6894. * @param {(string|Object)} evt_or_pane
  6895. */
  6896. , enableResizable = function (evt_or_pane) {
  6897. if (!isInitialized()) return;
  6898. var pane = evtPane.call(this, evt_or_pane)
  6899. , $R = $Rs[pane]
  6900. , o = options[pane]
  6901. ;
  6902. if (!$R || !$R.data('draggable')) return;
  6903. o.resizable = true;
  6904. $R.draggable("enable");
  6905. if (!state[pane].isClosed)
  6906. $R .css("cursor", o.resizerCursor)
  6907. .attr("title", o.tips.Resize);
  6908. }
  6909. /**
  6910. * @param {(string|Object)} evt_or_pane
  6911. */
  6912. , disableResizable = function (evt_or_pane) {
  6913. if (!isInitialized()) return;
  6914. var pane = evtPane.call(this, evt_or_pane)
  6915. , $R = $Rs[pane]
  6916. ;
  6917. if (!$R || !$R.data('draggable')) return;
  6918. options[pane].resizable = false;
  6919. $R .draggable("disable")
  6920. .css("cursor", "default")
  6921. .attr("title", "");
  6922. removeHover(null, $R[0]); // in case currently hovered
  6923. }
  6924. /**
  6925. * Move a pane from source-side (eg, west) to target-side (eg, east)
  6926. * If pane exists on target-side, move that to source-side, ie, 'swap' the panes
  6927. *
  6928. * @param {(string|Object)} evt_or_pane1 The pane/edge being swapped
  6929. * @param {string} pane2 ditto
  6930. */
  6931. , swapPanes = function (evt_or_pane1, pane2) {
  6932. if (!isInitialized()) return;
  6933. var pane1 = evtPane.call(this, evt_or_pane1);
  6934. // change state.edge NOW so callbacks can know where pane is headed...
  6935. state[pane1].edge = pane2;
  6936. state[pane2].edge = pane1;
  6937. // run these even if NOT state.initialized
  6938. if (false === _runCallbacks("onswap_start", pane1)
  6939. || false === _runCallbacks("onswap_start", pane2)
  6940. ) {
  6941. state[pane1].edge = pane1; // reset
  6942. state[pane2].edge = pane2;
  6943. return;
  6944. }
  6945. var
  6946. oPane1 = copy( pane1 )
  6947. , oPane2 = copy( pane2 )
  6948. , sizes = {}
  6949. ;
  6950. sizes[pane1] = oPane1 ? oPane1.state.size : 0;
  6951. sizes[pane2] = oPane2 ? oPane2.state.size : 0;
  6952. // clear pointers & state
  6953. $Ps[pane1] = false;
  6954. $Ps[pane2] = false;
  6955. state[pane1] = {};
  6956. state[pane2] = {};
  6957. // ALWAYS remove the resizer & toggler elements
  6958. if ($Ts[pane1]) $Ts[pane1].remove();
  6959. if ($Ts[pane2]) $Ts[pane2].remove();
  6960. if ($Rs[pane1]) $Rs[pane1].remove();
  6961. if ($Rs[pane2]) $Rs[pane2].remove();
  6962. $Rs[pane1] = $Rs[pane2] = $Ts[pane1] = $Ts[pane2] = false;
  6963. // transfer element pointers and data to NEW Layout keys
  6964. move( oPane1, pane2 );
  6965. move( oPane2, pane1 );
  6966. // cleanup objects
  6967. oPane1 = oPane2 = sizes = null;
  6968. // make panes 'visible' again
  6969. if ($Ps[pane1]) $Ps[pane1].css(_c.visible);
  6970. if ($Ps[pane2]) $Ps[pane2].css(_c.visible);
  6971. // fix any size discrepancies caused by swap
  6972. resizeAll();
  6973. // run these even if NOT state.initialized
  6974. _runCallbacks("onswap_end", pane1);
  6975. _runCallbacks("onswap_end", pane2);
  6976. return;
  6977. function copy (n) { // n = pane
  6978. var
  6979. $P = $Ps[n]
  6980. , $C = $Cs[n]
  6981. ;
  6982. return !$P ? false : {
  6983. pane: n
  6984. , P: $P ? $P[0] : false
  6985. , C: $C ? $C[0] : false
  6986. , state: $.extend(true, {}, state[n])
  6987. , options: $.extend(true, {}, options[n])
  6988. }
  6989. };
  6990. function move (oPane, pane) {
  6991. if (!oPane) return;
  6992. var
  6993. P = oPane.P
  6994. , C = oPane.C
  6995. , oldPane = oPane.pane
  6996. , c = _c[pane]
  6997. // save pane-options that should be retained
  6998. , s = $.extend(true, {}, state[pane])
  6999. , o = options[pane]
  7000. // RETAIN side-specific FX Settings - more below
  7001. , fx = { resizerCursor: o.resizerCursor }
  7002. , re, size, pos
  7003. ;
  7004. $.each("fxName,fxSpeed,fxSettings".split(","), function (i, k) {
  7005. fx[k +"_open"] = o[k +"_open"];
  7006. fx[k +"_close"] = o[k +"_close"];
  7007. fx[k +"_size"] = o[k +"_size"];
  7008. });
  7009. // update object pointers and attributes
  7010. $Ps[pane] = $(P)
  7011. .data({
  7012. layoutPane: Instance[pane] // NEW pointer to pane-alias-object
  7013. , layoutEdge: pane
  7014. })
  7015. .css(_c.hidden)
  7016. .css(c.cssReq)
  7017. ;
  7018. $Cs[pane] = C ? $(C) : false;
  7019. // set options and state
  7020. options[pane] = $.extend(true, {}, oPane.options, fx);
  7021. state[pane] = $.extend(true, {}, oPane.state);
  7022. // change classNames on the pane, eg: ui-layout-pane-east ==> ui-layout-pane-west
  7023. re = new RegExp(o.paneClass +"-"+ oldPane, "g");
  7024. P.className = P.className.replace(re, o.paneClass +"-"+ pane);
  7025. // ALWAYS regenerate the resizer & toggler elements
  7026. initHandles(pane); // create the required resizer & toggler
  7027. // if moving to different orientation, then keep 'target' pane size
  7028. if (c.dir != _c[oldPane].dir) {
  7029. size = sizes[pane] || 0;
  7030. setSizeLimits(pane); // update pane-state
  7031. size = max(size, state[pane].minSize);
  7032. // use manualSizePane to disable autoResize - not useful after panes are swapped
  7033. manualSizePane(pane, size, true, true); // true/true = skipCallback/noAnimation
  7034. }
  7035. else // move the resizer here
  7036. $Rs[pane].css(c.side, sC.inset[c.side] + (state[pane].isVisible ? getPaneSize(pane) : 0));
  7037. // ADD CLASSNAMES & SLIDE-BINDINGS
  7038. if (oPane.state.isVisible && !s.isVisible)
  7039. setAsOpen(pane, true); // true = skipCallback
  7040. else {
  7041. setAsClosed(pane);
  7042. bindStartSlidingEvents(pane, true); // will enable events IF option is set
  7043. }
  7044. // DESTROY the object
  7045. oPane = null;
  7046. };
  7047. }
  7048. /**
  7049. * INTERNAL method to sync pin-buttons when pane is opened or closed
  7050. * Unpinned means the pane is 'sliding' - ie, over-top of the adjacent panes
  7051. *
  7052. * @see open(), setAsOpen(), setAsClosed()
  7053. * @param {string} pane These are the params returned to callbacks by layout()
  7054. * @param {boolean} doPin True means set the pin 'down', False means 'up'
  7055. */
  7056. , syncPinBtns = function (pane, doPin) {
  7057. if ($.layout.plugins.buttons)
  7058. $.each(state[pane].pins, function (i, selector) {
  7059. $.layout.buttons.setPinState(Instance, $(selector), pane, doPin);
  7060. });
  7061. }
  7062. ; // END var DECLARATIONS
  7063. /**
  7064. * Capture keys when enableCursorHotkey - toggle pane if hotkey pressed
  7065. *
  7066. * @see document.keydown()
  7067. */
  7068. function keyDown (evt) {
  7069. if (!evt) return true;
  7070. var code = evt.keyCode;
  7071. if (code < 33) return true; // ignore special keys: ENTER, TAB, etc
  7072. var
  7073. PANE = {
  7074. 38: "north" // Up Cursor - $.ui.keyCode.UP
  7075. , 40: "south" // Down Cursor - $.ui.keyCode.DOWN
  7076. , 37: "west" // Left Cursor - $.ui.keyCode.LEFT
  7077. , 39: "east" // Right Cursor - $.ui.keyCode.RIGHT
  7078. }
  7079. , ALT = evt.altKey // no worky!
  7080. , SHIFT = evt.shiftKey
  7081. , CTRL = evt.ctrlKey
  7082. , CURSOR = (CTRL && code >= 37 && code <= 40)
  7083. , o, k, m, pane
  7084. ;
  7085. if (CURSOR && options[PANE[code]].enableCursorHotkey) // valid cursor-hotkey
  7086. pane = PANE[code];
  7087. else if (CTRL || SHIFT) // check to see if this matches a custom-hotkey
  7088. $.each(_c.borderPanes, function (i, p) { // loop each pane to check its hotkey
  7089. o = options[p];
  7090. k = o.customHotkey;
  7091. m = o.customHotkeyModifier; // if missing or invalid, treated as "CTRL+SHIFT"
  7092. if ((SHIFT && m=="SHIFT") || (CTRL && m=="CTRL") || (CTRL && SHIFT)) { // Modifier matches
  7093. if (k && code === (isNaN(k) || k <= 9 ? k.toUpperCase().charCodeAt(0) : k)) { // Key matches
  7094. pane = p;
  7095. return false; // BREAK
  7096. }
  7097. }
  7098. });
  7099. // validate pane
  7100. if (!pane || !$Ps[pane] || !options[pane].closable || state[pane].isHidden)
  7101. return true;
  7102. toggle(pane);
  7103. evt.stopPropagation();
  7104. evt.returnValue = false; // CANCEL key
  7105. return false;
  7106. };
  7107. /*
  7108. * ######################################
  7109. * UTILITY METHODS
  7110. * called externally or by initButtons
  7111. * ######################################
  7112. */
  7113. /**
  7114. * Change/reset a pane overflow setting & zIndex to allow popups/drop-downs to work
  7115. *
  7116. * @param {Object=} [el] (optional) Can also be 'bound' to a click, mouseOver, or other event
  7117. */
  7118. function allowOverflow (el) {
  7119. if (!isInitialized()) return;
  7120. if (this && this.tagName) el = this; // BOUND to element
  7121. var $P;
  7122. if (isStr(el))
  7123. $P = $Ps[el];
  7124. else if ($(el).data("layoutRole"))
  7125. $P = $(el);
  7126. else
  7127. $(el).parents().each(function(){
  7128. if ($(this).data("layoutRole")) {
  7129. $P = $(this);
  7130. return false; // BREAK
  7131. }
  7132. });
  7133. if (!$P || !$P.length) return; // INVALID
  7134. var
  7135. pane = $P.data("layoutEdge")
  7136. , s = state[pane]
  7137. ;
  7138. // if pane is already raised, then reset it before doing it again!
  7139. // this would happen if allowOverflow is attached to BOTH the pane and an element
  7140. if (s.cssSaved)
  7141. resetOverflow(pane); // reset previous CSS before continuing
  7142. // if pane is raised by sliding or resizing, or its closed, then abort
  7143. if (s.isSliding || s.isResizing || s.isClosed) {
  7144. s.cssSaved = false;
  7145. return;
  7146. }
  7147. var
  7148. newCSS = { zIndex: (options.zIndexes.resizer_normal + 1) }
  7149. , curCSS = {}
  7150. , of = $P.css("overflow")
  7151. , ofX = $P.css("overflowX")
  7152. , ofY = $P.css("overflowY")
  7153. ;
  7154. // determine which, if any, overflow settings need to be changed
  7155. if (of != "visible") {
  7156. curCSS.overflow = of;
  7157. newCSS.overflow = "visible";
  7158. }
  7159. if (ofX && !ofX.match(/(visible|auto)/)) {
  7160. curCSS.overflowX = ofX;
  7161. newCSS.overflowX = "visible";
  7162. }
  7163. if (ofY && !ofY.match(/(visible|auto)/)) {
  7164. curCSS.overflowY = ofX;
  7165. newCSS.overflowY = "visible";
  7166. }
  7167. // save the current overflow settings - even if blank!
  7168. s.cssSaved = curCSS;
  7169. // apply new CSS to raise zIndex and, if necessary, make overflow 'visible'
  7170. $P.css( newCSS );
  7171. // make sure the zIndex of all other panes is normal
  7172. $.each(_c.allPanes, function(i, p) {
  7173. if (p != pane) resetOverflow(p);
  7174. });
  7175. };
  7176. /**
  7177. * @param {Object=} [el] (optional) Can also be 'bound' to a click, mouseOver, or other event
  7178. */
  7179. function resetOverflow (el) {
  7180. if (!isInitialized()) return;
  7181. if (this && this.tagName) el = this; // BOUND to element
  7182. var $P;
  7183. if (isStr(el))
  7184. $P = $Ps[el];
  7185. else if ($(el).data("layoutRole"))
  7186. $P = $(el);
  7187. else
  7188. $(el).parents().each(function(){
  7189. if ($(this).data("layoutRole")) {
  7190. $P = $(this);
  7191. return false; // BREAK
  7192. }
  7193. });
  7194. if (!$P || !$P.length) return; // INVALID
  7195. var
  7196. pane = $P.data("layoutEdge")
  7197. , s = state[pane]
  7198. , CSS = s.cssSaved || {}
  7199. ;
  7200. // reset the zIndex
  7201. if (!s.isSliding && !s.isResizing)
  7202. $P.css("zIndex", options.zIndexes.pane_normal);
  7203. // reset Overflow - if necessary
  7204. $P.css( CSS );
  7205. // clear var
  7206. s.cssSaved = false;
  7207. };
  7208. /*
  7209. * #####################
  7210. * CREATE/RETURN LAYOUT
  7211. * #####################
  7212. */
  7213. // validate that container exists
  7214. var $N = $(this).eq(0); // FIRST matching Container element
  7215. if (!$N.length) {
  7216. return _log( options.errors.containerMissing );
  7217. };
  7218. // Users retrieve Instance of a layout with: $N.layout() OR $N.data("layout")
  7219. // return the Instance-pointer if layout has already been initialized
  7220. if ($N.data("layoutContainer") && $N.data("layout"))
  7221. return $N.data("layout"); // cached pointer
  7222. // init global vars
  7223. var
  7224. $Ps = {} // Panes x5 - set in initPanes()
  7225. , $Cs = {} // Content x5 - set in initPanes()
  7226. , $Rs = {} // Resizers x4 - set in initHandles()
  7227. , $Ts = {} // Togglers x4 - set in initHandles()
  7228. , $Ms = $([]) // Masks - up to 2 masks per pane (IFRAME + DIV)
  7229. // aliases for code brevity
  7230. , sC = state.container // alias for easy access to 'container dimensions'
  7231. , sID = state.id // alias for unique layout ID/namespace - eg: "layout435"
  7232. ;
  7233. // create Instance object to expose data & option Properties, and primary action Methods
  7234. var Instance = {
  7235. // layout data
  7236. options: options // property - options hash
  7237. , state: state // property - dimensions hash
  7238. // object pointers
  7239. , container: $N // property - object pointers for layout container
  7240. , panes: $Ps // property - object pointers for ALL Panes: panes.north, panes.center
  7241. , contents: $Cs // property - object pointers for ALL Content: contents.north, contents.center
  7242. , resizers: $Rs // property - object pointers for ALL Resizers, eg: resizers.north
  7243. , togglers: $Ts // property - object pointers for ALL Togglers, eg: togglers.north
  7244. // border-pane open/close
  7245. , hide: hide // method - ditto
  7246. , show: show // method - ditto
  7247. , toggle: toggle // method - pass a 'pane' ("north", "west", etc)
  7248. , open: open // method - ditto
  7249. , close: close // method - ditto
  7250. , slideOpen: slideOpen // method - ditto
  7251. , slideClose: slideClose // method - ditto
  7252. , slideToggle: slideToggle // method - ditto
  7253. // pane actions
  7254. , setSizeLimits: setSizeLimits // method - pass a 'pane' - update state min/max data
  7255. , _sizePane: sizePane // method -intended for user by plugins only!
  7256. , sizePane: manualSizePane // method - pass a 'pane' AND an 'outer-size' in pixels or percent, or 'auto'
  7257. , sizeContent: sizeContent // method - pass a 'pane'
  7258. , swapPanes: swapPanes // method - pass TWO 'panes' - will swap them
  7259. , showMasks: showMasks // method - pass a 'pane' OR list of panes - default = all panes with mask option set
  7260. , hideMasks: hideMasks // method - ditto'
  7261. // pane element methods
  7262. , initContent: initContent // method - ditto
  7263. , addPane: addPane // method - pass a 'pane'
  7264. , removePane: removePane // method - pass a 'pane' to remove from layout, add 'true' to delete the pane-elem
  7265. , createChildren: createChildren // method - pass a 'pane' and (optional) layout-options (OVERRIDES options[pane].children
  7266. , refreshChildren: refreshChildren // method - pass a 'pane' and a layout-instance
  7267. // special pane option setting
  7268. , enableClosable: enableClosable // method - pass a 'pane'
  7269. , disableClosable: disableClosable // method - ditto
  7270. , enableSlidable: enableSlidable // method - ditto
  7271. , disableSlidable: disableSlidable // method - ditto
  7272. , enableResizable: enableResizable // method - ditto
  7273. , disableResizable: disableResizable// method - ditto
  7274. // utility methods for panes
  7275. , allowOverflow: allowOverflow // utility - pass calling element (this)
  7276. , resetOverflow: resetOverflow // utility - ditto
  7277. // layout control
  7278. , destroy: destroy // method - no parameters
  7279. , initPanes: isInitialized // method - no parameters
  7280. , resizeAll: resizeAll // method - no parameters
  7281. // callback triggering
  7282. , runCallbacks: _runCallbacks // method - pass evtName & pane (if a pane-event), eg: trigger("onopen", "west")
  7283. // alias collections of options, state and children - created in addPane and extended elsewhere
  7284. , hasParentLayout: false // set by initContainer()
  7285. , children: children // pointers to child-layouts, eg: Instance.children.west.layoutName
  7286. , north: false // alias group: { name: pane, pane: $Ps[pane], options: options[pane], state: state[pane], children: children[pane] }
  7287. , south: false // ditto
  7288. , west: false // ditto
  7289. , east: false // ditto
  7290. , center: false // ditto
  7291. };
  7292. // create the border layout NOW
  7293. if (_create() === 'cancel') // onload_start callback returned false to CANCEL layout creation
  7294. return null;
  7295. else // true OR false -- if layout-elements did NOT init (hidden or do not exist), can auto-init later
  7296. return Instance; // return the Instance object
  7297. }
  7298. })( jQuery );
  7299. // END Layout - keep internal vars internal!
  7300. // START Plugins - shared wrapper, no global vars
  7301. (function ($) {
  7302. /**
  7303. * jquery.layout.state 1.0
  7304. * $Date: 2011-07-16 08:00:00 (Sat, 16 July 2011) $
  7305. *
  7306. * Copyright (c) 2012
  7307. * Kevin Dalman (http://allpro.net)
  7308. *
  7309. * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
  7310. * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
  7311. *
  7312. * @requires: UI Layout 1.3.0.rc30.1 or higher
  7313. * @requires: $.ui.cookie (above)
  7314. *
  7315. * @see: http://groups.google.com/group/jquery-ui-layout
  7316. */
  7317. /*
  7318. * State-management options stored in options.stateManagement, which includes a .cookie hash
  7319. * Default options saves ALL KEYS for ALL PANES, ie: pane.size, pane.isClosed, pane.isHidden
  7320. *
  7321. * // STATE/COOKIE OPTIONS
  7322. * @example $(el).layout({
  7323. stateManagement: {
  7324. enabled: true
  7325. , stateKeys: "east.size,west.size,east.isClosed,west.isClosed"
  7326. , cookie: { name: "appLayout", path: "/" }
  7327. }
  7328. })
  7329. * @example $(el).layout({ stateManagement__enabled: true }) // enable auto-state-management using cookies
  7330. * @example $(el).layout({ stateManagement__cookie: { name: "appLayout", path: "/" } })
  7331. * @example $(el).layout({ stateManagement__cookie__name: "appLayout", stateManagement__cookie__path: "/" })
  7332. *
  7333. * // STATE/COOKIE METHODS
  7334. * @example myLayout.saveCookie( "west.isClosed,north.size,south.isHidden", {expires: 7} );
  7335. * @example myLayout.loadCookie();
  7336. * @example myLayout.deleteCookie();
  7337. * @example var JSON = myLayout.readState(); // CURRENT Layout State
  7338. * @example var JSON = myLayout.readCookie(); // SAVED Layout State (from cookie)
  7339. * @example var JSON = myLayout.state.stateData; // LAST LOADED Layout State (cookie saved in layout.state hash)
  7340. *
  7341. * CUSTOM STATE-MANAGEMENT (eg, saved in a database)
  7342. * @example var JSON = myLayout.readState( "west.isClosed,north.size,south.isHidden" );
  7343. * @example myLayout.loadState( JSON );
  7344. */
  7345. /**
  7346. * UI COOKIE UTILITY
  7347. *
  7348. * A $.cookie OR $.ui.cookie namespace *should be standard*, but until then...
  7349. * This creates $.ui.cookie so Layout does not need the cookie.jquery.js plugin
  7350. * NOTE: This utility is REQUIRED by the layout.state plugin
  7351. *
  7352. * Cookie methods in Layout are created as part of State Management
  7353. */
  7354. if (!$.ui) $.ui = {};
  7355. $.ui.cookie = {
  7356. // cookieEnabled is not in DOM specs, but DOES works in all browsers,including IE6
  7357. acceptsCookies: !!navigator.cookieEnabled
  7358. , read: function (name) {
  7359. var c = document.cookie
  7360. , cs = c ? c.split(';') : []
  7361. , pair // loop var
  7362. ;
  7363. for (var i=0, n=cs.length; i < n; i++) {
  7364. pair = $.trim(cs[i]).split('='); // name=value pair
  7365. if (pair[0] == name) // found the layout cookie
  7366. return decodeURIComponent(pair[1]);
  7367. }
  7368. return null;
  7369. }
  7370. , write: function (name, val, cookieOpts) {
  7371. var params = ""
  7372. , date = ""
  7373. , clear = false
  7374. , o = cookieOpts || {}
  7375. , x = o.expires || null
  7376. , t = $.type(x)
  7377. ;
  7378. if (t === "date")
  7379. date = x;
  7380. else if (t === "string" && x > 0) {
  7381. x = parseInt(x,10);
  7382. t = "number";
  7383. }
  7384. if (t === "number") {
  7385. date = new Date();
  7386. if (x > 0)
  7387. date.setDate(date.getDate() + x);
  7388. else {
  7389. date.setFullYear(1970);
  7390. clear = true;
  7391. }
  7392. }
  7393. if (date) params += ";expires="+ date.toUTCString();
  7394. if (o.path) params += ";path="+ o.path;
  7395. if (o.domain) params += ";domain="+ o.domain;
  7396. if (o.secure) params += ";secure";
  7397. document.cookie = name +"="+ (clear ? "" : encodeURIComponent( val )) + params; // write or clear cookie
  7398. }
  7399. , clear: function (name) {
  7400. $.ui.cookie.write(name, "", {expires: -1});
  7401. }
  7402. };
  7403. // if cookie.jquery.js is not loaded, create an alias to replicate it
  7404. // this may be useful to other plugins or code dependent on that plugin
  7405. if (!$.cookie) $.cookie = function (k, v, o) {
  7406. var C = $.ui.cookie;
  7407. if (v === null)
  7408. C.clear(k);
  7409. else if (v === undefined)
  7410. return C.read(k);
  7411. else
  7412. C.write(k, v, o);
  7413. };
  7414. // tell Layout that the state plugin is available
  7415. $.layout.plugins.stateManagement = true;
  7416. // Add State-Management options to layout.defaults
  7417. $.layout.config.optionRootKeys.push("stateManagement");
  7418. $.layout.defaults.stateManagement = {
  7419. enabled: false // true = enable state-management, even if not using cookies
  7420. , autoSave: true // Save a state-cookie when page exits?
  7421. , autoLoad: true // Load the state-cookie when Layout inits?
  7422. , animateLoad: true // animate panes when loading state into an active layout
  7423. , includeChildren: true // recurse into child layouts to include their state as well
  7424. // List state-data to save - must be pane-specific
  7425. , stateKeys: "north.size,south.size,east.size,west.size,"+
  7426. "north.isClosed,south.isClosed,east.isClosed,west.isClosed,"+
  7427. "north.isHidden,south.isHidden,east.isHidden,west.isHidden"
  7428. , cookie: {
  7429. name: "" // If not specified, will use Layout.name, else just "Layout"
  7430. , domain: "" // blank = current domain
  7431. , path: "" // blank = current page, "/" = entire website
  7432. , expires: "" // 'days' to keep cookie - leave blank for 'session cookie'
  7433. , secure: false
  7434. }
  7435. };
  7436. // Set stateManagement as a layout-option, NOT a pane-option
  7437. $.layout.optionsMap.layout.push("stateManagement");
  7438. /*
  7439. * State Management methods
  7440. */
  7441. $.layout.state = {
  7442. /**
  7443. * Get the current layout state and save it to a cookie
  7444. *
  7445. * myLayout.saveCookie( keys, cookieOpts )
  7446. *
  7447. * @param {Object} inst
  7448. * @param {(string|Array)=} keys
  7449. * @param {Object=} cookieOpts
  7450. */
  7451. saveCookie: function (inst, keys, cookieOpts) {
  7452. var o = inst.options
  7453. , sm = o.stateManagement
  7454. , oC = $.extend(true, {}, sm.cookie, cookieOpts || null)
  7455. , data = inst.state.stateData = inst.readState( keys || sm.stateKeys ) // read current panes-state
  7456. ;
  7457. $.ui.cookie.write( oC.name || o.name || "Layout", $.layout.state.encodeJSON(data), oC );
  7458. return $.extend(true, {}, data); // return COPY of state.stateData data
  7459. }
  7460. /**
  7461. * Remove the state cookie
  7462. *
  7463. * @param {Object} inst
  7464. */
  7465. , deleteCookie: function (inst) {
  7466. var o = inst.options;
  7467. $.ui.cookie.clear( o.stateManagement.cookie.name || o.name || "Layout" );
  7468. }
  7469. /**
  7470. * Read & return data from the cookie - as JSON
  7471. *
  7472. * @param {Object} inst
  7473. */
  7474. , readCookie: function (inst) {
  7475. var o = inst.options;
  7476. var c = $.ui.cookie.read( o.stateManagement.cookie.name || o.name || "Layout" );
  7477. // convert cookie string back to a hash and return it
  7478. return c ? $.layout.state.decodeJSON(c) : {};
  7479. }
  7480. /**
  7481. * Get data from the cookie and USE IT to loadState
  7482. *
  7483. * @param {Object} inst
  7484. */
  7485. , loadCookie: function (inst) {
  7486. var c = $.layout.state.readCookie(inst); // READ the cookie
  7487. if (c) {
  7488. inst.state.stateData = $.extend(true, {}, c); // SET state.stateData
  7489. inst.loadState(c); // LOAD the retrieved state
  7490. }
  7491. return c;
  7492. }
  7493. /**
  7494. * Update layout options from the cookie, if one exists
  7495. *
  7496. * @param {Object} inst
  7497. * @param {Object=} stateData
  7498. * @param {boolean=} animate
  7499. */
  7500. , loadState: function (inst, data, opts) {
  7501. if (!$.isPlainObject( data ) || $.isEmptyObject( data )) return;
  7502. // normalize data & cache in the state object
  7503. data = inst.state.stateData = $.layout.transformData( data ); // panes = default subkey
  7504. // add missing/default state-restore options
  7505. var smo = inst.options.stateManagement;
  7506. opts = $.extend({
  7507. animateLoad: false //smo.animateLoad
  7508. , includeChildren: smo.includeChildren
  7509. }, opts );
  7510. if (!inst.state.initialized) {
  7511. /*
  7512. * layout NOT initialized, so just update its options
  7513. */
  7514. // MUST remove pane.children keys before applying to options
  7515. // use a copy so we don't remove keys from original data
  7516. var o = $.extend(true, {}, data);
  7517. //delete o.center; // center has no state-data - only children
  7518. $.each($.layout.config.allPanes, function (idx, pane) {
  7519. if (o[pane]) delete o[pane].children;
  7520. });
  7521. // update CURRENT layout-options with saved state data
  7522. $.extend(true, inst.options, o);
  7523. }
  7524. else {
  7525. /*
  7526. * layout already initialized, so modify layout's configuration
  7527. */
  7528. var noAnimate = !opts.animateLoad
  7529. , o, c, h, state, open
  7530. ;
  7531. $.each($.layout.config.borderPanes, function (idx, pane) {
  7532. o = data[ pane ];
  7533. if (!$.isPlainObject( o )) return; // no key, skip pane
  7534. s = o.size;
  7535. c = o.initClosed;
  7536. h = o.initHidden;
  7537. ar = o.autoResize
  7538. state = inst.state[pane];
  7539. open = state.isVisible;
  7540. // reset autoResize
  7541. if (ar)
  7542. state.autoResize = ar;
  7543. // resize BEFORE opening
  7544. if (!open)
  7545. inst._sizePane(pane, s, false, false, false); // false=skipCallback/noAnimation/forceResize
  7546. // open/close as necessary - DO NOT CHANGE THIS ORDER!
  7547. if (h === true) inst.hide(pane, noAnimate);
  7548. else if (c === true) inst.close(pane, false, noAnimate);
  7549. else if (c === false) inst.open (pane, false, noAnimate);
  7550. else if (h === false) inst.show (pane, false, noAnimate);
  7551. // resize AFTER any other actions
  7552. if (open)
  7553. inst._sizePane(pane, s, false, false, noAnimate); // animate resize if option passed
  7554. });
  7555. /*
  7556. * RECURSE INTO CHILD-LAYOUTS
  7557. */
  7558. if (opts.includeChildren) {
  7559. var paneStateChildren, childState;
  7560. $.each(inst.children, function (pane, paneChildren) {
  7561. paneStateChildren = data[pane] ? data[pane].children : 0;
  7562. if (paneStateChildren && paneChildren) {
  7563. $.each(paneChildren, function (stateKey, child) {
  7564. childState = paneStateChildren[stateKey];
  7565. if (child && childState)
  7566. child.loadState( childState );
  7567. });
  7568. }
  7569. });
  7570. }
  7571. }
  7572. }
  7573. /**
  7574. * Get the *current layout state* and return it as a hash
  7575. *
  7576. * @param {Object=} inst // Layout instance to get state for
  7577. * @param {object=} [opts] // State-Managements override options
  7578. */
  7579. , readState: function (inst, opts) {
  7580. // backward compatility
  7581. if ($.type(opts) === 'string') opts = { keys: opts };
  7582. if (!opts) opts = {};
  7583. var sm = inst.options.stateManagement
  7584. , ic = opts.includeChildren
  7585. , recurse = ic !== undefined ? ic : sm.includeChildren
  7586. , keys = opts.stateKeys || sm.stateKeys
  7587. , alt = { isClosed: 'initClosed', isHidden: 'initHidden' }
  7588. , state = inst.state
  7589. , panes = $.layout.config.allPanes
  7590. , data = {}
  7591. , pair, pane, key, val
  7592. , ps, pC, child, array, count, branch
  7593. ;
  7594. if ($.isArray(keys)) keys = keys.join(",");
  7595. // convert keys to an array and change delimiters from '__' to '.'
  7596. keys = keys.replace(/__/g, ".").split(',');
  7597. // loop keys and create a data hash
  7598. for (var i=0, n=keys.length; i < n; i++) {
  7599. pair = keys[i].split(".");
  7600. pane = pair[0];
  7601. key = pair[1];
  7602. if ($.inArray(pane, panes) < 0) continue; // bad pane!
  7603. val = state[ pane ][ key ];
  7604. if (val == undefined) continue;
  7605. if (key=="isClosed" && state[pane]["isSliding"])
  7606. val = true; // if sliding, then *really* isClosed
  7607. ( data[pane] || (data[pane]={}) )[ alt[key] ? alt[key] : key ] = val;
  7608. }
  7609. // recurse into the child-layouts for each pane
  7610. if (recurse) {
  7611. $.each(panes, function (idx, pane) {
  7612. pC = inst.children[pane];
  7613. ps = state.stateData[pane];
  7614. if ($.isPlainObject( pC ) && !$.isEmptyObject( pC )) {
  7615. // ensure a key exists for this 'pane', eg: branch = data.center
  7616. branch = data[pane] || (data[pane] = {});
  7617. if (!branch.children) branch.children = {};
  7618. $.each( pC, function (key, child) {
  7619. // ONLY read state from an initialize layout
  7620. if ( child.state.initialized )
  7621. branch.children[ key ] = $.layout.state.readState( child );
  7622. // if we have PREVIOUS (onLoad) state for this child-layout, KEEP IT!
  7623. else if ( ps && ps.children && ps.children[ key ] ) {
  7624. branch.children[ key ] = $.extend(true, {}, ps.children[ key ] );
  7625. }
  7626. });
  7627. }
  7628. });
  7629. }
  7630. return data;
  7631. }
  7632. /**
  7633. * Stringify a JSON hash so can save in a cookie or db-field
  7634. */
  7635. , encodeJSON: function (JSON) {
  7636. return parse(JSON);
  7637. function parse (h) {
  7638. var D=[], i=0, k, v, t // k = key, v = value
  7639. , a = $.isArray(h)
  7640. ;
  7641. for (k in h) {
  7642. v = h[k];
  7643. t = typeof v;
  7644. if (t == 'string') // STRING - add quotes
  7645. v = '"'+ v +'"';
  7646. else if (t == 'object') // SUB-KEY - recurse into it
  7647. v = parse(v);
  7648. D[i++] = (!a ? '"'+ k +'":' : '') + v;
  7649. }
  7650. return (a ? '[' : '{') + D.join(',') + (a ? ']' : '}');
  7651. };
  7652. }
  7653. /**
  7654. * Convert stringified JSON back to a hash object
  7655. * @see $.parseJSON(), adding in jQuery 1.4.1
  7656. */
  7657. , decodeJSON: function (str) {
  7658. try { return $.parseJSON ? $.parseJSON(str) : window["eval"]("("+ str +")") || {}; }
  7659. catch (e) { return {}; }
  7660. }
  7661. , _create: function (inst) {
  7662. var _ = $.layout.state
  7663. , o = inst.options
  7664. , sm = o.stateManagement
  7665. ;
  7666. // ADD State-Management plugin methods to inst
  7667. $.extend( inst, {
  7668. // readCookie - update options from cookie - returns hash of cookie data
  7669. readCookie: function () { return _.readCookie(inst); }
  7670. // deleteCookie
  7671. , deleteCookie: function () { _.deleteCookie(inst); }
  7672. // saveCookie - optionally pass keys-list and cookie-options (hash)
  7673. , saveCookie: function (keys, cookieOpts) { return _.saveCookie(inst, keys, cookieOpts); }
  7674. // loadCookie - readCookie and use to loadState() - returns hash of cookie data
  7675. , loadCookie: function () { return _.loadCookie(inst); }
  7676. // loadState - pass a hash of state to use to update options
  7677. , loadState: function (stateData, opts) { _.loadState(inst, stateData, opts); }
  7678. // readState - returns hash of current layout-state
  7679. , readState: function (keys) { return _.readState(inst, keys); }
  7680. // add JSON utility methods too...
  7681. , encodeJSON: _.encodeJSON
  7682. , decodeJSON: _.decodeJSON
  7683. });
  7684. // init state.stateData key, even if plugin is initially disabled
  7685. inst.state.stateData = {};
  7686. // autoLoad MUST BE one of: data-array, data-hash, callback-function, or TRUE
  7687. if ( !sm.autoLoad ) return;
  7688. // When state-data exists in the autoLoad key USE IT,
  7689. // even if stateManagement.enabled == false
  7690. if ($.isPlainObject( sm.autoLoad )) {
  7691. if (!$.isEmptyObject( sm.autoLoad )) {
  7692. inst.loadState( sm.autoLoad );
  7693. }
  7694. }
  7695. else if ( sm.enabled ) {
  7696. // update the options from cookie or callback
  7697. // if options is a function, call it to get stateData
  7698. if ($.isFunction( sm.autoLoad )) {
  7699. var d = {};
  7700. try {
  7701. d = sm.autoLoad( inst, inst.state, inst.options, inst.options.name || '' ); // try to get data from fn
  7702. } catch (e) {}
  7703. if (d && $.isPlainObject( d ) && !$.isEmptyObject( d ))
  7704. inst.loadState(d);
  7705. }
  7706. else // any other truthy value will trigger loadCookie
  7707. inst.loadCookie();
  7708. }
  7709. }
  7710. , _unload: function (inst) {
  7711. var sm = inst.options.stateManagement;
  7712. if (sm.enabled && sm.autoSave) {
  7713. // if options is a function, call it to save the stateData
  7714. if ($.isFunction( sm.autoSave )) {
  7715. try {
  7716. sm.autoSave( inst, inst.state, inst.options, inst.options.name || '' ); // try to get data from fn
  7717. } catch (e) {}
  7718. }
  7719. else // any truthy value will trigger saveCookie
  7720. inst.saveCookie();
  7721. }
  7722. }
  7723. };
  7724. // add state initialization method to Layout's onCreate array of functions
  7725. $.layout.onCreate.push( $.layout.state._create );
  7726. $.layout.onUnload.push( $.layout.state._unload );
  7727. /**
  7728. * jquery.layout.buttons 1.0
  7729. * $Date: 2011-07-16 08:00:00 (Sat, 16 July 2011) $
  7730. *
  7731. * Copyright (c) 2012
  7732. * Kevin Dalman (http://allpro.net)
  7733. *
  7734. * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
  7735. * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
  7736. *
  7737. * @requires: UI Layout 1.3.0.rc30.1 or higher
  7738. *
  7739. * @see: http://groups.google.com/group/jquery-ui-layout
  7740. *
  7741. * Docs: [ to come ]
  7742. * Tips: [ to come ]
  7743. */
  7744. // tell Layout that the state plugin is available
  7745. $.layout.plugins.buttons = true;
  7746. // Add buttons options to layout.defaults
  7747. $.layout.defaults.autoBindCustomButtons = false;
  7748. // Specify autoBindCustomButtons as a layout-option, NOT a pane-option
  7749. $.layout.optionsMap.layout.push("autoBindCustomButtons");
  7750. /*
  7751. * Button methods
  7752. */
  7753. $.layout.buttons = {
  7754. /**
  7755. * Searches for .ui-layout-button-xxx elements and auto-binds them as layout-buttons
  7756. *
  7757. * @see _create()
  7758. *
  7759. * @param {Object} inst Layout Instance object
  7760. */
  7761. init: function (inst) {
  7762. var pre = "ui-layout-button-"
  7763. , layout = inst.options.name || ""
  7764. , name;
  7765. $.each("toggle,open,close,pin,toggle-slide,open-slide".split(","), function (i, action) {
  7766. $.each($.layout.config.borderPanes, function (ii, pane) {
  7767. $("."+pre+action+"-"+pane).each(function(){
  7768. // if button was previously 'bound', data.layoutName was set, but is blank if layout has no 'name'
  7769. name = $(this).data("layoutName") || $(this).attr("layoutName");
  7770. if (name == undefined || name === layout)
  7771. inst.bindButton(this, action, pane);
  7772. });
  7773. });
  7774. });
  7775. }
  7776. /**
  7777. * Helper function to validate params received by addButton utilities
  7778. *
  7779. * Two classes are added to the element, based on the buttonClass...
  7780. * The type of button is appended to create the 2nd className:
  7781. * - ui-layout-button-pin // action btnClass
  7782. * - ui-layout-button-pin-west // action btnClass + pane
  7783. * - ui-layout-button-toggle
  7784. * - ui-layout-button-open
  7785. * - ui-layout-button-close
  7786. *
  7787. * @param {Object} inst Layout Instance object
  7788. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  7789. * @param {string} pane Name of the pane the button is for: 'north', 'south', etc.
  7790. *
  7791. * @return {Array.<Object>} If both params valid, the element matching 'selector' in a jQuery wrapper - otherwise returns null
  7792. */
  7793. , get: function (inst, selector, pane, action) {
  7794. var $E = $(selector)
  7795. , o = inst.options
  7796. , err = o.errors.addButtonError
  7797. ;
  7798. if (!$E.length) { // element not found
  7799. $.layout.msg(err +" "+ o.errors.selector +": "+ selector, true);
  7800. }
  7801. else if ($.inArray(pane, $.layout.config.borderPanes) < 0) { // invalid 'pane' sepecified
  7802. $.layout.msg(err +" "+ o.errors.pane +": "+ pane, true);
  7803. $E = $(""); // NO BUTTON
  7804. }
  7805. else { // VALID
  7806. var btn = o[pane].buttonClass +"-"+ action;
  7807. $E .addClass( btn +" "+ btn +"-"+ pane )
  7808. .data("layoutName", o.name); // add layout identifier - even if blank!
  7809. }
  7810. return $E;
  7811. }
  7812. /**
  7813. * NEW syntax for binding layout-buttons - will eventually replace addToggle, addOpen, etc.
  7814. *
  7815. * @param {Object} inst Layout Instance object
  7816. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  7817. * @param {string} action
  7818. * @param {string} pane
  7819. */
  7820. , bind: function (inst, selector, action, pane) {
  7821. var _ = $.layout.buttons;
  7822. switch (action.toLowerCase()) {
  7823. case "toggle": _.addToggle (inst, selector, pane); break;
  7824. case "open": _.addOpen (inst, selector, pane); break;
  7825. case "close": _.addClose (inst, selector, pane); break;
  7826. case "pin": _.addPin (inst, selector, pane); break;
  7827. case "toggle-slide": _.addToggle (inst, selector, pane, true); break;
  7828. case "open-slide": _.addOpen (inst, selector, pane, true); break;
  7829. }
  7830. return inst;
  7831. }
  7832. /**
  7833. * Add a custom Toggler button for a pane
  7834. *
  7835. * @param {Object} inst Layout Instance object
  7836. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  7837. * @param {string} pane Name of the pane the button is for: 'north', 'south', etc.
  7838. * @param {boolean=} slide true = slide-open, false = pin-open
  7839. */
  7840. , addToggle: function (inst, selector, pane, slide) {
  7841. $.layout.buttons.get(inst, selector, pane, "toggle")
  7842. .click(function(evt){
  7843. inst.toggle(pane, !!slide);
  7844. evt.stopPropagation();
  7845. });
  7846. return inst;
  7847. }
  7848. /**
  7849. * Add a custom Open button for a pane
  7850. *
  7851. * @param {Object} inst Layout Instance object
  7852. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  7853. * @param {string} pane Name of the pane the button is for: 'north', 'south', etc.
  7854. * @param {boolean=} slide true = slide-open, false = pin-open
  7855. */
  7856. , addOpen: function (inst, selector, pane, slide) {
  7857. $.layout.buttons.get(inst, selector, pane, "open")
  7858. .attr("title", inst.options[pane].tips.Open)
  7859. .click(function (evt) {
  7860. inst.open(pane, !!slide);
  7861. evt.stopPropagation();
  7862. });
  7863. return inst;
  7864. }
  7865. /**
  7866. * Add a custom Close button for a pane
  7867. *
  7868. * @param {Object} inst Layout Instance object
  7869. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  7870. * @param {string} pane Name of the pane the button is for: 'north', 'south', etc.
  7871. */
  7872. , addClose: function (inst, selector, pane) {
  7873. $.layout.buttons.get(inst, selector, pane, "close")
  7874. .attr("title", inst.options[pane].tips.Close)
  7875. .click(function (evt) {
  7876. inst.close(pane);
  7877. evt.stopPropagation();
  7878. });
  7879. return inst;
  7880. }
  7881. /**
  7882. * Add a custom Pin button for a pane
  7883. *
  7884. * Four classes are added to the element, based on the paneClass for the associated pane...
  7885. * Assuming the default paneClass and the pin is 'up', these classes are added for a west-pane pin:
  7886. * - ui-layout-pane-pin
  7887. * - ui-layout-pane-west-pin
  7888. * - ui-layout-pane-pin-up
  7889. * - ui-layout-pane-west-pin-up
  7890. *
  7891. * @param {Object} inst Layout Instance object
  7892. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  7893. * @param {string} pane Name of the pane the pin is for: 'north', 'south', etc.
  7894. */
  7895. , addPin: function (inst, selector, pane) {
  7896. var _ = $.layout.buttons
  7897. , $E = _.get(inst, selector, pane, "pin");
  7898. if ($E.length) {
  7899. var s = inst.state[pane];
  7900. $E.click(function (evt) {
  7901. _.setPinState(inst, $(this), pane, (s.isSliding || s.isClosed));
  7902. if (s.isSliding || s.isClosed) inst.open( pane ); // change from sliding to open
  7903. else inst.close( pane ); // slide-closed
  7904. evt.stopPropagation();
  7905. });
  7906. // add up/down pin attributes and classes
  7907. _.setPinState(inst, $E, pane, (!s.isClosed && !s.isSliding));
  7908. // add this pin to the pane data so we can 'sync it' automatically
  7909. // PANE.pins key is an array so we can store multiple pins for each pane
  7910. s.pins.push( selector ); // just save the selector string
  7911. }
  7912. return inst;
  7913. }
  7914. /**
  7915. * Change the class of the pin button to make it look 'up' or 'down'
  7916. *
  7917. * @see addPin(), syncPins()
  7918. *
  7919. * @param {Object} inst Layout Instance object
  7920. * @param {Array.<Object>} $Pin The pin-span element in a jQuery wrapper
  7921. * @param {string} pane These are the params returned to callbacks by layout()
  7922. * @param {boolean} doPin true = set the pin 'down', false = set it 'up'
  7923. */
  7924. , setPinState: function (inst, $Pin, pane, doPin) {
  7925. var updown = $Pin.attr("pin");
  7926. if (updown && doPin === (updown=="down")) return; // already in correct state
  7927. var
  7928. o = inst.options[pane]
  7929. , pin = o.buttonClass +"-pin"
  7930. , side = pin +"-"+ pane
  7931. , UP = pin +"-up "+ side +"-up"
  7932. , DN = pin +"-down "+side +"-down"
  7933. ;
  7934. $Pin
  7935. .attr("pin", doPin ? "down" : "up") // logic
  7936. .attr("title", doPin ? o.tips.Unpin : o.tips.Pin)
  7937. .removeClass( doPin ? UP : DN )
  7938. .addClass( doPin ? DN : UP )
  7939. ;
  7940. }
  7941. /**
  7942. * INTERNAL function to sync 'pin buttons' when pane is opened or closed
  7943. * Unpinned means the pane is 'sliding' - ie, over-top of the adjacent panes
  7944. *
  7945. * @see open(), close()
  7946. *
  7947. * @param {Object} inst Layout Instance object
  7948. * @param {string} pane These are the params returned to callbacks by layout()
  7949. * @param {boolean} doPin True means set the pin 'down', False means 'up'
  7950. */
  7951. , syncPinBtns: function (inst, pane, doPin) {
  7952. // REAL METHOD IS _INSIDE_ LAYOUT - THIS IS HERE JUST FOR REFERENCE
  7953. $.each(inst.state[pane].pins, function (i, selector) {
  7954. $.layout.buttons.setPinState(inst, $(selector), pane, doPin);
  7955. });
  7956. }
  7957. , _load: function (inst) {
  7958. var _ = $.layout.buttons;
  7959. // ADD Button methods to Layout Instance
  7960. // Note: sel = jQuery Selector string
  7961. $.extend( inst, {
  7962. bindButton: function (sel, action, pane) { return _.bind(inst, sel, action, pane); }
  7963. // DEPRECATED METHODS
  7964. , addToggleBtn: function (sel, pane, slide) { return _.addToggle(inst, sel, pane, slide); }
  7965. , addOpenBtn: function (sel, pane, slide) { return _.addOpen(inst, sel, pane, slide); }
  7966. , addCloseBtn: function (sel, pane) { return _.addClose(inst, sel, pane); }
  7967. , addPinBtn: function (sel, pane) { return _.addPin(inst, sel, pane); }
  7968. });
  7969. // init state array to hold pin-buttons
  7970. for (var i=0; i<4; i++) {
  7971. var pane = $.layout.config.borderPanes[i];
  7972. inst.state[pane].pins = [];
  7973. }
  7974. // auto-init buttons onLoad if option is enabled
  7975. if ( inst.options.autoBindCustomButtons )
  7976. _.init(inst);
  7977. }
  7978. , _unload: function (inst) {
  7979. // TODO: unbind all buttons???
  7980. }
  7981. };
  7982. // add initialization method to Layout's onLoad array of functions
  7983. $.layout.onLoad.push( $.layout.buttons._load );
  7984. //$.layout.onUnload.push( $.layout.buttons._unload );
  7985. /**
  7986. * jquery.layout.browserZoom 1.0
  7987. * $Date: 2011-12-29 08:00:00 (Thu, 29 Dec 2011) $
  7988. *
  7989. * Copyright (c) 2012
  7990. * Kevin Dalman (http://allpro.net)
  7991. *
  7992. * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
  7993. * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
  7994. *
  7995. * @requires: UI Layout 1.3.0.rc30.1 or higher
  7996. *
  7997. * @see: http://groups.google.com/group/jquery-ui-layout
  7998. *
  7999. * TODO: Extend logic to handle other problematic zooming in browsers
  8000. * TODO: Add hotkey/mousewheel bindings to _instantly_ respond to these zoom event
  8001. */
  8002. // tell Layout that the plugin is available
  8003. $.layout.plugins.browserZoom = true;
  8004. $.layout.defaults.browserZoomCheckInterval = 1000;
  8005. $.layout.optionsMap.layout.push("browserZoomCheckInterval");
  8006. /*
  8007. * browserZoom methods
  8008. */
  8009. $.layout.browserZoom = {
  8010. _init: function (inst) {
  8011. // abort if browser does not need this check
  8012. if ($.layout.browserZoom.ratio() !== false)
  8013. $.layout.browserZoom._setTimer(inst);
  8014. }
  8015. , _setTimer: function (inst) {
  8016. // abort if layout destroyed or browser does not need this check
  8017. if (inst.destroyed) return;
  8018. var o = inst.options
  8019. , s = inst.state
  8020. // don't need check if inst has parentLayout, but check occassionally in case parent destroyed!
  8021. // MINIMUM 100ms interval, for performance
  8022. , ms = inst.hasParentLayout ? 5000 : Math.max( o.browserZoomCheckInterval, 100 )
  8023. ;
  8024. // set the timer
  8025. setTimeout(function(){
  8026. if (inst.destroyed || !o.resizeWithWindow) return;
  8027. var d = $.layout.browserZoom.ratio();
  8028. if (d !== s.browserZoom) {
  8029. s.browserZoom = d;
  8030. inst.resizeAll();
  8031. }
  8032. // set a NEW timeout
  8033. $.layout.browserZoom._setTimer(inst);
  8034. }
  8035. , ms );
  8036. }
  8037. , ratio: function () {
  8038. var w = window
  8039. , s = screen
  8040. , d = document
  8041. , dE = d.documentElement || d.body
  8042. , b = $.layout.browser
  8043. , v = b.version
  8044. , r, sW, cW
  8045. ;
  8046. // we can ignore all browsers that fire window.resize event onZoom
  8047. if ((b.msie && v > 8)
  8048. || !b.msie
  8049. ) return false; // don't need to track zoom
  8050. if (s.deviceXDPI && s.systemXDPI) // syntax compiler hack
  8051. return calc(s.deviceXDPI, s.systemXDPI);
  8052. // everything below is just for future reference!
  8053. if (b.webkit && (r = d.body.getBoundingClientRect))
  8054. return calc((r.left - r.right), d.body.offsetWidth);
  8055. if (b.webkit && (sW = w.outerWidth))
  8056. return calc(sW, w.innerWidth);
  8057. if ((sW = s.width) && (cW = dE.clientWidth))
  8058. return calc(sW, cW);
  8059. return false; // no match, so cannot - or don't need to - track zoom
  8060. function calc (x,y) { return (parseInt(x,10) / parseInt(y,10) * 100).toFixed(); }
  8061. }
  8062. };
  8063. // add initialization method to Layout's onLoad array of functions
  8064. $.layout.onReady.push( $.layout.browserZoom._init );
  8065. })( jQuery );