jquery.layout.js 176 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183
  1. /**
  2. * @preserve jquery.layout 1.3.0 - Release Candidate 30.4
  3. * $Date: 2012-03-10 08:00:00 (Sat, 10 Mar 2012) $
  4. * $Rev: 303004 $
  5. *
  6. * Copyright (c) 2012
  7. * Fabrizio Balliano (http://www.fabrizioballiano.net)
  8. * Kevin Dalman (http://allpro.net)
  9. *
  10. * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
  11. * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
  12. *
  13. * Changelog: http://layout.jquery-dev.net/changelog.cfm#1.3.0.rc30.2
  14. *
  15. * Docs: http://layout.jquery-dev.net/documentation.html
  16. * Tips: http://layout.jquery-dev.net/tips.html
  17. * Help: http://groups.google.com/group/jquery-ui-layout
  18. */
  19. /* JavaDoc Info: http://code.google.com/closure/compiler/docs/js-for-compiler.html
  20. * {!Object} non-nullable type (never NULL)
  21. * {?string} nullable type (sometimes NULL) - default for {Object}
  22. * {number=} optional parameter
  23. * {*} ALL types
  24. */
  25. // NOTE: For best readability, view with a fixed-width font and tabs equal to 4-chars
  26. ;(function ($) {
  27. // alias Math methods - used a lot!
  28. var min = Math.min
  29. , max = Math.max
  30. , round = Math.floor
  31. ;
  32. function isStr (v) { return $.type(v) === "string"; }
  33. function runPluginCallbacks (Instance, a_fn) {
  34. if ($.isArray(a_fn))
  35. for (var i=0, c=a_fn.length; i<c; i++) {
  36. var fn = a_fn[i];
  37. try {
  38. if (isStr(fn)) // 'name' of a function
  39. fn = eval(fn);
  40. if ($.isFunction(fn))
  41. fn( Instance );
  42. } catch (ex) {}
  43. }
  44. };
  45. /*
  46. * GENERIC $.layout METHODS - used by all layouts
  47. */
  48. $.layout = {
  49. version: "1.3.rc30.4"
  50. , revision: 0.033004 // 1.3.0 final = 1.0300 - major(n+).minor(nn)+patch(nn+)
  51. // LANGUAGE CUSTOMIZATION
  52. , language: {
  53. // Tips and messages for resizers, togglers, custom buttons, etc.
  54. Open: "Open" // eg: "Open Pane"
  55. , Close: "Close"
  56. , Resize: "Resize"
  57. , Slide: "Slide Open"
  58. , Pin: "Pin"
  59. , Unpin: "Un-Pin"
  60. , noRoomToOpenTip: "Not enough room to show this pane."
  61. , minSizeWarning: "Panel has reached its minimum size"
  62. , maxSizeWarning: "Panel has reached its maximum size"
  63. // Developer error messages
  64. , pane: "pane" // description of "layout pane element"
  65. , selector: "selector" // description of "jQuery-selector"
  66. , errButton: "Error Adding Button \n\nInvalid "
  67. , errContainerMissing: "UI Layout Initialization Error\n\nThe specified layout-container does not exist."
  68. , errCenterPaneMissing: "UI Layout Initialization Error\n\nThe center-pane element does not exist.\n\nThe center-pane is a required element."
  69. , errContainerHeight: "UI Layout Initialization Warning\n\nThe layout-container \"CONTAINER\" has no height.\n\nTherefore the layout is 0-height and hence 'invisible'!"
  70. }
  71. // can update code here if $.browser is phased out
  72. , browser: {
  73. mozilla: !!$.browser.mozilla
  74. , webkit: !!$.browser.webkit || !!$.browser.safari // webkit = jQ 1.4
  75. , msie: !!$.browser.msie
  76. , isIE6: !!$.browser.msie && $.browser.version == 6
  77. , version: $.browser.version // not used in Layout core, but may be used by plugins
  78. }
  79. // *PREDEFINED* EFFECTS & DEFAULTS
  80. // MUST list effect here - OR MUST set an fxSettings option (can be an empty hash: {})
  81. , effects: {
  82. // Pane Open/Close Animations
  83. slide: {
  84. all: { duration: "fast" } // eg: duration: 1000, easing: "easeOutBounce"
  85. , north: { direction: "up" }
  86. , south: { direction: "down" }
  87. , east: { direction: "right"}
  88. , west: { direction: "left" }
  89. }
  90. , drop: {
  91. all: { duration: "slow" } // eg: duration: 1000, easing: "easeOutQuint"
  92. , north: { direction: "up" }
  93. , south: { direction: "down" }
  94. , east: { direction: "right"}
  95. , west: { direction: "left" }
  96. }
  97. , scale: {
  98. all: { duration: "fast" }
  99. }
  100. // these are not recommended, but can be used
  101. , blind: {}
  102. , clip: {}
  103. , explode: {}
  104. , fade: {}
  105. , fold: {}
  106. , puff: {}
  107. // Pane Resize Animations
  108. , size: {
  109. all: { easing: "swing" }
  110. }
  111. }
  112. // INTERNAL CONFIG DATA - DO NOT CHANGE THIS!
  113. , config: {
  114. optionRootKeys: "effects,panes,north,south,west,east,center".split(",")
  115. , allPanes: "north,south,west,east,center".split(",")
  116. , borderPanes: "north,south,west,east".split(",")
  117. , oppositeEdge: {
  118. north: "south"
  119. , south: "north"
  120. , east: "west"
  121. , west: "east"
  122. }
  123. // CSS used in multiple places
  124. , hidden: { visibility: "hidden" }
  125. , visible: { visibility: "visible" }
  126. // layout element settings
  127. , resizers: {
  128. cssReq: {
  129. position: "absolute"
  130. , padding: 0
  131. , margin: 0
  132. , fontSize: "1px"
  133. , textAlign: "left" // to counter-act "center" alignment!
  134. , overflow: "hidden" // prevent toggler-button from overflowing
  135. // SEE $.layout.defaults.zIndexes.resizer_normal
  136. }
  137. , cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
  138. background: "#DDD"
  139. , border: "none"
  140. }
  141. }
  142. , togglers: {
  143. cssReq: {
  144. position: "absolute"
  145. , display: "block"
  146. , padding: 0
  147. , margin: 0
  148. , overflow: "hidden"
  149. , textAlign: "center"
  150. , fontSize: "1px"
  151. , cursor: "pointer"
  152. , zIndex: 1
  153. }
  154. , cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
  155. background: "#AAA"
  156. }
  157. }
  158. , content: {
  159. cssReq: {
  160. position: "relative" /* contain floated or positioned elements */
  161. }
  162. , cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
  163. overflow: "auto"
  164. , padding: "10px"
  165. }
  166. , cssDemoPane: { // DEMO CSS - REMOVE scrolling from 'pane' when it has a content-div
  167. overflow: "hidden"
  168. , padding: 0
  169. }
  170. }
  171. , panes: { // defaults for ALL panes - overridden by 'per-pane settings' below
  172. cssReq: {
  173. position: "absolute"
  174. , margin: 0
  175. // $.layout.defaults.zIndexes.pane_normal
  176. }
  177. , cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
  178. padding: "10px"
  179. , background: "#FFF"
  180. , border: "1px solid #BBB"
  181. , overflow: "auto"
  182. }
  183. }
  184. , north: {
  185. side: "Top"
  186. , sizeType: "Height"
  187. , dir: "horz"
  188. , cssReq: {
  189. top: 0
  190. , bottom: "auto"
  191. , left: 0
  192. , right: 0
  193. , width: "auto"
  194. // height: DYNAMIC
  195. }
  196. }
  197. , south: {
  198. side: "Bottom"
  199. , sizeType: "Height"
  200. , dir: "horz"
  201. , cssReq: {
  202. top: "auto"
  203. , bottom: 0
  204. , left: 0
  205. , right: 0
  206. , width: "auto"
  207. // height: DYNAMIC
  208. }
  209. }
  210. , east: {
  211. side: "Right"
  212. , sizeType: "Width"
  213. , dir: "vert"
  214. , cssReq: {
  215. left: "auto"
  216. , right: 0
  217. , top: "auto" // DYNAMIC
  218. , bottom: "auto" // DYNAMIC
  219. , height: "auto"
  220. // width: DYNAMIC
  221. }
  222. }
  223. , west: {
  224. side: "Left"
  225. , sizeType: "Width"
  226. , dir: "vert"
  227. , cssReq: {
  228. left: 0
  229. , right: "auto"
  230. , top: "auto" // DYNAMIC
  231. , bottom: "auto" // DYNAMIC
  232. , height: "auto"
  233. // width: DYNAMIC
  234. }
  235. }
  236. , center: {
  237. dir: "center"
  238. , cssReq: {
  239. left: "auto" // DYNAMIC
  240. , right: "auto" // DYNAMIC
  241. , top: "auto" // DYNAMIC
  242. , bottom: "auto" // DYNAMIC
  243. , height: "auto"
  244. , width: "auto"
  245. }
  246. }
  247. }
  248. // CALLBACK FUNCTION NAMESPACE - used to store reusable callback functions
  249. , callbacks: {}
  250. // LAYOUT-PLUGIN REGISTRATION
  251. // more plugins can added beyond this default list
  252. , plugins: {
  253. draggable: !!$.fn.draggable // resizing
  254. , effects: {
  255. core: !!$.effects // animimations (specific effects tested by initOptions)
  256. , slide: $.effects && $.effects.slide // default effect
  257. }
  258. }
  259. // arrays of plugin or other methods to be triggered for events in *each layout* - will be passed 'Instance'
  260. , onCreate: [] // runs when layout is just starting to be created - right after options are set
  261. , onLoad: [] // runs after layout container and global events init, but before initPanes is called
  262. , onReady: [] // runs after initialization *completes* - ie, after initPanes completes successfully
  263. , onDestroy: [] // runs after layout is destroyed
  264. , onUnload: [] // runs after layout is destroyed OR when page unloads
  265. , afterOpen: [] // runs after setAsOpen() completes
  266. , afterClose: [] // runs after setAsClosed() completes
  267. /*
  268. * GENERIC UTILITY METHODS
  269. */
  270. // calculate and return the scrollbar width, as an integer
  271. , scrollbarWidth: function () { return window.scrollbarWidth || $.layout.getScrollbarSize('width'); }
  272. , scrollbarHeight: function () { return window.scrollbarHeight || $.layout.getScrollbarSize('height'); }
  273. , getScrollbarSize: function (dim) {
  274. var $c = $('<div style="position: absolute; top: -10000px; left: -10000px; width: 100px; height: 100px; overflow: scroll;"></div>').appendTo("body");
  275. var d = { width: $c.width() - $c[0].clientWidth, height: $c.height() - $c[0].clientHeight };
  276. $c.remove();
  277. window.scrollbarWidth = d.width;
  278. window.scrollbarHeight = d.height;
  279. return dim.match(/^(width|height)$/) ? d[dim] : d;
  280. }
  281. /**
  282. * Returns hash container 'display' and 'visibility'
  283. *
  284. * @see $.swap() - swaps CSS, runs callback, resets CSS
  285. */
  286. , showInvisibly: function ($E, force) {
  287. if (!$E) return {};
  288. if (!$E.jquery) $E = $($E);
  289. var CSS = {
  290. display: $E.css('display')
  291. , visibility: $E.css('visibility')
  292. };
  293. if (force || CSS.display === "none") { // only if not *already hidden*
  294. $E.css({ display: "block", visibility: "hidden" }); // show element 'invisibly' so can be measured
  295. return CSS;
  296. }
  297. else return {};
  298. }
  299. /**
  300. * Returns data for setting size of an element (container or a pane).
  301. *
  302. * @see _create(), onWindowResize() for container, plus others for pane
  303. * @return JSON Returns a hash of all dimensions: top, bottom, left, right, outerWidth, innerHeight, etc
  304. */
  305. , getElementDimensions: function ($E) {
  306. var
  307. d = {} // dimensions hash
  308. , x = d.css = {} // CSS hash
  309. , i = {} // TEMP insets
  310. , b, p // TEMP border, padding
  311. , N = $.layout.cssNum
  312. , off = $E.offset()
  313. ;
  314. d.offsetLeft = off.left;
  315. d.offsetTop = off.top;
  316. $.each("Left,Right,Top,Bottom".split(","), function (idx, e) { // e = edge
  317. b = x["border" + e] = $.layout.borderWidth($E, e);
  318. p = x["padding"+ e] = $.layout.cssNum($E, "padding"+e);
  319. i[e] = b + p; // total offset of content from outer side
  320. d["inset"+ e] = p;
  321. });
  322. d.offsetWidth = $E.innerWidth(); // offsetWidth is used in calc when doing manual resize
  323. d.offsetHeight = $E.innerHeight(); // ditto
  324. d.outerWidth = $E.outerWidth();
  325. d.outerHeight = $E.outerHeight();
  326. d.innerWidth = max(0, d.outerWidth - i.Left - i.Right);
  327. d.innerHeight = max(0, d.outerHeight - i.Top - i.Bottom);
  328. x.width = $E.width();
  329. x.height = $E.height();
  330. x.top = N($E,"top",true);
  331. x.bottom = N($E,"bottom",true);
  332. x.left = N($E,"left",true);
  333. x.right = N($E,"right",true);
  334. //d.visible = $E.is(":visible");// && x.width > 0 && x.height > 0;
  335. return d;
  336. }
  337. , getElementCSS: function ($E, list) {
  338. var
  339. CSS = {}
  340. , style = $E[0].style
  341. , props = list.split(",")
  342. , sides = "Top,Bottom,Left,Right".split(",")
  343. , attrs = "Color,Style,Width".split(",")
  344. , p, s, a, i, j, k
  345. ;
  346. for (i=0; i < props.length; i++) {
  347. p = props[i];
  348. if (p.match(/(border|padding|margin)$/))
  349. for (j=0; j < 4; j++) {
  350. s = sides[j];
  351. if (p === "border")
  352. for (k=0; k < 3; k++) {
  353. a = attrs[k];
  354. CSS[p+s+a] = style[p+s+a];
  355. }
  356. else
  357. CSS[p+s] = style[p+s];
  358. }
  359. else
  360. CSS[p] = style[p];
  361. };
  362. return CSS
  363. }
  364. /**
  365. * Return the innerWidth for the current browser/doctype
  366. *
  367. * @see initPanes(), sizeMidPanes(), initHandles(), sizeHandles()
  368. * @param {Array.<Object>} $E Must pass a jQuery object - first element is processed
  369. * @param {number=} outerWidth (optional) Can pass a width, allowing calculations BEFORE element is resized
  370. * @return {number} Returns the innerWidth of the elem by subtracting padding and borders
  371. */
  372. , cssWidth: function ($E, outerWidth) {
  373. var
  374. b = $.layout.borderWidth
  375. , n = $.layout.cssNum
  376. ;
  377. // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
  378. if (outerWidth <= 0) return 0;
  379. if (!$.support.boxModel) return outerWidth;
  380. // strip border and padding from outerWidth to get CSS Width
  381. var W = outerWidth
  382. - b($E, "Left")
  383. - b($E, "Right")
  384. - n($E, "paddingLeft")
  385. - n($E, "paddingRight")
  386. ;
  387. return max(0,W);
  388. }
  389. /**
  390. * Return the innerHeight for the current browser/doctype
  391. *
  392. * @see initPanes(), sizeMidPanes(), initHandles(), sizeHandles()
  393. * @param {Array.<Object>} $E Must pass a jQuery object - first element is processed
  394. * @param {number=} outerHeight (optional) Can pass a width, allowing calculations BEFORE element is resized
  395. * @return {number} Returns the innerHeight of the elem by subtracting padding and borders
  396. */
  397. , cssHeight: function ($E, outerHeight) {
  398. var
  399. b = $.layout.borderWidth
  400. , n = $.layout.cssNum
  401. ;
  402. // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
  403. if (outerHeight <= 0) return 0;
  404. if (!$.support.boxModel) return outerHeight;
  405. // strip border and padding from outerHeight to get CSS Height
  406. var H = outerHeight
  407. - b($E, "Top")
  408. - b($E, "Bottom")
  409. - n($E, "paddingTop")
  410. - n($E, "paddingBottom")
  411. ;
  412. return max(0,H);
  413. }
  414. /**
  415. * Returns the 'current CSS numeric value' for a CSS property - 0 if property does not exist
  416. *
  417. * @see Called by many methods
  418. * @param {Array.<Object>} $E Must pass a jQuery object - first element is processed
  419. * @param {string} prop The name of the CSS property, eg: top, width, etc.
  420. * @param {boolean=} [allowAuto=false] true = return 'auto' if that is value; false = return 0
  421. * @return {(string|number)} Usually used to get an integer value for position (top, left) or size (height, width)
  422. */
  423. , cssNum: function ($E, prop, allowAuto) {
  424. if (!$E.jquery) $E = $($E);
  425. var CSS = $.layout.showInvisibly($E)
  426. , p = $.curCSS($E[0], prop, true)
  427. , v = allowAuto && p=="auto" ? p : (parseInt(p, 10) || 0);
  428. $E.css( CSS ); // RESET
  429. return v;
  430. }
  431. , borderWidth: function (el, side) {
  432. if (el.jquery) el = el[0];
  433. var b = "border"+ side.substr(0,1).toUpperCase() + side.substr(1); // left => Left
  434. return $.curCSS(el, b+"Style", true) === "none" ? 0 : (parseInt($.curCSS(el, b+"Width", true), 10) || 0);
  435. }
  436. /**
  437. * Mouse-tracking utility - FUTURE REFERENCE
  438. *
  439. * init: if (!window.mouse) {
  440. * window.mouse = { x: 0, y: 0 };
  441. * $(document).mousemove( $.layout.trackMouse );
  442. * }
  443. *
  444. * @param {Object} evt
  445. *
  446. , trackMouse: function (evt) {
  447. window.mouse = { x: evt.clientX, y: evt.clientY };
  448. }
  449. */
  450. /**
  451. * SUBROUTINE for preventPrematureSlideClose option
  452. *
  453. * @param {Object} evt
  454. * @param {Object=} el
  455. */
  456. , isMouseOverElem: function (evt, el) {
  457. var
  458. $E = $(el || this)
  459. , d = $E.offset()
  460. , T = d.top
  461. , L = d.left
  462. , R = L + $E.outerWidth()
  463. , B = T + $E.outerHeight()
  464. , x = evt.pageX // evt.clientX ?
  465. , y = evt.pageY // evt.clientY ?
  466. ;
  467. // if X & Y are < 0, probably means is over an open SELECT
  468. return ($.layout.browser.msie && x < 0 && y < 0) || ((x >= L && x <= R) && (y >= T && y <= B));
  469. }
  470. /**
  471. * Message/Logging Utility
  472. *
  473. * @example $.layout.msg("My message"); // log text
  474. * @example $.layout.msg("My message", true); // alert text
  475. * @example $.layout.msg({ foo: "bar" }, "Title"); // log hash-data, with custom title
  476. * @example $.layout.msg({ foo: "bar" }, true, "Title", { sort: false }); -OR-
  477. * @example $.layout.msg({ foo: "bar" }, "Title", { sort: false, display: true }); // alert hash-data
  478. *
  479. * @param {(Object|string)} info String message OR Hash/Array
  480. * @param {(Boolean|string|Object)=} [popup=false] True means alert-box - can be skipped
  481. * @param {(Object|string)=} [debugTitle=""] Title for Hash data - can be skipped
  482. * @param {Object=} [debutOpts={}] Extra options for debug output
  483. */
  484. , msg: function (info, popup, debugTitle, debugOpts) {
  485. if ($.isPlainObject(info) && window.debugData) {
  486. if (typeof popup === "string") {
  487. debugOpts = debugTitle;
  488. debugTitle = popup;
  489. }
  490. else if (typeof debugTitle === "object") {
  491. debugOpts = debugTitle;
  492. debugTitle = null;
  493. }
  494. var t = debugTitle || "log( <object> )"
  495. , o = $.extend({ sort: false, returnHTML: false, display: false }, debugOpts);
  496. if (popup === true || o.display)
  497. debugData( info, t, o );
  498. else if (window.console)
  499. console.log(debugData( info, t, o ));
  500. }
  501. else if (popup)
  502. alert(info);
  503. else if (window.console)
  504. console.log(info);
  505. }
  506. };
  507. var lang = $.layout.language; // alias used in defaults...
  508. // DEFAULT OPTIONS - CHANGE IF DESIRED
  509. $.layout.defaults = {
  510. /*
  511. * LAYOUT & LAYOUT-CONTAINER OPTIONS
  512. * - none of these options are applicable to individual panes
  513. */
  514. name: "" // Not required, but useful for buttons and used for the state-cookie
  515. , containerClass: "ui-layout-container" // layout-container element
  516. , scrollToBookmarkOnLoad: true // after creating a layout, scroll to bookmark in URL (.../page.htm#myBookmark)
  517. , resizeWithWindow: true // bind thisLayout.resizeAll() to the window.resize event
  518. , resizeWithWindowDelay: 200 // delay calling resizeAll because makes window resizing very jerky
  519. , resizeWithWindowMaxDelay: 0 // 0 = none - force resize every XX ms while window is being resized
  520. , onresizeall_start: null // CALLBACK when resizeAll() STARTS - NOT pane-specific
  521. , onresizeall_end: null // CALLBACK when resizeAll() ENDS - NOT pane-specific
  522. , onload_start: null // CALLBACK when Layout inits - after options initialized, but before elements
  523. , onload_end: null // CALLBACK when Layout inits - after EVERYTHING has been initialized
  524. , onunload_start: null // CALLBACK when Layout is destroyed OR onWindowUnload
  525. , onunload_end: null // CALLBACK when Layout is destroyed OR onWindowUnload
  526. , autoBindCustomButtons: false // search for buttons with ui-layout-button class and auto-bind them
  527. , initPanes: true // false = DO NOT initialize the panes onLoad - will init later
  528. , showErrorMessages: true // enables fatal error messages to warn developers of common errors
  529. , showDebugMessages: false // display console-and-alert debug msgs - IF this Layout version _has_ debugging code!
  530. // Changing this zIndex value will cause other zIndex values to automatically change
  531. , zIndex: null // the PANE zIndex - resizers and masks will be +1
  532. // DO NOT CHANGE the zIndex values below unless you clearly understand their relationships
  533. , zIndexes: { // set _default_ z-index values here...
  534. pane_normal: 0 // normal z-index for panes
  535. , content_mask: 1 // applied to overlays used to mask content INSIDE panes during resizing
  536. , resizer_normal: 2 // normal z-index for resizer-bars
  537. , pane_sliding: 100 // applied to *BOTH* the pane and its resizer when a pane is 'slid open'
  538. , pane_animate: 1000 // applied to the pane when being animated - not applied to the resizer
  539. , resizer_drag: 10000 // applied to the CLONED resizer-bar when being 'dragged'
  540. }
  541. /*
  542. * PANE DEFAULT SETTINGS
  543. * - settings under the 'panes' key become the default settings for *all panes*
  544. * - ALL pane-options can also be set specifically for each panes, which will override these 'default values'
  545. */
  546. , panes: { // default options for 'all panes' - will be overridden by 'per-pane settings'
  547. applyDemoStyles: false // NOTE: renamed from applyDefaultStyles for clarity
  548. , closable: true // pane can open & close
  549. , resizable: true // when open, pane can be resized
  550. , slidable: true // when closed, pane can 'slide open' over other panes - closes on mouse-out
  551. , initClosed: false // true = init pane as 'closed'
  552. , initHidden: false // true = init pane as 'hidden' - no resizer-bar/spacing
  553. // SELECTORS
  554. //, paneSelector: "" // MUST be pane-specific - jQuery selector for pane
  555. , contentSelector: ".ui-layout-content" // INNER div/element to auto-size so only it scrolls, not the entire pane!
  556. , contentIgnoreSelector: ".ui-layout-ignore" // element(s) to 'ignore' when measuring 'content'
  557. , findNestedContent: false // true = $P.find(contentSelector), false = $P.children(contentSelector)
  558. // GENERIC ROOT-CLASSES - for auto-generated classNames
  559. , paneClass: "ui-layout-pane" // Layout Pane
  560. , resizerClass: "ui-layout-resizer" // Resizer Bar
  561. , togglerClass: "ui-layout-toggler" // Toggler Button
  562. , buttonClass: "ui-layout-button" // CUSTOM Buttons - eg: '[ui-layout-button]-toggle/-open/-close/-pin'
  563. // ELEMENT SIZE & SPACING
  564. //, size: 100 // MUST be pane-specific -initial size of pane
  565. , minSize: 0 // when manually resizing a pane
  566. , maxSize: 0 // ditto, 0 = no limit
  567. , spacing_open: 6 // space between pane and adjacent panes - when pane is 'open'
  568. , spacing_closed: 6 // ditto - when pane is 'closed'
  569. , togglerLength_open: 50 // Length = WIDTH of toggler button on north/south sides - HEIGHT on east/west sides
  570. , togglerLength_closed: 50 // 100% OR -1 means 'full height/width of resizer bar' - 0 means 'hidden'
  571. , togglerAlign_open: "center" // top/left, bottom/right, center, OR...
  572. , togglerAlign_closed: "center" // 1 => nn = offset from top/left, -1 => -nn == offset from bottom/right
  573. , togglerTip_open: lang.Close // Toggler tool-tip (title)
  574. , togglerTip_closed: lang.Open // ditto
  575. , togglerContent_open: "" // text or HTML to put INSIDE the toggler
  576. , togglerContent_closed: "" // ditto
  577. // RESIZING OPTIONS
  578. , resizerDblClickToggle: true //
  579. , autoResize: true // IF size is 'auto' or a percentage, then recalc 'pixel size' whenever the layout resizes
  580. , autoReopen: true // IF a pane was auto-closed due to noRoom, reopen it when there is room? False = leave it closed
  581. , resizerDragOpacity: 1 // option for ui.draggable
  582. //, resizerCursor: "" // MUST be pane-specific - cursor when over resizer-bar
  583. , maskContents: false // true = add DIV-mask over-or-inside this pane so can 'drag' over IFRAMES
  584. , maskObjects: false // true = add IFRAME-mask over-or-inside this pane to cover objects/applets - content-mask will overlay this mask
  585. , maskZindex: null // will override zIndexes.content_mask if specified - not applicable to iframe-panes
  586. , resizingGrid: false // grid size that the resizers will snap-to during resizing, eg: [20,20]
  587. , livePaneResizing: false // true = LIVE Resizing as resizer is dragged
  588. , liveContentResizing: false // true = re-measure header/footer heights as resizer is dragged
  589. , liveResizingTolerance: 1 // how many px change before pane resizes, to control performance
  590. // TIPS & MESSAGES - also see lang object
  591. , noRoomToOpenTip: lang.noRoomToOpenTip
  592. , resizerTip: lang.Resize // Resizer tool-tip (title)
  593. , sliderTip: lang.Slide // resizer-bar triggers 'sliding' when pane is closed
  594. , sliderCursor: "pointer" // cursor when resizer-bar will trigger 'sliding'
  595. , slideTrigger_open: "click" // click, dblclick, mouseenter
  596. , slideTrigger_close: "mouseleave"// click, mouseleave
  597. , slideDelay_open: 300 // applies only for mouseenter event - 0 = instant open
  598. , slideDelay_close: 300 // applies only for mouseleave event (300ms is the minimum!)
  599. , hideTogglerOnSlide: false // when pane is slid-open, should the toggler show?
  600. , preventQuickSlideClose: $.layout.browser.webkit // Chrome triggers slideClosed as it is opening
  601. , preventPrematureSlideClose: false // handle incorrect mouseleave trigger, like when over a SELECT-list in IE
  602. // HOT-KEYS & MISC
  603. , showOverflowOnHover: false // will bind allowOverflow() utility to pane.onMouseOver
  604. , enableCursorHotkey: true // enabled 'cursor' hotkeys
  605. //, customHotkey: "" // MUST be pane-specific - EITHER a charCode OR a character
  606. , customHotkeyModifier: "SHIFT" // either 'SHIFT', 'CTRL' or 'CTRL+SHIFT' - NOT 'ALT'
  607. // PANE ANIMATION
  608. // NOTE: fxSss_open, fxSss_close & fxSss_size options (eg: fxName_open) are auto-generated if not passed
  609. , fxName: "slide" // ('none' or blank), slide, drop, scale -- only relevant to 'open' & 'close', NOT 'size'
  610. , fxSpeed: null // slow, normal, fast, 200, nnn - if passed, will OVERRIDE fxSettings.duration
  611. , fxSettings: {} // can be passed, eg: { easing: "easeOutBounce", duration: 1500 }
  612. , fxOpacityFix: true // tries to fix opacity in IE to restore anti-aliasing after animation
  613. , animatePaneSizing: false // true = animate resizing after dragging resizer-bar OR sizePane() is called
  614. /* NOTE: Action-specific FX options are auto-generated from the options above if not specifically set:
  615. fxName_open: "slide" // 'Open' pane animation
  616. fnName_close: "slide" // 'Close' pane animation
  617. fxName_size: "slide" // 'Size' pane animation - when animatePaneSizing = true
  618. fxSpeed_open: null
  619. fxSpeed_close: null
  620. fxSpeed_size: null
  621. fxSettings_open: {}
  622. fxSettings_close: {}
  623. fxSettings_size: {}
  624. */
  625. // CHILD/NESTED LAYOUTS
  626. , childOptions: null // Layout-options for nested/child layout - even {} is valid as options
  627. , initChildLayout: true // true = child layout will be created as soon as _this_ layout completes initialization
  628. , destroyChildLayout: true // true = destroy child-layout if this pane is destroyed
  629. , resizeChildLayout: true // true = trigger child-layout.resizeAll() when this pane is resized
  630. // PANE CALLBACKS
  631. , triggerEventsOnLoad: false // true = trigger onopen OR onclose callbacks when layout initializes
  632. , triggerEventsDuringLiveResize: true // true = trigger onresize callback REPEATEDLY if livePaneResizing==true
  633. , onshow_start: null // CALLBACK when pane STARTS to Show - BEFORE onopen/onhide_start
  634. , onshow_end: null // CALLBACK when pane ENDS being Shown - AFTER onopen/onhide_end
  635. , onhide_start: null // CALLBACK when pane STARTS to Close - BEFORE onclose_start
  636. , onhide_end: null // CALLBACK when pane ENDS being Closed - AFTER onclose_end
  637. , onopen_start: null // CALLBACK when pane STARTS to Open
  638. , onopen_end: null // CALLBACK when pane ENDS being Opened
  639. , onclose_start: null // CALLBACK when pane STARTS to Close
  640. , onclose_end: null // CALLBACK when pane ENDS being Closed
  641. , onresize_start: null // CALLBACK when pane STARTS being Resized ***FOR ANY REASON***
  642. , onresize_end: null // CALLBACK when pane ENDS being Resized ***FOR ANY REASON***
  643. , onsizecontent_start: null // CALLBACK when sizing of content-element STARTS
  644. , onsizecontent_end: null // CALLBACK when sizing of content-element ENDS
  645. , onswap_start: null // CALLBACK when pane STARTS to Swap
  646. , onswap_end: null // CALLBACK when pane ENDS being Swapped
  647. , ondrag_start: null // CALLBACK when pane STARTS being ***MANUALLY*** Resized
  648. , ondrag_end: null // CALLBACK when pane ENDS being ***MANUALLY*** Resized
  649. }
  650. /*
  651. * PANE-SPECIFIC SETTINGS
  652. * - options listed below MUST be specified per-pane - they CANNOT be set under 'panes'
  653. * - all options under the 'panes' key can also be set specifically for any pane
  654. * - most options under the 'panes' key apply only to 'border-panes' - NOT the the center-pane
  655. */
  656. , north: {
  657. paneSelector: ".ui-layout-north"
  658. , size: "auto" // eg: "auto", "30%", .30, 200
  659. , resizerCursor: "n-resize" // custom = url(myCursor.cur)
  660. , customHotkey: "" // EITHER a charCode (43) OR a character ("o")
  661. }
  662. , south: {
  663. paneSelector: ".ui-layout-south"
  664. , size: "auto"
  665. , resizerCursor: "s-resize"
  666. , customHotkey: ""
  667. }
  668. , east: {
  669. paneSelector: ".ui-layout-east"
  670. , size: 200
  671. , resizerCursor: "e-resize"
  672. , customHotkey: ""
  673. }
  674. , west: {
  675. paneSelector: ".ui-layout-west"
  676. , size: 200
  677. , resizerCursor: "w-resize"
  678. , customHotkey: ""
  679. }
  680. , center: {
  681. paneSelector: ".ui-layout-center"
  682. , minWidth: 0
  683. , minHeight: 0
  684. }
  685. };
  686. $.layout.optionsMap = {
  687. // layout/global options - NOT pane-options
  688. layout: ("stateManagement,effects,zIndexes,"
  689. + "name,zIndex,scrollToBookmarkOnLoad,showErrorMessages,"
  690. + "resizeWithWindow,resizeWithWindowDelay,resizeWithWindowMaxDelay,"
  691. + "onresizeall,onresizeall_start,onresizeall_end,onload,onunload,autoBindCustomButtons").split(",")
  692. // borderPanes: [ ALL options that are NOT specified as 'layout' ]
  693. // default.panes options that apply to the center-pane (most options apply _only_ to border-panes)
  694. , center: ("paneClass,contentSelector,contentIgnoreSelector,findNestedContent,applyDemoStyles,triggerEventsOnLoad,"
  695. + "showOverflowOnHover,maskContents,maskObjects,liveContentResizing,"
  696. + "childOptions,initChildLayout,resizeChildLayout,destroyChildLayout,"
  697. + "onresize,onresize_start,onresize_end,onsizecontent,onsizecontent_start,onsizecontent_end").split(",")
  698. // options that MUST be specifically set 'per-pane' - CANNOT set in the panes (defaults) key
  699. , noDefault: ("paneSelector,resizerCursor,customHotkey").split(",")
  700. };
  701. /**
  702. * Processes options passed in converts flat-format data into subkey (JSON) format
  703. * In flat-format, subkeys are _currently_ separated with 2 underscores, like north__optName
  704. * Plugins may also call this method so they can transform their own data
  705. *
  706. * @param {!Object} hash Data/options passed by user - may be a single level or nested levels
  707. * @return {Object} Returns hash of minWidth & minHeight
  708. */
  709. $.layout.transformData = function (hash) {
  710. var json = { panes: {}, center: {} } // init return object
  711. , data, branch, optKey, keys, key, val, i, c;
  712. if (typeof hash !== "object") return json; // no options passed
  713. // convert all 'flat-keys' to 'sub-key' format
  714. for (optKey in hash) {
  715. branch = json;
  716. data = $.layout.optionsMap.layout;
  717. val = hash[ optKey ];
  718. keys = optKey.split("__"); // eg: west__size or north__fxSettings__duration
  719. c = keys.length - 1;
  720. // convert underscore-delimited to subkeys
  721. for (i=0; i <= c; i++) {
  722. key = keys[i];
  723. if (i === c)
  724. branch[key] = val;
  725. else if (!branch[key])
  726. branch[key] = {}; // create the subkey
  727. // recurse to sub-key for next loop - if not done
  728. branch = branch[key];
  729. }
  730. }
  731. return json;
  732. }
  733. // INTERNAL CONFIG DATA - DO NOT CHANGE THIS!
  734. $.layout.backwardCompatibility = {
  735. // data used by renameOldOptions()
  736. map: {
  737. // OLD Option Name: NEW Option Name
  738. applyDefaultStyles: "applyDemoStyles"
  739. , resizeNestedLayout: "resizeChildLayout"
  740. , resizeWhileDragging: "livePaneResizing"
  741. , resizeContentWhileDragging: "liveContentResizing"
  742. , triggerEventsWhileDragging: "triggerEventsDuringLiveResize"
  743. , maskIframesOnResize: "maskContents"
  744. , useStateCookie: "stateManagement.enabled"
  745. , "cookie.autoLoad": "stateManagement.autoLoad"
  746. , "cookie.autoSave": "stateManagement.autoSave"
  747. , "cookie.keys": "stateManagement.stateKeys"
  748. , "cookie.name": "stateManagement.cookie.name"
  749. , "cookie.domain": "stateManagement.cookie.domain"
  750. , "cookie.path": "stateManagement.cookie.path"
  751. , "cookie.expires": "stateManagement.cookie.expires"
  752. , "cookie.secure": "stateManagement.cookie.secure"
  753. }
  754. /**
  755. * @param {Object} opts
  756. */
  757. , renameOptions: function (opts) {
  758. var map = $.layout.backwardCompatibility.map
  759. , oldData, newData, value
  760. ;
  761. for (var itemPath in map) {
  762. oldData = getBranch( itemPath );
  763. value = oldData.branch[ oldData.key ]
  764. if (value !== undefined) {
  765. newData = getBranch( map[itemPath], true )
  766. newData.branch[ newData.key ] = value;
  767. delete oldData.branch[ oldData.key ];
  768. }
  769. }
  770. /**
  771. * @param {string} path
  772. * @param {boolean=} [create=false] Create path if does not exist
  773. */
  774. function getBranch (path, create) {
  775. var a = path.split(".") // split keys into array
  776. , c = a.length - 1
  777. , D = { branch: opts, key: a[c] } // init branch at top & set key (last item)
  778. , i = 0, k, undef;
  779. for (; i<c; i++) { // skip the last key (data)
  780. k = a[i];
  781. if (D.branch[ k ] == undefined) { // child-key does not exist
  782. if (create) {
  783. D.branch = D.branch[ k ] = {}; // create child-branch
  784. }
  785. else // can't go any farther
  786. D.branch = {}; // branch is undefined
  787. }
  788. else
  789. D.branch = D.branch[ k ]; // get child-branch
  790. }
  791. return D;
  792. };
  793. }
  794. /**
  795. * @param {Object} opts
  796. */
  797. , renameAllOptions: function (opts) {
  798. var ren = $.layout.backwardCompatibility.renameOptions;
  799. // rename root (layout) options
  800. ren( opts );
  801. // rename 'defaults' to 'panes'
  802. if (opts.defaults) {
  803. if (typeof opts.panes !== "object")
  804. opts.panes = {};
  805. $.extend(true, opts.panes, opts.defaults);
  806. delete opts.defaults;
  807. }
  808. // rename options in the the options.panes key
  809. if (opts.panes) ren( opts.panes );
  810. // rename options inside *each pane key*, eg: options.west
  811. $.each($.layout.config.allPanes, function (i, pane) {
  812. if (opts[pane]) ren( opts[pane] );
  813. });
  814. return opts;
  815. }
  816. };
  817. /* ============================================================
  818. * BEGIN WIDGET: $( selector ).layout( {options} );
  819. * ============================================================
  820. */
  821. $.fn.layout = function (opts) {
  822. var
  823. // local aliases to global data
  824. browser = $.layout.browser
  825. , lang = $.layout.language // internal alias
  826. , _c = $.layout.config
  827. // local aliases to utlity methods
  828. , cssW = $.layout.cssWidth
  829. , cssH = $.layout.cssHeight
  830. , elDims = $.layout.getElementDimensions
  831. , elCSS = $.layout.getElementCSS
  832. /**
  833. * options - populated by initOptions()
  834. */
  835. , options = $.extend(true, {}, $.layout.defaults)
  836. , effects = options.effects = $.extend(true, {}, $.layout.effects)
  837. /**
  838. * layout-state object
  839. */
  840. , state = {
  841. // generate unique ID to use for event.namespace so can unbind only events added by 'this layout'
  842. id: "layout"+ $.now() // code uses alias: sID
  843. , initialized: false
  844. , container: {} // init all keys
  845. , north: {}
  846. , south: {}
  847. , east: {}
  848. , west: {}
  849. , center: {}
  850. }
  851. /**
  852. * parent/child-layout pointers
  853. */
  854. //, Layout = false - exists ONLY as Instance.Layout
  855. , children = {
  856. north: null
  857. , south: null
  858. , east: null
  859. , west: null
  860. , center: null
  861. }
  862. /*
  863. * ###########################
  864. * INTERNAL HELPER FUNCTIONS
  865. * ###########################
  866. */
  867. /**
  868. * Manages all internal timers
  869. */
  870. , timer = {
  871. data: {}
  872. , set: function (s, fn, ms) { timer.clear(s); timer.data[s] = setTimeout(fn, ms); }
  873. , clear: function (s) { var t=timer.data; if (t[s]) {clearTimeout(t[s]); delete t[s];} }
  874. }
  875. , _log = function (msg, popup) {
  876. $.layout.msg( msg, (popup && options.showErrorMessages) );
  877. }
  878. /**
  879. * Executes a Callback function after a trigger event, like resize, open or close
  880. *
  881. * @param {?string} pane This is passed only so we can pass the 'pane object' to the callback
  882. * @param {(string|function())} v_fn Accepts a function name, OR a comma-delimited array: [0]=function name, [1]=argument
  883. */
  884. , _execCallback = function (pane, v_fn) {
  885. if (!v_fn) return;
  886. var fn;
  887. try {
  888. if (typeof v_fn === "function")
  889. fn = v_fn;
  890. else if (!isStr(v_fn))
  891. return;
  892. else if (v_fn.match(/,/)) {
  893. // function name cannot contain a comma, so must be a function name AND a 'name' parameter
  894. var
  895. args = v_fn.split(",")
  896. , fn = eval(args[0])
  897. ;
  898. if (typeof fn=="function" && args.length > 1)
  899. return fn(args[1]); // pass the argument parsed from 'list'
  900. }
  901. else // just the name of an external function?
  902. fn = eval(v_fn);
  903. if ($.isFunction( fn )) {
  904. if (pane && $Ps[pane])
  905. // pass data: pane-name, pane-element, pane-state, pane-options, and layout-name
  906. return fn( pane, $Ps[pane], state[pane], options[pane], options.name );
  907. else // must be a layout/container callback - pass suitable info
  908. return fn( Instance, state, options, options.name );
  909. }
  910. }
  911. catch (ex) {}
  912. }
  913. , trigger = function (fnName, pane) {
  914. var o = options
  915. , fn = pane && o[pane] ? o[pane][fnName] : o[fnName];
  916. if (fn) _execCallback(pane || null, fn);
  917. }
  918. /**
  919. * cure iframe display issues in IE & other browsers
  920. */
  921. , _fixIframe = function (pane) {
  922. if (browser.mozilla) return; // skip FireFox - it auto-refreshes iframes onShow
  923. var $P = $Ps[pane];
  924. // if the 'pane' is an iframe, do it
  925. if (state[pane].tagName === "IFRAME")
  926. $P.css(_c.hidden).css(_c.visible);
  927. else // ditto for any iframes INSIDE the pane
  928. $P.find('IFRAME').css(_c.hidden).css(_c.visible);
  929. }
  930. /**
  931. * @param {string} pane Can accept ONLY a 'pane' (east, west, etc)
  932. * @param {number=} outerSize (optional) Can pass a width, allowing calculations BEFORE element is resized
  933. * @return {number} Returns the innerHeight/Width of el by subtracting padding and borders
  934. */
  935. , cssSize = function (pane, outerSize) {
  936. var fn = _c[pane].dir=="horz" ? cssH : cssW;
  937. return fn($Ps[pane], outerSize);
  938. }
  939. /**
  940. * @param {string} pane Can accept ONLY a 'pane' (east, west, etc)
  941. * @return {Object} Returns hash of minWidth & minHeight
  942. */
  943. , cssMinDims = function (pane) {
  944. // minWidth/Height means CSS width/height = 1px
  945. var
  946. $P = $Ps[pane]
  947. dir = _c[pane].dir
  948. , d = {
  949. minWidth: 1001 - cssW($P, 1000)
  950. , minHeight: 1001 - cssH($P, 1000)
  951. }
  952. ;
  953. if (dir === "horz") d.minSize = d.minHeight;
  954. if (dir === "vert") d.minSize = d.minWidth;
  955. return d;
  956. }
  957. // TODO: see if these methods can be made more useful...
  958. // TODO: *maybe* return cssW/H from these so caller can use this info
  959. /**
  960. * @param {(string|!Object)} el
  961. * @param {number=} outerWidth
  962. * @param {boolean=} [autoHide=false]
  963. */
  964. , setOuterWidth = function (el, outerWidth, autoHide) {
  965. var $E = el, w;
  966. if (isStr(el)) $E = $Ps[el]; // west
  967. else if (!el.jquery) $E = $(el);
  968. w = cssW($E, outerWidth);
  969. $E.css({ width: w });
  970. if (w > 0) {
  971. if (autoHide && $E.data('autoHidden') && $E.innerHeight() > 0) {
  972. $E.show().data('autoHidden', false);
  973. if (!browser.mozilla) // FireFox refreshes iframes - IE does not
  974. // make hidden, then visible to 'refresh' display after animation
  975. $E.css(_c.hidden).css(_c.visible);
  976. }
  977. }
  978. else if (autoHide && !$E.data('autoHidden'))
  979. $E.hide().data('autoHidden', true);
  980. }
  981. /**
  982. * @param {(string|!Object)} el
  983. * @param {number=} outerHeight
  984. * @param {boolean=} [autoHide=false]
  985. */
  986. , setOuterHeight = function (el, outerHeight, autoHide) {
  987. var $E = el, h;
  988. if (isStr(el)) $E = $Ps[el]; // west
  989. else if (!el.jquery) $E = $(el);
  990. h = cssH($E, outerHeight);
  991. $E.css({ height: h, visibility: "visible" }); // may have been 'hidden' by sizeContent
  992. if (h > 0 && $E.innerWidth() > 0) {
  993. if (autoHide && $E.data('autoHidden')) {
  994. $E.show().data('autoHidden', false);
  995. if (!browser.mozilla) // FireFox refreshes iframes - IE does not
  996. $E.css(_c.hidden).css(_c.visible);
  997. }
  998. }
  999. else if (autoHide && !$E.data('autoHidden'))
  1000. $E.hide().data('autoHidden', true);
  1001. }
  1002. /**
  1003. * @param {(string|!Object)} el
  1004. * @param {number=} outerSize
  1005. * @param {boolean=} [autoHide=false]
  1006. */
  1007. , setOuterSize = function (el, outerSize, autoHide) {
  1008. if (_c[pane].dir=="horz") // pane = north or south
  1009. setOuterHeight(el, outerSize, autoHide);
  1010. else // pane = east or west
  1011. setOuterWidth(el, outerSize, autoHide);
  1012. }
  1013. /**
  1014. * Converts any 'size' params to a pixel/integer size, if not already
  1015. * If 'auto' or a decimal/percentage is passed as 'size', a pixel-size is calculated
  1016. *
  1017. /**
  1018. * @param {string} pane
  1019. * @param {(string|number)=} size
  1020. * @param {string=} [dir]
  1021. * @return {number}
  1022. */
  1023. , _parseSize = function (pane, size, dir) {
  1024. if (!dir) dir = _c[pane].dir;
  1025. if (isStr(size) && size.match(/%/))
  1026. size = (size === '100%') ? -1 : parseInt(size, 10) / 100; // convert % to decimal
  1027. if (size === 0)
  1028. return 0;
  1029. else if (size >= 1)
  1030. return parseInt(size, 10);
  1031. var o = options, avail = 0;
  1032. if (dir=="horz") // north or south or center.minHeight
  1033. avail = sC.innerHeight - ($Ps.north ? o.north.spacing_open : 0) - ($Ps.south ? o.south.spacing_open : 0);
  1034. else if (dir=="vert") // east or west or center.minWidth
  1035. avail = sC.innerWidth - ($Ps.west ? o.west.spacing_open : 0) - ($Ps.east ? o.east.spacing_open : 0);
  1036. if (size === -1) // -1 == 100%
  1037. return avail;
  1038. else if (size > 0) // percentage, eg: .25
  1039. return round(avail * size);
  1040. else if (pane=="center")
  1041. return 0;
  1042. else { // size < 0 || size=='auto' || size==Missing || size==Invalid
  1043. // auto-size the pane
  1044. var
  1045. $P = $Ps[pane]
  1046. , dim = (dir === "horz" ? "height" : "width")
  1047. , vis = $.layout.showInvisibly($P) // show pane invisibly if hidden
  1048. , s = $P.css(dim); // SAVE current size
  1049. ;
  1050. $P.css(dim, "auto");
  1051. size = (dim === "height") ? $P.outerHeight() : $P.outerWidth(); // MEASURE
  1052. $P.css(dim, s).css(vis); // RESET size & visibility
  1053. return size;
  1054. }
  1055. }
  1056. /**
  1057. * Calculates current 'size' (outer-width or outer-height) of a border-pane - optionally with 'pane-spacing' added
  1058. *
  1059. * @param {(string|!Object)} pane
  1060. * @param {boolean=} [inclSpace=false]
  1061. * @return {number} Returns EITHER Width for east/west panes OR Height for north/south panes - adjusted for boxModel & browser
  1062. */
  1063. , getPaneSize = function (pane, inclSpace) {
  1064. var
  1065. $P = $Ps[pane]
  1066. , o = options[pane]
  1067. , s = state[pane]
  1068. , oSp = (inclSpace ? o.spacing_open : 0)
  1069. , cSp = (inclSpace ? o.spacing_closed : 0)
  1070. ;
  1071. if (!$P || s.isHidden)
  1072. return 0;
  1073. else if (s.isClosed || (s.isSliding && inclSpace))
  1074. return cSp;
  1075. else if (_c[pane].dir === "horz")
  1076. return $P.outerHeight() + oSp;
  1077. else // dir === "vert"
  1078. return $P.outerWidth() + oSp;
  1079. }
  1080. /**
  1081. * Calculate min/max pane dimensions and limits for resizing
  1082. *
  1083. * @param {string} pane
  1084. * @param {boolean=} [slide=false]
  1085. */
  1086. , setSizeLimits = function (pane, slide) {
  1087. if (!isInitialized()) return;
  1088. var
  1089. o = options[pane]
  1090. , s = state[pane]
  1091. , c = _c[pane]
  1092. , dir = c.dir
  1093. , side = c.side.toLowerCase()
  1094. , type = c.sizeType.toLowerCase()
  1095. , isSliding = (slide != undefined ? slide : s.isSliding) // only open() passes 'slide' param
  1096. , $P = $Ps[pane]
  1097. , paneSpacing = o.spacing_open
  1098. // measure the pane on the *opposite side* from this pane
  1099. , altPane = _c.oppositeEdge[pane]
  1100. , altS = state[altPane]
  1101. , $altP = $Ps[altPane]
  1102. , altPaneSize = (!$altP || altS.isVisible===false || altS.isSliding ? 0 : (dir=="horz" ? $altP.outerHeight() : $altP.outerWidth()))
  1103. , altPaneSpacing = ((!$altP || altS.isHidden ? 0 : options[altPane][ altS.isClosed !== false ? "spacing_closed" : "spacing_open" ]) || 0)
  1104. // limitSize prevents this pane from 'overlapping' opposite pane
  1105. , containerSize = (dir=="horz" ? sC.innerHeight : sC.innerWidth)
  1106. , minCenterDims = cssMinDims("center")
  1107. , minCenterSize = dir=="horz" ? max(options.center.minHeight, minCenterDims.minHeight) : max(options.center.minWidth, minCenterDims.minWidth)
  1108. // if pane is 'sliding', then ignore center and alt-pane sizes - because 'overlays' them
  1109. , limitSize = (containerSize - paneSpacing - (isSliding ? 0 : (_parseSize("center", minCenterSize, dir) + altPaneSize + altPaneSpacing)))
  1110. , minSize = s.minSize = max( _parseSize(pane, o.minSize), cssMinDims(pane).minSize )
  1111. , maxSize = s.maxSize = min( (o.maxSize ? _parseSize(pane, o.maxSize) : 100000), limitSize )
  1112. , r = s.resizerPosition = {} // used to set resizing limits
  1113. , top = sC.insetTop
  1114. , left = sC.insetLeft
  1115. , W = sC.innerWidth
  1116. , H = sC.innerHeight
  1117. , rW = o.spacing_open // subtract resizer-width to get top/left position for south/east
  1118. ;
  1119. switch (pane) {
  1120. case "north": r.min = top + minSize;
  1121. r.max = top + maxSize;
  1122. break;
  1123. case "west": r.min = left + minSize;
  1124. r.max = left + maxSize;
  1125. break;
  1126. case "south": r.min = top + H - maxSize - rW;
  1127. r.max = top + H - minSize - rW;
  1128. break;
  1129. case "east": r.min = left + W - maxSize - rW;
  1130. r.max = left + W - minSize - rW;
  1131. break;
  1132. };
  1133. }
  1134. /**
  1135. * Returns data for setting the size/position of center pane. Also used to set Height for east/west panes
  1136. *
  1137. * @return JSON Returns a hash of all dimensions: top, bottom, left, right, (outer) width and (outer) height
  1138. */
  1139. , calcNewCenterPaneDims = function () {
  1140. var d = {
  1141. top: getPaneSize("north", true) // true = include 'spacing' value for pane
  1142. , bottom: getPaneSize("south", true)
  1143. , left: getPaneSize("west", true)
  1144. , right: getPaneSize("east", true)
  1145. , width: 0
  1146. , height: 0
  1147. };
  1148. // NOTE: sC = state.container
  1149. // calc center-pane outer dimensions
  1150. d.width = sC.innerWidth - d.left - d.right; // outerWidth
  1151. d.height = sC.innerHeight - d.bottom - d.top; // outerHeight
  1152. // add the 'container border/padding' to get final positions relative to the container
  1153. d.top += sC.insetTop;
  1154. d.bottom += sC.insetBottom;
  1155. d.left += sC.insetLeft;
  1156. d.right += sC.insetRight;
  1157. return d;
  1158. }
  1159. /**
  1160. * @param {!Object} el
  1161. * @param {boolean=} [allStates=false]
  1162. */
  1163. , getHoverClasses = function (el, allStates) {
  1164. var
  1165. $El = $(el)
  1166. , type = $El.data("layoutRole")
  1167. , pane = $El.data("layoutEdge")
  1168. , o = options[pane]
  1169. , root = o[type +"Class"]
  1170. , _pane = "-"+ pane // eg: "-west"
  1171. , _open = "-open"
  1172. , _closed = "-closed"
  1173. , _slide = "-sliding"
  1174. , _hover = "-hover " // NOTE the trailing space
  1175. , _state = $El.hasClass(root+_closed) ? _closed : _open
  1176. , _alt = _state === _closed ? _open : _closed
  1177. , classes = (root+_hover) + (root+_pane+_hover) + (root+_state+_hover) + (root+_pane+_state+_hover)
  1178. ;
  1179. if (allStates) // when 'removing' classes, also remove alternate-state classes
  1180. classes += (root+_alt+_hover) + (root+_pane+_alt+_hover);
  1181. if (type=="resizer" && $El.hasClass(root+_slide))
  1182. classes += (root+_slide+_hover) + (root+_pane+_slide+_hover);
  1183. return $.trim(classes);
  1184. }
  1185. , addHover = function (evt, el) {
  1186. var $E = $(el || this);
  1187. if (evt && $E.data("layoutRole") === "toggler")
  1188. evt.stopPropagation(); // prevent triggering 'slide' on Resizer-bar
  1189. $E.addClass( getHoverClasses($E) );
  1190. }
  1191. , removeHover = function (evt, el) {
  1192. var $E = $(el || this);
  1193. $E.removeClass( getHoverClasses($E, true) );
  1194. }
  1195. , onResizerEnter = function (evt) { // ALSO called by toggler.mouseenter
  1196. if ($.fn.disableSelection)
  1197. $("body").disableSelection();
  1198. }
  1199. , onResizerLeave = function (evt, el) {
  1200. var
  1201. e = el || this // el is only passed when called by the timer
  1202. , pane = $(e).data("layoutEdge")
  1203. , name = pane +"ResizerLeave"
  1204. ;
  1205. timer.clear(pane+"_openSlider"); // cancel slideOpen timer, if set
  1206. timer.clear(name); // cancel enableSelection timer - may re/set below
  1207. // this method calls itself on a timer because it needs to allow
  1208. // enough time for dragging to kick-in and set the isResizing flag
  1209. // dragging has a 100ms delay set, so this delay must be >100
  1210. if (!el) // 1st call - mouseleave event
  1211. timer.set(name, function(){ onResizerLeave(evt, e); }, 200);
  1212. // if user is resizing, then dragStop will enableSelection(), so can skip it here
  1213. else if (!state[pane].isResizing && $.fn.enableSelection) // 2nd call - by timer
  1214. $("body").enableSelection();
  1215. }
  1216. /*
  1217. * ###########################
  1218. * INITIALIZATION METHODS
  1219. * ###########################
  1220. */
  1221. /**
  1222. * Initialize the layout - called automatically whenever an instance of layout is created
  1223. *
  1224. * @see none - triggered onInit
  1225. * @return mixed true = fully initialized | false = panes not initialized (yet) | 'cancel' = abort
  1226. */
  1227. , _create = function () {
  1228. // initialize config/options
  1229. initOptions();
  1230. var o = options;
  1231. // TEMP state so isInitialized returns true during init process
  1232. state.creatingLayout = true;
  1233. // init plugins for this layout, if there are any (eg: stateManagement)
  1234. runPluginCallbacks( Instance, $.layout.onCreate );
  1235. // options & state have been initialized, so now run beforeLoad callback
  1236. // onload will CANCEL layout creation if it returns false
  1237. if (false === _execCallback(null, o.onload_start))
  1238. return 'cancel';
  1239. // initialize the container element
  1240. _initContainer();
  1241. // bind hotkey function - keyDown - if required
  1242. initHotkeys();
  1243. // bind window.onunload
  1244. $(window).bind("unload."+ sID, unload);
  1245. // init plugins for this layout, if there are any (eg: customButtons)
  1246. runPluginCallbacks( Instance, $.layout.onLoad );
  1247. // if this layout's container is another layout's pane, then set child/parent pointers
  1248. var parent = $N.data("parentLayout");
  1249. if (parent) {
  1250. Instance.hasParentLayout = true;
  1251. var pane = $N.data("layoutEdge"); // container's pane-name in parent-layout
  1252. // set pointers to THIS child-layout in parent-layout
  1253. // NOTE: parent.PANE.child is an ALIAS to parent.children.PANE
  1254. parent[pane].child = parent.children[pane] = Instance;
  1255. }
  1256. // if layout elements are hidden, then layout WILL NOT complete initialization!
  1257. // initLayoutElements will set initialized=true and run the onload callback IF successful
  1258. if (o.initPanes) _initLayoutElements();
  1259. delete state.creatingLayout;
  1260. return state.initialized;
  1261. }
  1262. /**
  1263. * Initialize the layout IF not already
  1264. *
  1265. * @see All methods in Instance run this test
  1266. * @return boolean true = layoutElements have been initialized | false = panes are not initialized (yet)
  1267. */
  1268. , isInitialized = function () {
  1269. if (state.initialized || state.creatingLayout) return true; // already initialized
  1270. else return _initLayoutElements(); // try to init panes NOW
  1271. }
  1272. /**
  1273. * Initialize the layout - called automatically whenever an instance of layout is created
  1274. *
  1275. * @see _create() & isInitialized
  1276. * @return An object pointer to the instance created
  1277. */
  1278. , _initLayoutElements = function (retry) {
  1279. // initialize config/options
  1280. var o = options;
  1281. // CANNOT init panes inside a hidden container!
  1282. if (!$N.is(":visible")) {
  1283. // handle Chrome bug where popup window 'has no height'
  1284. // if layout is BODY element, try again in 50ms
  1285. // SEE: http://layout.jquery-dev.net/samples/test_popup_window.html
  1286. if ( !retry && browser.webkit && $N[0].tagName === "BODY" )
  1287. setTimeout(function(){ _initLayoutElements(true); }, 50);
  1288. return false;
  1289. }
  1290. // a center pane is required, so make sure it exists
  1291. if (!getPane("center").length) {
  1292. _log( lang.errCenterPaneMissing, true );
  1293. return false;
  1294. }
  1295. // TEMP state so isInitialized returns true during init process
  1296. state.creatingLayout = true;
  1297. // update Container dims
  1298. $.extend(sC, elDims( $N ));
  1299. // check to see if this layout 'nested' inside a pane
  1300. if ($N.data("layoutRole") === "pane")
  1301. o.resizeWithWindow = false;
  1302. // initialize all layout elements
  1303. initPanes(); // size & position panes - calls initHandles() - which calls initResizable()
  1304. if (o.scrollToBookmarkOnLoad) {
  1305. var l = self.location;
  1306. if (l.hash) l.replace( l.hash ); // scrollTo Bookmark
  1307. }
  1308. // bind resizeAll() for 'this layout instance' to window.resize event
  1309. if (o.resizeWithWindow)
  1310. $(window).bind("resize."+ sID, windowResize);
  1311. delete state.creatingLayout;
  1312. state.initialized = true;
  1313. // init plugins for this layout, if there are any
  1314. runPluginCallbacks( Instance, $.layout.onReady );
  1315. // now run the onload callback, if exists
  1316. _execCallback(null, o.onload_end || o.onload);
  1317. return true; // elements initialized successfully
  1318. }
  1319. /**
  1320. * Initialize nested layouts - called when _initLayoutElements completes
  1321. *
  1322. * NOT CURRENTLY USED
  1323. *
  1324. * @see _initLayoutElements
  1325. * @return An object pointer to the instance created
  1326. */
  1327. , _initChildLayouts = function () {
  1328. $.each(_c.allPanes, function (idx, pane) {
  1329. if (options[pane].initChildLayout)
  1330. createChildLayout( pane );
  1331. });
  1332. }
  1333. /**
  1334. * Initialize nested layouts for a specific pane - can optionally pass layout-options
  1335. *
  1336. * @see _initChildLayouts
  1337. * @param {string} pane The pane being opened, ie: north, south, east, or west
  1338. * @param {Object=} [opts] Layout-options - if passed, will OVERRRIDE options[pane].childOptions
  1339. * @return An object pointer to the layout instance created - or null
  1340. */
  1341. , createChildLayout = function (pane, opts) {
  1342. var $P = $Ps[pane]
  1343. , o = opts || options[pane].childOptions
  1344. , C = children
  1345. , d = "layout"
  1346. // see if a child-layout ALREADY exists on this element
  1347. , L = $P ? (C[pane] = $P.data(d) || null) : false
  1348. ;
  1349. // if no layout exists, but childOptions are set, try to create the layout now
  1350. if (!L && $P && o)
  1351. L = C[pane] = $P.layout(o) || null;
  1352. if (L)
  1353. L.Layout = true; // set parent-flag in child - DO NOT set pointer or else have infinite recursion!
  1354. Instance[pane].child = C[pane]; // set pane-object pointer, even if null
  1355. }
  1356. , windowResize = function () {
  1357. var delay = Number(options.resizeWithWindowDelay);
  1358. if (delay < 10) delay = 100; // MUST have a delay!
  1359. // resizing uses a delay-loop because the resize event fires repeatly - except in FF, but delay anyway
  1360. timer.clear("winResize"); // if already running
  1361. timer.set("winResize", function(){
  1362. timer.clear("winResize");
  1363. timer.clear("winResizeRepeater");
  1364. var dims = elDims( $N );
  1365. // only trigger resizeAll() if container has changed size
  1366. if (dims.innerWidth !== sC.innerWidth || dims.innerHeight !== sC.innerHeight)
  1367. resizeAll();
  1368. }, delay);
  1369. // ALSO set fixed-delay timer, if not already running
  1370. if (!timer.data["winResizeRepeater"]) setWindowResizeRepeater();
  1371. }
  1372. , setWindowResizeRepeater = function () {
  1373. var delay = Number(options.resizeWithWindowMaxDelay);
  1374. if (delay > 0)
  1375. timer.set("winResizeRepeater", function(){ setWindowResizeRepeater(); resizeAll(); }, delay);
  1376. }
  1377. , unload = function () {
  1378. var o = options;
  1379. _execCallback(null, o.onunload_start);
  1380. // trigger plugin callabacks for this layout (eg: stateManagement)
  1381. runPluginCallbacks( Instance, $.layout.onUnload );
  1382. _execCallback(null, o.onunload_end || o.onunload);
  1383. }
  1384. /**
  1385. * Validate and initialize container CSS and events
  1386. *
  1387. * @see _create()
  1388. */
  1389. , _initContainer = function () {
  1390. var
  1391. N = $N[0]
  1392. , tag = sC.tagName = N.tagName
  1393. , id = sC.id = N.id
  1394. , cls = sC.className = N.className
  1395. , o = options
  1396. , name = o.name
  1397. , fullPage= (tag === "BODY")
  1398. , props = "overflow,position,margin,padding,border"
  1399. , CSS = {}
  1400. , hid = "hidden" // used A LOT!
  1401. ;
  1402. // sC -> state.container
  1403. sC.selector = $N.selector.split(".slice")[0];
  1404. sC.ref = (o.name ? o.name +' layout / ' : '') + tag + (id ? "#"+id : cls ? '.['+cls+']' : ''); // used in messages
  1405. $N .data("layout", Instance)
  1406. .data("layoutContainer", sID) // unique identifier for internal use
  1407. .addClass(o.containerClass)
  1408. ;
  1409. // SAVE original container CSS for use in destroy()
  1410. var css = "layoutCSS";
  1411. if (!$N.data(css)) {
  1412. // handle props like overflow different for BODY & HTML - has 'system default' values
  1413. if (fullPage) {
  1414. CSS = $.extend( elCSS($N, props), {
  1415. height: $N.css("height")
  1416. , overflow: $N.css("overflow")
  1417. , overflowX: $N.css("overflowX")
  1418. , overflowY: $N.css("overflowY")
  1419. });
  1420. // ALSO SAVE <HTML> CSS
  1421. var $H = $("html");
  1422. $H.data(css, {
  1423. height: "auto" // FF would return a fixed px-size!
  1424. , overflow: $H.css("overflow")
  1425. , overflowX: $H.css("overflowX")
  1426. , overflowY: $H.css("overflowY")
  1427. });
  1428. }
  1429. else // handle props normally for non-body elements
  1430. CSS = elCSS($N, props+",top,bottom,left,right,width,height,overflow,overflowX,overflowY");
  1431. $N.data(css, CSS);
  1432. }
  1433. try { // format html/body if this is a full page layout
  1434. if (fullPage) {
  1435. $("html").css({
  1436. height: "100%"
  1437. , overflow: hid
  1438. , overflowX: hid
  1439. , overflowY: hid
  1440. });
  1441. $("body").css({
  1442. position: "relative"
  1443. , height: "100%"
  1444. , overflow: hid
  1445. , overflowX: hid
  1446. , overflowY: hid
  1447. , margin: 0
  1448. , padding: 0 // TODO: test whether body-padding could be handled?
  1449. , border: "none" // a body-border creates problems because it cannot be measured!
  1450. });
  1451. // set current layout-container dimensions
  1452. $.extend(sC, elDims( $N ));
  1453. }
  1454. else { // set required CSS for overflow and position
  1455. // ENSURE container will not 'scroll'
  1456. CSS = { overflow: hid, overflowX: hid, overflowY: hid }
  1457. var
  1458. p = $N.css("position")
  1459. , h = $N.css("height")
  1460. ;
  1461. // if this is a NESTED layout, then container/outer-pane ALREADY has position and height
  1462. if (!$N.data("layoutRole")) {
  1463. if (!p || !p.match(/fixed|absolute|relative/))
  1464. CSS.position = "relative"; // container MUST have a 'position'
  1465. /*
  1466. if (!h || h=="auto")
  1467. CSS.height = "100%"; // container MUST have a 'height'
  1468. */
  1469. }
  1470. $N.css( CSS );
  1471. // set current layout-container dimensions
  1472. if ( $N.is(":visible") ) {
  1473. $.extend(sC, elDims( $N ));
  1474. if (o.showErrorMessages && sC.innerHeight < 1)
  1475. _log( lang.errContainerHeight.replace(/CONTAINER/, sC.ref), true );
  1476. }
  1477. }
  1478. } catch (ex) {}
  1479. }
  1480. /**
  1481. * Bind layout hotkeys - if options enabled
  1482. *
  1483. * @see _create() and addPane()
  1484. * @param {string=} [panes=""] The edge(s) to process
  1485. */
  1486. , initHotkeys = function (panes) {
  1487. panes = panes ? panes.split(",") : _c.borderPanes;
  1488. // bind keyDown to capture hotkeys, if option enabled for ANY pane
  1489. $.each(panes, function (i, pane) {
  1490. var o = options[pane];
  1491. if (o.enableCursorHotkey || o.customHotkey) {
  1492. $(document).bind("keydown."+ sID, keyDown); // only need to bind this ONCE
  1493. return false; // BREAK - binding was done
  1494. }
  1495. });
  1496. }
  1497. /**
  1498. * Build final OPTIONS data
  1499. *
  1500. * @see _create()
  1501. */
  1502. , initOptions = function () {
  1503. var data, d, pane, key, val, i, c, o;
  1504. // reprocess user's layout-options to have correct options sub-key structure
  1505. opts = $.layout.transformData( opts ); // panes = default subkey
  1506. // auto-rename old options for backward compatibility
  1507. opts = $.layout.backwardCompatibility.renameAllOptions( opts );
  1508. // if user-options has 'panes' key (pane-defaults), process it...
  1509. if (!$.isEmptyObject(opts.panes)) {
  1510. // REMOVE any pane-defaults that MUST be set per-pane
  1511. data = $.layout.optionsMap.noDefault;
  1512. for (i=0, c=data.length; i<c; i++) {
  1513. key = data[i];
  1514. delete opts.panes[key]; // OK if does not exist
  1515. }
  1516. // REMOVE any layout-options specified under opts.panes
  1517. data = $.layout.optionsMap.layout;
  1518. for (i=0, c=data.length; i<c; i++) {
  1519. key = data[i];
  1520. delete opts.panes[key]; // OK if does not exist
  1521. }
  1522. }
  1523. // MOVE any NON-layout-options to opts.panes
  1524. data = $.layout.optionsMap.layout;
  1525. var rootKeys = $.layout.config.optionRootKeys;
  1526. for (key in opts) {
  1527. val = opts[key];
  1528. if ($.inArray(key, rootKeys) < 0 && $.inArray(key, data) < 0) {
  1529. if (!opts.panes[key])
  1530. opts.panes[key] = $.isPlainObject(val) ? $.extend(true, {}, val) : val;
  1531. delete opts[key]
  1532. }
  1533. }
  1534. // START by updating ALL options from opts
  1535. $.extend(true, options, opts);
  1536. // CREATE final options (and config) for EACH pane
  1537. $.each(_c.allPanes, function (i, pane) {
  1538. // apply 'pane-defaults' to CONFIG.[PANE]
  1539. _c[pane] = $.extend( true, {}, _c.panes, _c[pane] );
  1540. d = options.panes;
  1541. o = options[pane];
  1542. // center-pane uses SOME keys in defaults.panes branch
  1543. if (pane === 'center') {
  1544. // ONLY copy keys from opts.panes listed in: $.layout.optionsMap.center
  1545. data = $.layout.optionsMap.center; // list of 'center-pane keys'
  1546. for (i=0, c=data.length; i<c; i++) { // loop the list...
  1547. key = data[i];
  1548. // only need to use pane-default if pane-specific value not set
  1549. if (!opts.center[key] && (opts.panes[key] || !o[key]))
  1550. o[key] = d[key]; // pane-default
  1551. }
  1552. }
  1553. else {
  1554. // border-panes use ALL keys in defaults.panes branch
  1555. o = options[pane] = $.extend({}, d, o); // re-apply pane-specific opts AFTER pane-defaults
  1556. createFxOptions( pane );
  1557. // ensure all border-pane-specific base-classes exist
  1558. if (!o.resizerClass) o.resizerClass = "ui-layout-resizer";
  1559. if (!o.togglerClass) o.togglerClass = "ui-layout-toggler";
  1560. }
  1561. // ensure we have base pane-class (ALL panes)
  1562. if (!o.paneClass) o.paneClass = "ui-layout-pane";
  1563. });
  1564. // update options.zIndexes if a zIndex-option specified
  1565. var zo = opts.zIndex
  1566. , z = options.zIndexes;
  1567. if (zo > 0) {
  1568. z.pane_normal = zo;
  1569. z.content_mask = max(zo+1, z.content_mask); // MIN = +1
  1570. z.resizer_normal = max(zo+2, z.resizer_normal); // MIN = +2
  1571. }
  1572. function createFxOptions ( pane ) {
  1573. var o = options[pane]
  1574. , d = options.panes;
  1575. // ensure fxSettings key to avoid errors
  1576. if (!o.fxSettings) o.fxSettings = {};
  1577. if (!d.fxSettings) d.fxSettings = {};
  1578. $.each(["_open","_close","_size"], function (i,n) {
  1579. var
  1580. sName = "fxName"+ n
  1581. , sSpeed = "fxSpeed"+ n
  1582. , sSettings = "fxSettings"+ n
  1583. // recalculate fxName according to specificity rules
  1584. , fxName = o[sName] =
  1585. o[sName] // options.west.fxName_open
  1586. || d[sName] // options.panes.fxName_open
  1587. || o.fxName // options.west.fxName
  1588. || d.fxName // options.panes.fxName
  1589. || "none" // MEANS $.layout.defaults.panes.fxName == "" || false || null || 0
  1590. ;
  1591. // validate fxName to ensure is valid effect - MUST have effect-config data in options.effects
  1592. if (fxName === "none" || !$.effects || !$.effects[fxName] || !options.effects[fxName])
  1593. fxName = o[sName] = "none"; // effect not loaded OR unrecognized fxName
  1594. // set vars for effects subkeys to simplify logic
  1595. var fx = options.effects[fxName] || {} // effects.slide
  1596. , fx_all = fx.all || null // effects.slide.all
  1597. , fx_pane = fx[pane] || null // effects.slide.west
  1598. ;
  1599. // create fxSpeed[_open|_close|_size]
  1600. o[sSpeed] =
  1601. o[sSpeed] // options.west.fxSpeed_open
  1602. || d[sSpeed] // options.west.fxSpeed_open
  1603. || o.fxSpeed // options.west.fxSpeed
  1604. || d.fxSpeed // options.panes.fxSpeed
  1605. || null // DEFAULT - let fxSetting.duration control speed
  1606. ;
  1607. // create fxSettings[_open|_close|_size]
  1608. o[sSettings] = $.extend(
  1609. {}
  1610. , fx_all // effects.slide.all
  1611. , fx_pane // effects.slide.west
  1612. , d.fxSettings // options.panes.fxSettings
  1613. , o.fxSettings // options.west.fxSettings
  1614. , d[sSettings] // options.panes.fxSettings_open
  1615. , o[sSettings] // options.west.fxSettings_open
  1616. );
  1617. });
  1618. // DONE creating action-specific-settings for this pane,
  1619. // so DELETE generic options - are no longer meaningful
  1620. delete o.fxName;
  1621. delete o.fxSpeed;
  1622. delete o.fxSettings;
  1623. }
  1624. // DELETE 'panes' key now that we are done - values were copied to EACH pane
  1625. delete options.panes;
  1626. }
  1627. /**
  1628. * Initialize module objects, styling, size and position for all panes
  1629. *
  1630. * @see _initElements()
  1631. * @param {string} pane The pane to process
  1632. */
  1633. , getPane = function (pane) {
  1634. var sel = options[pane].paneSelector
  1635. if (sel.substr(0,1)==="#") // ID selector
  1636. // NOTE: elements selected 'by ID' DO NOT have to be 'children'
  1637. return $N.find(sel).eq(0);
  1638. else { // class or other selector
  1639. var $P = $N.children(sel).eq(0);
  1640. // look for the pane nested inside a 'form' element
  1641. return $P.length ? $P : $N.children("form:first").children(sel).eq(0);
  1642. }
  1643. }
  1644. , initPanes = function () {
  1645. // NOTE: do north & south FIRST so we can measure their height - do center LAST
  1646. $.each(_c.allPanes, function (idx, pane) {
  1647. addPane( pane, true );
  1648. });
  1649. // init the pane-handles NOW in case we have to hide or close the pane below
  1650. initHandles();
  1651. // now that all panes have been initialized and initially-sized,
  1652. // make sure there is really enough space available for each pane
  1653. $.each(_c.borderPanes, function (i, pane) {
  1654. if ($Ps[pane] && state[pane].isVisible) { // pane is OPEN
  1655. setSizeLimits(pane);
  1656. makePaneFit(pane); // pane may be Closed, Hidden or Resized by makePaneFit()
  1657. }
  1658. });
  1659. // size center-pane AGAIN in case we 'closed' a border-pane in loop above
  1660. sizeMidPanes("center");
  1661. // Chrome/Webkit sometimes fires callbacks BEFORE it completes resizing!
  1662. // Before RC30.3, there was a 10ms delay here, but that caused layout
  1663. // to load asynchrously, which is BAD, so try skipping delay for now
  1664. // process pane contents and callbacks, and init/resize child-layout if exists
  1665. $.each(_c.allPanes, function (i, pane) {
  1666. var o = options[pane];
  1667. if ($Ps[pane]) {
  1668. if (state[pane].isVisible) { // pane is OPEN
  1669. sizeContent(pane);
  1670. // trigger pane.onResize if triggerEventsOnLoad = true
  1671. if (o.triggerEventsOnLoad)
  1672. _execCallback(pane, o.onresize_end || o.onresize);
  1673. // resize child - IF inner-layout already exists (created before this layout)
  1674. resizeChildLayout(pane);
  1675. }
  1676. // init childLayout - even if pane is not visible
  1677. if (o.initChildLayout && o.childOptions)
  1678. createChildLayout(pane);
  1679. }
  1680. });
  1681. }
  1682. /**
  1683. * Add a pane to the layout - subroutine of initPanes()
  1684. *
  1685. * @see initPanes()
  1686. * @param {string} pane The pane to process
  1687. * @param {boolean=} [force=false] Size content after init
  1688. */
  1689. , addPane = function (pane, force) {
  1690. if (!force && !isInitialized()) return;
  1691. var
  1692. o = options[pane]
  1693. , s = state[pane]
  1694. , c = _c[pane]
  1695. , fx = s.fx
  1696. , dir = c.dir
  1697. , spacing = o.spacing_open || 0
  1698. , isCenter = (pane === "center")
  1699. , CSS = {}
  1700. , $P = $Ps[pane]
  1701. , size, minSize, maxSize
  1702. ;
  1703. // if pane-pointer already exists, remove the old one first
  1704. if ($P)
  1705. removePane( pane, false, true, false );
  1706. else
  1707. $Cs[pane] = false; // init
  1708. $P = $Ps[pane] = getPane(pane);
  1709. if (!$P.length) {
  1710. $Ps[pane] = false; // logic
  1711. return;
  1712. }
  1713. // SAVE original Pane CSS
  1714. if (!$P.data("layoutCSS")) {
  1715. var props = "position,top,left,bottom,right,width,height,overflow,zIndex,display,backgroundColor,padding,margin,border";
  1716. $P.data("layoutCSS", elCSS($P, props));
  1717. }
  1718. // add classes, attributes & events
  1719. $P .data("parentLayout", Instance)
  1720. .data("layoutRole", "pane")
  1721. .data("layoutEdge", pane)
  1722. .css(c.cssReq).css("zIndex", options.zIndexes.pane_normal)
  1723. .css(o.applyDemoStyles ? c.cssDemo : {}) // demo styles
  1724. .addClass( o.paneClass +" "+ o.paneClass+"-"+pane ) // default = "ui-layout-pane ui-layout-pane-west" - may be a dupe of 'paneSelector'
  1725. .bind("mouseenter."+ sID, addHover )
  1726. .bind("mouseleave."+ sID, removeHover );
  1727. // create alias for pane data in Instance - initHandles will add more
  1728. Instance[pane] = { name: pane, pane: $Ps[pane], options: options[pane], state: state[pane], child: children[pane] };
  1729. // see if this pane has a 'scrolling-content element'
  1730. initContent(pane, false); // false = do NOT sizeContent() - called later
  1731. if (!isCenter) {
  1732. // call _parseSize AFTER applying pane classes & styles - but before making visible (if hidden)
  1733. // if o.size is auto or not valid, then MEASURE the pane and use that as its 'size'
  1734. size = s.size = _parseSize(pane, o.size);
  1735. minSize = _parseSize(pane,o.minSize) || 1;
  1736. maxSize = _parseSize(pane,o.maxSize) || 100000;
  1737. if (size > 0) size = max(min(size, maxSize), minSize);
  1738. // state for border-panes
  1739. s.isClosed = false; // true = pane is closed
  1740. s.isSliding = false; // true = pane is currently open by 'sliding' over adjacent panes
  1741. s.isResizing= false; // true = pane is in process of being resized
  1742. s.isHidden = false; // true = pane is hidden - no spacing, resizer or toggler is visible!
  1743. // array for 'pin buttons' whose classNames are auto-updated on pane-open/-close
  1744. if (!s.pins) s.pins = [];
  1745. }
  1746. // states common to ALL panes
  1747. s.tagName = $P[0].tagName;
  1748. s.edge = pane; // useful if pane is (or about to be) 'swapped' - easy find out where it is (or is going)
  1749. s.noRoom = false; // true = pane 'automatically' hidden due to insufficient room - will unhide automatically
  1750. s.isVisible = true; // false = pane is invisible - closed OR hidden - simplify logic
  1751. // set css-position to account for container borders & padding
  1752. switch (pane) {
  1753. case "north": CSS.top = sC.insetTop;
  1754. CSS.left = sC.insetLeft;
  1755. CSS.right = sC.insetRight;
  1756. break;
  1757. case "south": CSS.bottom = sC.insetBottom;
  1758. CSS.left = sC.insetLeft;
  1759. CSS.right = sC.insetRight;
  1760. break;
  1761. case "west": CSS.left = sC.insetLeft; // top, bottom & height set by sizeMidPanes()
  1762. break;
  1763. case "east": CSS.right = sC.insetRight; // ditto
  1764. break;
  1765. case "center": // top, left, width & height set by sizeMidPanes()
  1766. }
  1767. if (dir === "horz") // north or south pane
  1768. CSS.height = cssH($P, size);
  1769. else if (dir === "vert") // east or west pane
  1770. CSS.width = cssW($P, size);
  1771. //else if (isCenter) {}
  1772. $P.css(CSS); // apply size -- top, bottom & height will be set by sizeMidPanes
  1773. if (dir != "horz") sizeMidPanes(pane, true); // true = skipCallback
  1774. // close or hide the pane if specified in settings
  1775. if (o.initClosed && o.closable && !o.initHidden)
  1776. close(pane, true, true); // true, true = force, noAnimation
  1777. else if (o.initHidden || o.initClosed)
  1778. hide(pane); // will be completely invisible - no resizer or spacing
  1779. else if (!s.noRoom)
  1780. // make the pane visible - in case was initially hidden
  1781. $P.css("display","block");
  1782. // ELSE setAsOpen() - called later by initHandles()
  1783. // RESET visibility now - pane will appear IF display:block
  1784. $P.css("visibility","visible");
  1785. // check option for auto-handling of pop-ups & drop-downs
  1786. if (o.showOverflowOnHover)
  1787. $P.hover( allowOverflow, resetOverflow );
  1788. // if adding a pane AFTER initialization, then...
  1789. if (state.initialized) {
  1790. initHandles( pane );
  1791. initHotkeys( pane );
  1792. resizeAll(); // will sizeContent if pane is visible
  1793. if (s.isVisible) { // pane is OPEN
  1794. if (o.triggerEventsOnLoad)
  1795. _execCallback(pane, o.onresize_end || o.onresize);
  1796. resizeChildLayout(pane);
  1797. }
  1798. if (o.initChildLayout && o.childOptions)
  1799. createChildLayout(pane);
  1800. }
  1801. }
  1802. /**
  1803. * Initialize module objects, styling, size and position for all resize bars and toggler buttons
  1804. *
  1805. * @see _create()
  1806. * @param {string=} [panes=""] The edge(s) to process
  1807. */
  1808. , initHandles = function (panes) {
  1809. panes = panes ? panes.split(",") : _c.borderPanes;
  1810. // create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV
  1811. $.each(panes, function (i, pane) {
  1812. var $P = $Ps[pane];
  1813. $Rs[pane] = false; // INIT
  1814. $Ts[pane] = false;
  1815. if (!$P) return; // pane does not exist - skip
  1816. var
  1817. o = options[pane]
  1818. , s = state[pane]
  1819. , c = _c[pane]
  1820. , rClass = o.resizerClass
  1821. , tClass = o.togglerClass
  1822. , side = c.side.toLowerCase()
  1823. , spacing = (s.isVisible ? o.spacing_open : o.spacing_closed)
  1824. , _pane = "-"+ pane // used for classNames
  1825. , _state = (s.isVisible ? "-open" : "-closed") // used for classNames
  1826. , I = Instance[pane]
  1827. // INIT RESIZER BAR
  1828. , $R = I.resizer = $Rs[pane] = $("<div></div>")
  1829. // INIT TOGGLER BUTTON
  1830. , $T = I.toggler = (o.closable ? $Ts[pane] = $("<div></div>") : false)
  1831. ;
  1832. //if (s.isVisible && o.resizable) ... handled by initResizable
  1833. if (!s.isVisible && o.slidable)
  1834. $R.attr("title", o.sliderTip).css("cursor", o.sliderCursor);
  1835. $R // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-resizer"
  1836. .attr("id", (o.paneSelector.substr(0,1)=="#" ? o.paneSelector.substr(1) + "-resizer" : ""))
  1837. .data("parentLayout", Instance)
  1838. .data("layoutRole", "resizer")
  1839. .data("layoutEdge", pane)
  1840. .css(_c.resizers.cssReq).css("zIndex", options.zIndexes.resizer_normal)
  1841. .css(o.applyDemoStyles ? _c.resizers.cssDemo : {}) // add demo styles
  1842. .addClass(rClass +" "+ rClass+_pane)
  1843. .hover(addHover, removeHover) // ALWAYS add hover-classes, even if resizing is not enabled - handle with CSS instead
  1844. .hover(onResizerEnter, onResizerLeave) // ALWAYS NEED resizer.mouseleave to balance toggler.mouseenter
  1845. .appendTo($N) // append DIV to container
  1846. ;
  1847. if ($T) {
  1848. $T // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "#paneLeft-toggler"
  1849. .attr("id", (o.paneSelector.substr(0,1)=="#" ? o.paneSelector.substr(1) + "-toggler" : ""))
  1850. .data("parentLayout", Instance)
  1851. .data("layoutRole", "toggler")
  1852. .data("layoutEdge", pane)
  1853. .css(_c.togglers.cssReq) // add base/required styles
  1854. .css(o.applyDemoStyles ? _c.togglers.cssDemo : {}) // add demo styles
  1855. .addClass(tClass +" "+ tClass+_pane)
  1856. .hover(addHover, removeHover) // ALWAYS add hover-classes, even if toggling is not enabled - handle with CSS instead
  1857. .bind("mouseenter", onResizerEnter) // NEED toggler.mouseenter because mouseenter MAY NOT fire on resizer
  1858. .appendTo($R) // append SPAN to resizer DIV
  1859. ;
  1860. // ADD INNER-SPANS TO TOGGLER
  1861. if (o.togglerContent_open) // ui-layout-open
  1862. $("<span>"+ o.togglerContent_open +"</span>")
  1863. .data("layoutRole", "togglerContent")
  1864. .data("layoutEdge", pane)
  1865. .addClass("content content-open")
  1866. .css("display","none")
  1867. .appendTo( $T )
  1868. //.hover( addHover, removeHover ) // use ui-layout-toggler-west-hover .content-open instead!
  1869. ;
  1870. if (o.togglerContent_closed) // ui-layout-closed
  1871. $("<span>"+ o.togglerContent_closed +"</span>")
  1872. .data("layoutRole", "togglerContent")
  1873. .data("layoutEdge", pane)
  1874. .addClass("content content-closed")
  1875. .css("display","none")
  1876. .appendTo( $T )
  1877. //.hover( addHover, removeHover ) // use ui-layout-toggler-west-hover .content-closed instead!
  1878. ;
  1879. // ADD TOGGLER.click/.hover
  1880. enableClosable(pane);
  1881. }
  1882. // add Draggable events
  1883. initResizable(pane);
  1884. // ADD CLASSNAMES & SLIDE-BINDINGS - eg: class="resizer resizer-west resizer-open"
  1885. if (s.isVisible)
  1886. setAsOpen(pane); // onOpen will be called, but NOT onResize
  1887. else {
  1888. setAsClosed(pane); // onClose will be called
  1889. bindStartSlidingEvent(pane, true); // will enable events IF option is set
  1890. }
  1891. });
  1892. // SET ALL HANDLE DIMENSIONS
  1893. sizeHandles();
  1894. }
  1895. /**
  1896. * Initialize scrolling ui-layout-content div - if exists
  1897. *
  1898. * @see initPane() - or externally after an Ajax injection
  1899. * @param {string} [pane] The pane to process
  1900. * @param {boolean=} [resize=true] Size content after init
  1901. */
  1902. , initContent = function (pane, resize) {
  1903. if (!isInitialized()) return;
  1904. var
  1905. o = options[pane]
  1906. , sel = o.contentSelector
  1907. , I = Instance[pane]
  1908. , $P = $Ps[pane]
  1909. , $C
  1910. ;
  1911. if (sel) $C = I.content = $Cs[pane] = (o.findNestedContent)
  1912. ? $P.find(sel).eq(0) // match 1-element only
  1913. : $P.children(sel).eq(0)
  1914. ;
  1915. if ($C && $C.length) {
  1916. $C.data("layoutRole", "content");
  1917. // SAVE original Pane CSS
  1918. if (!$C.data("layoutCSS"))
  1919. $C.data("layoutCSS", elCSS($C, "height"));
  1920. $C.css( _c.content.cssReq );
  1921. if (o.applyDemoStyles) {
  1922. $C.css( _c.content.cssDemo ); // add padding & overflow: auto to content-div
  1923. $P.css( _c.content.cssDemoPane ); // REMOVE padding/scrolling from pane
  1924. }
  1925. state[pane].content = {}; // init content state
  1926. if (resize !== false) sizeContent(pane);
  1927. // sizeContent() is called AFTER init of all elements
  1928. }
  1929. else
  1930. I.content = $Cs[pane] = false;
  1931. }
  1932. /**
  1933. * Add resize-bars to all panes that specify it in options
  1934. * -dependancy: $.fn.resizable - will skip if not found
  1935. *
  1936. * @see _create()
  1937. * @param {string=} [panes=""] The edge(s) to process
  1938. */
  1939. , initResizable = function (panes) {
  1940. var draggingAvailable = $.layout.plugins.draggable
  1941. , side // set in start()
  1942. ;
  1943. panes = panes ? panes.split(",") : _c.borderPanes;
  1944. $.each(panes, function (idx, pane) {
  1945. var o = options[pane];
  1946. if (!draggingAvailable || !$Ps[pane] || !o.resizable) {
  1947. o.resizable = false;
  1948. return true; // skip to next
  1949. }
  1950. var s = state[pane]
  1951. , z = options.zIndexes
  1952. , c = _c[pane]
  1953. , side = c.dir=="horz" ? "top" : "left"
  1954. , opEdge = _c.oppositeEdge[pane]
  1955. , masks = pane +",center,"+ opEdge + (c.dir=="horz" ? ",west,east" : "")
  1956. , $P = $Ps[pane]
  1957. , $R = $Rs[pane]
  1958. , base = o.resizerClass
  1959. , lastPos = 0 // used when live-resizing
  1960. , r, live // set in start because may change
  1961. // 'drag' classes are applied to the ORIGINAL resizer-bar while dragging is in process
  1962. , resizerClass = base+"-drag" // resizer-drag
  1963. , resizerPaneClass = base+"-"+pane+"-drag" // resizer-north-drag
  1964. // 'helper' class is applied to the CLONED resizer-bar while it is being dragged
  1965. , helperClass = base+"-dragging" // resizer-dragging
  1966. , helperPaneClass = base+"-"+pane+"-dragging" // resizer-north-dragging
  1967. , helperLimitClass = base+"-dragging-limit" // resizer-drag
  1968. , helperPaneLimitClass = base+"-"+pane+"-dragging-limit" // resizer-north-drag
  1969. , helperClassesSet = false // logic var
  1970. ;
  1971. if (!s.isClosed)
  1972. $R.attr("title", o.resizerTip)
  1973. .css("cursor", o.resizerCursor); // n-resize, s-resize, etc
  1974. $R.draggable({
  1975. containment: $N[0] // limit resizing to layout container
  1976. , axis: (c.dir=="horz" ? "y" : "x") // limit resizing to horz or vert axis
  1977. , delay: 0
  1978. , distance: 1
  1979. , grid: o.resizingGrid
  1980. // basic format for helper - style it using class: .ui-draggable-dragging
  1981. , helper: "clone"
  1982. , opacity: o.resizerDragOpacity
  1983. , addClasses: false // avoid ui-state-disabled class when disabled
  1984. //, iframeFix: o.draggableIframeFix // TODO: consider using when bug is fixed
  1985. , zIndex: z.resizer_drag
  1986. , start: function (e, ui) {
  1987. // REFRESH options & state pointers in case we used swapPanes
  1988. o = options[pane];
  1989. s = state[pane];
  1990. // re-read options
  1991. live = o.livePaneResizing;
  1992. // ondrag_start callback - will CANCEL hide if returns false
  1993. // TODO: dragging CANNOT be cancelled like this, so see if there is a way?
  1994. if (false === _execCallback(pane, o.ondrag_start)) return false;
  1995. s.isResizing = true; // prevent pane from closing while resizing
  1996. timer.clear(pane+"_closeSlider"); // just in case already triggered
  1997. // SET RESIZER LIMITS - used in drag()
  1998. setSizeLimits(pane); // update pane/resizer state
  1999. r = s.resizerPosition;
  2000. lastPos = ui.position[ side ]
  2001. $R.addClass( resizerClass +" "+ resizerPaneClass ); // add drag classes
  2002. helperClassesSet = false; // reset logic var - see drag()
  2003. // DISABLE TEXT SELECTION (probably already done by resizer.mouseOver)
  2004. $('body').disableSelection();
  2005. // MASK PANES CONTAINING IFRAMES, APPLETS OR OTHER TROUBLESOME ELEMENTS
  2006. showMasks( masks );
  2007. }
  2008. , drag: function (e, ui) {
  2009. if (!helperClassesSet) { // can only add classes after clone has been added to the DOM
  2010. //$(".ui-draggable-dragging")
  2011. ui.helper
  2012. .addClass( helperClass +" "+ helperPaneClass ) // add helper classes
  2013. .css({ right: "auto", bottom: "auto" }) // fix dir="rtl" issue
  2014. .children().css("visibility","hidden") // hide toggler inside dragged resizer-bar
  2015. ;
  2016. helperClassesSet = true;
  2017. // draggable bug!? RE-SET zIndex to prevent E/W resize-bar showing through N/S pane!
  2018. if (s.isSliding) $Ps[pane].css("zIndex", z.pane_sliding);
  2019. }
  2020. // CONTAIN RESIZER-BAR TO RESIZING LIMITS
  2021. var limit = 0;
  2022. if (ui.position[side] < r.min) {
  2023. ui.position[side] = r.min;
  2024. limit = -1;
  2025. }
  2026. else if (ui.position[side] > r.max) {
  2027. ui.position[side] = r.max;
  2028. limit = 1;
  2029. }
  2030. // ADD/REMOVE dragging-limit CLASS
  2031. if (limit) {
  2032. ui.helper.addClass( helperLimitClass +" "+ helperPaneLimitClass ); // at dragging-limit
  2033. window.defaultStatus = (limit>0 && pane.match(/north|west/)) || (limit<0 && pane.match(/south|east/)) ? lang.maxSizeWarning : lang.minSizeWarning;
  2034. }
  2035. else {
  2036. ui.helper.removeClass( helperLimitClass +" "+ helperPaneLimitClass ); // not at dragging-limit
  2037. window.defaultStatus = "";
  2038. }
  2039. // DYNAMICALLY RESIZE PANES IF OPTION ENABLED
  2040. // won't trigger unless resizer has actually moved!
  2041. if (live && Math.abs(ui.position[side] - lastPos) >= o.liveResizingTolerance) {
  2042. lastPos = ui.position[side];
  2043. resizePanes(e, ui, pane)
  2044. }
  2045. }
  2046. , stop: function (e, ui) {
  2047. $('body').enableSelection(); // RE-ENABLE TEXT SELECTION
  2048. window.defaultStatus = ""; // clear 'resizing limit' message from statusbar
  2049. $R.removeClass( resizerClass +" "+ resizerPaneClass ); // remove drag classes from Resizer
  2050. s.isResizing = false;
  2051. resizePanes(e, ui, pane, true, masks); // true = resizingDone
  2052. }
  2053. });
  2054. });
  2055. /**
  2056. * resizePanes
  2057. *
  2058. * Sub-routine called from stop() - and drag() if livePaneResizing
  2059. *
  2060. * @param {!Object} evt
  2061. * @param {!Object} ui
  2062. * @param {string} pane
  2063. * @param {boolean=} [resizingDone=false]
  2064. */
  2065. var resizePanes = function (evt, ui, pane, resizingDone, masks) {
  2066. var dragPos = ui.position
  2067. , c = _c[pane]
  2068. , o = options[pane]
  2069. , s = state[pane]
  2070. , resizerPos
  2071. ;
  2072. switch (pane) {
  2073. case "north": resizerPos = dragPos.top; break;
  2074. case "west": resizerPos = dragPos.left; break;
  2075. case "south": resizerPos = sC.offsetHeight - dragPos.top - o.spacing_open; break;
  2076. case "east": resizerPos = sC.offsetWidth - dragPos.left - o.spacing_open; break;
  2077. };
  2078. // remove container margin from resizer position to get the pane size
  2079. var newSize = resizerPos - sC["inset"+ c.side];
  2080. // Disable OR Resize Mask(s) created in drag.start
  2081. if (!resizingDone) {
  2082. // ensure we meet liveResizingTolerance criteria
  2083. if (Math.abs(newSize - s.size) < o.liveResizingTolerance)
  2084. return; // SKIP resize this time
  2085. // resize the pane
  2086. manualSizePane(pane, newSize, false, true); // true = noAnimation
  2087. sizeMasks(); // resize all visible masks
  2088. }
  2089. else { // resizingDone
  2090. // ondrag_end callback
  2091. if (false !== _execCallback(pane, o.ondrag_end || o.ondrag))
  2092. manualSizePane(pane, newSize, false, true); // true = noAnimation
  2093. hideMasks(); // hide all masks, which include panes with 'content/iframe-masks'
  2094. if (s.isSliding && masks) // RE-SHOW only 'object-masks' so objects won't show through sliding pane
  2095. showMasks( masks, true ); // true = onlyForObjects
  2096. }
  2097. };
  2098. }
  2099. /**
  2100. * sizeMask
  2101. *
  2102. * Needed to overlay a DIV over an IFRAME-pane because mask CANNOT be *inside* the pane
  2103. * Called when mask created, and during livePaneResizing
  2104. */
  2105. , sizeMask = function () {
  2106. var $M = $(this)
  2107. , pane = $M.data("layoutMask") // eg: "west"
  2108. , s = state[pane]
  2109. ;
  2110. // only masks over an IFRAME-pane need manual resizing
  2111. if (s.tagName == "IFRAME" && s.isVisible) // no need to mask closed/hidden panes
  2112. $M.css({
  2113. top: s.offsetTop
  2114. , left: s.offsetLeft
  2115. , width: s.outerWidth
  2116. , height: s.outerHeight
  2117. });
  2118. /* ALT Method...
  2119. var $P = $Ps[pane];
  2120. $M.css( $P.position() ).css({ width: $P[0].offsetWidth, height: $P[0].offsetHeight });
  2121. */
  2122. }
  2123. , sizeMasks = function () {
  2124. $Ms.each( sizeMask ); // resize all 'visible' masks
  2125. }
  2126. , showMasks = function (panes, onlyForObjects) {
  2127. var a = panes ? panes.split(",") : $.layout.config.allPanes
  2128. , z = options.zIndexes
  2129. , o, s;
  2130. $.each(a, function(i,p){
  2131. s = state[p];
  2132. o = options[p];
  2133. if (s.isVisible && ( (!onlyForObjects && o.maskContents) || o.maskObjects )) {
  2134. getMasks(p).each(function(){
  2135. sizeMask.apply(this);
  2136. this.style.zIndex = s.isSliding ? z.pane_sliding+1 : z.pane_normal+1
  2137. this.style.display = "block";
  2138. });
  2139. }
  2140. });
  2141. }
  2142. , hideMasks = function () {
  2143. // ensure no pane is resizing - could be a timing issue
  2144. var skip;
  2145. $.each( $.layout.config.borderPanes, function(i,p){
  2146. if (state[p].isResizing) {
  2147. skip = true;
  2148. return false; // BREAK
  2149. }
  2150. });
  2151. if (!skip)
  2152. $Ms.hide(); // hide ALL masks
  2153. }
  2154. , getMasks = function (pane) {
  2155. var $Masks = $([])
  2156. , $M, i = 0, c = $Ms.length
  2157. ;
  2158. for (; i<c; i++) {
  2159. $M = $Ms.eq(i);
  2160. if ($M.data("layoutMask") === pane)
  2161. $Masks = $Masks.add( $M );
  2162. }
  2163. if ($Masks.length)
  2164. return $Masks;
  2165. else
  2166. return createMasks(pane);
  2167. }
  2168. /**
  2169. * createMasks
  2170. *
  2171. * Generates both DIV (ALWAYS used) and IFRAME (optional) elements as masks
  2172. * An IFRAME mask is created *under* the DIV when maskObjects=true, because a DIV cannot mask an applet
  2173. */
  2174. , createMasks = function (pane) {
  2175. var
  2176. $P = $Ps[pane]
  2177. , s = state[pane]
  2178. , o = options[pane]
  2179. , z = options.zIndexes
  2180. //, objMask = o.maskObjects && s.tagName != "IFRAME" // check for option
  2181. , $Masks = $([])
  2182. , isIframe, el, $M, css, i
  2183. ;
  2184. if (!o.maskContents && !o.maskObjects) return $Masks;
  2185. // if o.maskObjects=true, then loop TWICE to create BOTH kinds of mask, else only create a DIV
  2186. for (i=0; i < (o.maskObjects ? 2 : 1); i++) {
  2187. isIframe = o.maskObjects && i==0;
  2188. el = document.createElement( isIframe ? "iframe" : "div" );
  2189. $M = $(el).data("layoutMask", pane); // add data to relate mask to pane
  2190. el.className = "ui-layout-mask ui-layout-mask-"+ pane; // for user styling
  2191. css = el.style;
  2192. // styles common to both DIVs and IFRAMES
  2193. css.display = "block";
  2194. css.position = "absolute";
  2195. if (isIframe) { // IFRAME-only props
  2196. el.frameborder = 0;
  2197. el.src = "about:blank";
  2198. css.opacity = 0;
  2199. css.filter = "Alpha(Opacity='0')";
  2200. css.border = 0;
  2201. }
  2202. // if pane is an IFRAME, then must mask the pane itself
  2203. if (s.tagName == "IFRAME") {
  2204. // NOTE sizing done by a subroutine so can be called during live-resizing
  2205. css.zIndex = z.pane_normal+1; // 1-higher than pane
  2206. $N.append( el ); // append to LAYOUT CONTAINER
  2207. }
  2208. // otherwise put masks *inside the pane* to mask its contents
  2209. else {
  2210. $M.addClass("ui-layout-mask-inside-pane");
  2211. css.zIndex = o.maskZindex || z.content_mask; // usually 1, but customizable
  2212. css.top = 0;
  2213. css.left = 0;
  2214. css.width = "100%";
  2215. css.height = "100%";
  2216. $P.append( el ); // append INSIDE pane element
  2217. }
  2218. // add to return object
  2219. $Masks = $Masks.add( el );
  2220. // add Mask to cached array so can be resized & reused
  2221. $Ms = $Ms.add( el );
  2222. }
  2223. return $Masks;
  2224. }
  2225. /**
  2226. * Destroy this layout and reset all elements
  2227. *
  2228. * @param {boolean=} [destroyChildren=false] Destory Child-Layouts first?
  2229. */
  2230. , destroy = function (destroyChildren) {
  2231. // UNBIND layout events and remove global object
  2232. $(window).unbind("."+ sID); // resize & unload
  2233. $(document).unbind("."+ sID); // keyDown (hotkeys)
  2234. // reset layout-container
  2235. $N .clearQueue()
  2236. .removeData("layout")
  2237. .removeData("layoutContainer")
  2238. .removeClass(options.containerClass)
  2239. ;
  2240. // remove all mask elements that have been created
  2241. $Ms.remove();
  2242. // loop all panes to remove layout classes, attributes and bindings
  2243. $.each(_c.allPanes, function (i, pane) {
  2244. removePane( pane, false, true, destroyChildren ); // true = skipResize
  2245. });
  2246. // do NOT reset container CSS if is a 'pane' (or 'content') in an outer-layout - ie, THIS layout is 'nested'
  2247. var css = "layoutCSS";
  2248. if ($N.data(css) && !$N.data("layoutRole")) // RESET CSS
  2249. $N.css( $N.data(css) ).removeData(css);
  2250. // for full-page layouts, also reset the <HTML> CSS
  2251. if (sC.tagName === "BODY" && ($N = $("html")).data(css)) // RESET <HTML> CSS
  2252. $N.css( $N.data(css) ).removeData(css);
  2253. // trigger plugins for this layout, if there are any
  2254. runPluginCallbacks( Instance, $.layout.onDestroy );
  2255. // trigger state-management and onunload callback
  2256. unload();
  2257. // clear the Instance of everything except for container & options (so could recreate)
  2258. // RE-CREATE: myLayout = myLayout.container.layout( myLayout.options );
  2259. for (n in Instance)
  2260. if (!n.match(/^(container|options)$/)) delete Instance[ n ];
  2261. // add a 'destroyed' flag to make it easy to check
  2262. Instance.destroyed = true;
  2263. }
  2264. /**
  2265. * Remove a pane from the layout - subroutine of destroy()
  2266. *
  2267. * @see destroy()
  2268. * @param {string} pane The pane to process
  2269. * @param {boolean=} [remove=false] Remove the DOM element?
  2270. * @param {boolean=} [skipResize=false] Skip calling resizeAll()?
  2271. */
  2272. , removePane = function (pane, remove, skipResize, destroyChild) {
  2273. if (!isInitialized()) return;
  2274. var $P = $Ps[pane]
  2275. , $C = $Cs[pane]
  2276. , $R = $Rs[pane]
  2277. , $T = $Ts[pane]
  2278. ;
  2279. // NOTE: elements can still exist even after remove()
  2280. // so check for missing data(), which is cleared by removed()
  2281. if ($P && $.isEmptyObject( $P.data() )) $P = false;
  2282. if ($C && $.isEmptyObject( $C.data() )) $C = false;
  2283. if ($R && $.isEmptyObject( $R.data() )) $R = false;
  2284. if ($T && $.isEmptyObject( $T.data() )) $T = false;
  2285. if ($P) $P.stop(true, true);
  2286. // check for a child layout
  2287. var o = options[pane]
  2288. , s = state[pane]
  2289. , d = "layout"
  2290. , css = "layoutCSS"
  2291. , Child = children[pane] || ($P ? $P.data(d) : 0) || ($C ? $C.data(d) : 0) || null
  2292. , destroy = destroyChild !== undefined ? destroyChild : o.destroyChildLayout
  2293. ;
  2294. // FIRST destroy the child-layout(s)
  2295. if (destroy && Child && !Child.destroyed) {
  2296. Child.destroy(true); // tell child-layout to destroy ALL its child-layouts too
  2297. if (Child.destroyed) // destroy was successful
  2298. Child = null; // clear pointer for logic below
  2299. }
  2300. if ($P && remove && !Child)
  2301. $P.remove();
  2302. else if ($P) {
  2303. // create list of ALL pane-classes that need to be removed
  2304. var root = o.paneClass // default="ui-layout-pane"
  2305. , pRoot = root +"-"+ pane // eg: "ui-layout-pane-west"
  2306. , _open = "-open"
  2307. , _sliding= "-sliding"
  2308. , _closed = "-closed"
  2309. , classes = [ root, root+_open, root+_closed, root+_sliding, // generic classes
  2310. pRoot, pRoot+_open, pRoot+_closed, pRoot+_sliding ] // pane-specific classes
  2311. ;
  2312. $.merge(classes, getHoverClasses($P, true)); // ADD hover-classes
  2313. // remove all Layout classes from pane-element
  2314. $P .removeClass( classes.join(" ") ) // remove ALL pane-classes
  2315. .removeData("layoutParent")
  2316. .removeData("layoutRole")
  2317. .removeData("layoutEdge")
  2318. .removeData("autoHidden") // in case set
  2319. .unbind("."+ sID) // remove ALL Layout events
  2320. // TODO: remove these extra unbind commands when jQuery is fixed
  2321. //.unbind("mouseenter"+ sID)
  2322. //.unbind("mouseleave"+ sID)
  2323. ;
  2324. // do NOT reset CSS if this pane/content is STILL the container of a nested layout!
  2325. // the nested layout will reset its 'container' CSS when/if it is destroyed
  2326. if ($C && $C.data(d)) {
  2327. // a content-div may not have a specific width, so give it one to contain the Layout
  2328. $C.width( $C.width() );
  2329. Child.resizeAll(); // now resize the Layout
  2330. }
  2331. else if ($C)
  2332. $C.css( $C.data(css) ).removeData(css).removeData("layoutRole");
  2333. // remove pane AFTER content in case there was a nested layout
  2334. if (!$P.data(d))
  2335. $P.css( $P.data(css) ).removeData(css);
  2336. }
  2337. // REMOVE pane resizer and toggler elements
  2338. if ($T) $T.remove();
  2339. if ($R) $R.remove();
  2340. // CLEAR all pointers and state data
  2341. Instance[pane] = $Ps[pane] = $Cs[pane] = $Rs[pane] = $Ts[pane] = children[pane] = false;
  2342. s = { removed: true };
  2343. if (!skipResize)
  2344. resizeAll();
  2345. }
  2346. /*
  2347. * ###########################
  2348. * ACTION METHODS
  2349. * ###########################
  2350. */
  2351. /**
  2352. * Completely 'hides' a pane, including its spacing - as if it does not exist
  2353. * The pane is not actually 'removed' from the source, so can use 'show' to un-hide it
  2354. *
  2355. * @param {string} pane The pane being hidden, ie: north, south, east, or west
  2356. * @param {boolean=} [noAnimation=false]
  2357. */
  2358. , hide = function (pane, noAnimation) {
  2359. if (!isInitialized()) return;
  2360. var
  2361. o = options[pane]
  2362. , s = state[pane]
  2363. , $P = $Ps[pane]
  2364. , $R = $Rs[pane]
  2365. ;
  2366. if (!$P || s.isHidden) return; // pane does not exist OR is already hidden
  2367. // onhide_start callback - will CANCEL hide if returns false
  2368. if (state.initialized && false === _execCallback(pane, o.onhide_start)) return;
  2369. s.isSliding = false; // just in case
  2370. // now hide the elements
  2371. if ($R) $R.hide(); // hide resizer-bar
  2372. if (!state.initialized || s.isClosed) {
  2373. s.isClosed = true; // to trigger open-animation on show()
  2374. s.isHidden = true;
  2375. s.isVisible = false;
  2376. $P.hide(); // no animation when loading page
  2377. sizeMidPanes(_c[pane].dir === "horz" ? "" : "center");
  2378. if (state.initialized || o.triggerEventsOnLoad)
  2379. _execCallback(pane, o.onhide_end || o.onhide);
  2380. }
  2381. else {
  2382. s.isHiding = true; // used by onclose
  2383. close(pane, false, noAnimation); // adjust all panes to fit
  2384. }
  2385. }
  2386. /**
  2387. * Show a hidden pane - show as 'closed' by default unless openPane = true
  2388. *
  2389. * @param {string} pane The pane being opened, ie: north, south, east, or west
  2390. * @param {boolean=} [openPane=false]
  2391. * @param {boolean=} [noAnimation=false]
  2392. * @param {boolean=} [noAlert=false]
  2393. */
  2394. , show = function (pane, openPane, noAnimation, noAlert) {
  2395. if (!isInitialized()) return;
  2396. var
  2397. o = options[pane]
  2398. , s = state[pane]
  2399. , $P = $Ps[pane]
  2400. , $R = $Rs[pane]
  2401. ;
  2402. if (!$P || !s.isHidden) return; // pane does not exist OR is not hidden
  2403. // onshow_start callback - will CANCEL show if returns false
  2404. if (false === _execCallback(pane, o.onshow_start)) return;
  2405. s.isSliding = false; // just in case
  2406. s.isShowing = true; // used by onopen/onclose
  2407. //s.isHidden = false; - will be set by open/close - if not cancelled
  2408. // now show the elements
  2409. //if ($R) $R.show(); - will be shown by open/close
  2410. if (openPane === false)
  2411. close(pane, true); // true = force
  2412. else
  2413. open(pane, false, noAnimation, noAlert); // adjust all panes to fit
  2414. }
  2415. /**
  2416. * Toggles a pane open/closed by calling either open or close
  2417. *
  2418. * @param {string} pane The pane being toggled, ie: north, south, east, or west
  2419. * @param {boolean=} [slide=false]
  2420. */
  2421. , toggle = function (pane, slide) {
  2422. if (!isInitialized()) return;
  2423. if (!isStr(pane)) {
  2424. pane.stopImmediatePropagation(); // pane = event
  2425. pane = $(this).data("layoutEdge"); // bound to $R.dblclick
  2426. }
  2427. var s = state[pane];
  2428. if (s.isHidden)
  2429. show(pane); // will call 'open' after unhiding it
  2430. else if (s.isClosed)
  2431. open(pane, !!slide);
  2432. else
  2433. close(pane);
  2434. }
  2435. /**
  2436. * Utility method used during init or other auto-processes
  2437. *
  2438. * @param {string} pane The pane being closed
  2439. * @param {boolean=} [setHandles=false]
  2440. */
  2441. , _closePane = function (pane, setHandles) {
  2442. var
  2443. $P = $Ps[pane]
  2444. , s = state[pane]
  2445. ;
  2446. $P.hide();
  2447. s.isClosed = true;
  2448. s.isVisible = false;
  2449. // UNUSED: if (setHandles) setAsClosed(pane, true); // true = force
  2450. }
  2451. /**
  2452. * Close the specified pane (animation optional), and resize all other panes as needed
  2453. *
  2454. * @param {string} pane The pane being closed, ie: north, south, east, or west
  2455. * @param {boolean=} [force=false]
  2456. * @param {boolean=} [noAnimation=false]
  2457. * @param {boolean=} [skipCallback=false]
  2458. */
  2459. , close = function (pane, force, noAnimation, skipCallback) {
  2460. if (!state.initialized && $Ps[pane]) {
  2461. _closePane(pane); // INIT pane as closed
  2462. return;
  2463. }
  2464. if (!isInitialized()) return;
  2465. var
  2466. $P = $Ps[pane]
  2467. , $R = $Rs[pane]
  2468. , $T = $Ts[pane]
  2469. , o = options[pane]
  2470. , s = state[pane]
  2471. , c = _c[pane]
  2472. , doFX, isShowing, isHiding, wasSliding;
  2473. // QUEUE in case another action/animation is in progress
  2474. $N.queue(function( queueNext ){
  2475. if ( !$P
  2476. || (!o.closable && !s.isShowing && !s.isHiding) // invalid request // (!o.resizable && !o.closable) ???
  2477. || (!force && s.isClosed && !s.isShowing) // already closed
  2478. ) return queueNext();
  2479. // onclose_start callback - will CANCEL hide if returns false
  2480. // SKIP if just 'showing' a hidden pane as 'closed'
  2481. var abort = !s.isShowing && false === _execCallback(pane, o.onclose_start);
  2482. // transfer logic vars to temp vars
  2483. isShowing = s.isShowing;
  2484. isHiding = s.isHiding;
  2485. wasSliding = s.isSliding;
  2486. // now clear the logic vars
  2487. delete s.isShowing;
  2488. delete s.isHiding;
  2489. if (abort) return queueNext();
  2490. doFX = !noAnimation && !s.isClosed && (o.fxName_close != "none");
  2491. s.isMoving = true;
  2492. s.isClosed = true;
  2493. s.isVisible = false;
  2494. // update isHidden BEFORE sizing panes
  2495. if (isHiding) s.isHidden = true;
  2496. else if (isShowing) s.isHidden = false;
  2497. if (s.isSliding) // pane is being closed, so UNBIND trigger events
  2498. bindStopSlidingEvents(pane, false); // will set isSliding=false
  2499. else // resize panes adjacent to this one
  2500. sizeMidPanes(_c[pane].dir === "horz" ? "" : "center", false); // false = NOT skipCallback
  2501. // if this pane has a resizer bar, move it NOW - before animation
  2502. setAsClosed(pane);
  2503. // CLOSE THE PANE
  2504. if (doFX) { // animate the close
  2505. // mask panes with objects
  2506. var masks = "center"+ (c.dir=="horz" ? ",west,east" : "");
  2507. showMasks( masks, true ); // true = ONLY mask panes with maskObjects=true
  2508. lockPaneForFX(pane, true); // need to set left/top so animation will work
  2509. $P.hide( o.fxName_close, o.fxSettings_close, o.fxSpeed_close, function () {
  2510. lockPaneForFX(pane, false); // undo
  2511. if (s.isClosed) close_2();
  2512. queueNext();
  2513. });
  2514. }
  2515. else { // hide the pane without animation
  2516. $P.hide();
  2517. close_2();
  2518. queueNext();
  2519. };
  2520. });
  2521. // SUBROUTINE
  2522. function close_2 () {
  2523. s.isMoving = false;
  2524. bindStartSlidingEvent(pane, true); // will enable if o.slidable = true
  2525. // if opposite-pane was autoClosed, see if it can be autoOpened now
  2526. var altPane = _c.oppositeEdge[pane];
  2527. if (state[ altPane ].noRoom) {
  2528. setSizeLimits( altPane );
  2529. makePaneFit( altPane );
  2530. }
  2531. // hide any masks shown while closing
  2532. hideMasks();
  2533. if (!skipCallback && (state.initialized || o.triggerEventsOnLoad)) {
  2534. // onclose callback - UNLESS just 'showing' a hidden pane as 'closed'
  2535. if (!isShowing) _execCallback(pane, o.onclose_end || o.onclose);
  2536. // onhide OR onshow callback
  2537. if (isShowing) _execCallback(pane, o.onshow_end || o.onshow);
  2538. if (isHiding) _execCallback(pane, o.onhide_end || o.onhide);
  2539. }
  2540. }
  2541. }
  2542. /**
  2543. * @param {string} pane The pane just closed, ie: north, south, east, or west
  2544. */
  2545. , setAsClosed = function (pane) {
  2546. var
  2547. $P = $Ps[pane]
  2548. , $R = $Rs[pane]
  2549. , $T = $Ts[pane]
  2550. , o = options[pane]
  2551. , s = state[pane]
  2552. , side = _c[pane].side.toLowerCase()
  2553. , inset = "inset"+ _c[pane].side
  2554. , rClass = o.resizerClass
  2555. , tClass = o.togglerClass
  2556. , _pane = "-"+ pane // used for classNames
  2557. , _open = "-open"
  2558. , _sliding= "-sliding"
  2559. , _closed = "-closed"
  2560. ;
  2561. $R
  2562. .css(side, sC[inset]) // move the resizer
  2563. .removeClass( rClass+_open +" "+ rClass+_pane+_open )
  2564. .removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
  2565. .addClass( rClass+_closed +" "+ rClass+_pane+_closed )
  2566. .unbind("dblclick."+ sID)
  2567. ;
  2568. // DISABLE 'resizing' when closed - do this BEFORE bindStartSlidingEvent?
  2569. if (o.resizable && $.layout.plugins.draggable)
  2570. $R
  2571. .draggable("disable")
  2572. .removeClass("ui-state-disabled") // do NOT apply disabled styling - not suitable here
  2573. .css("cursor", "default")
  2574. .attr("title","")
  2575. ;
  2576. // if pane has a toggler button, adjust that too
  2577. if ($T) {
  2578. $T
  2579. .removeClass( tClass+_open +" "+ tClass+_pane+_open )
  2580. .addClass( tClass+_closed +" "+ tClass+_pane+_closed )
  2581. .attr("title", o.togglerTip_closed) // may be blank
  2582. ;
  2583. // toggler-content - if exists
  2584. $T.children(".content-open").hide();
  2585. $T.children(".content-closed").css("display","block");
  2586. }
  2587. // sync any 'pin buttons'
  2588. syncPinBtns(pane, false);
  2589. if (state.initialized) {
  2590. // resize 'length' and position togglers for adjacent panes
  2591. sizeHandles();
  2592. }
  2593. }
  2594. /**
  2595. * Open the specified pane (animation optional), and resize all other panes as needed
  2596. *
  2597. * @param {string} pane The pane being opened, ie: north, south, east, or west
  2598. * @param {boolean=} [slide=false]
  2599. * @param {boolean=} [noAnimation=false]
  2600. * @param {boolean=} [noAlert=false]
  2601. */
  2602. , open = function (pane, slide, noAnimation, noAlert) {
  2603. if (!isInitialized()) return;
  2604. var
  2605. $P = $Ps[pane]
  2606. , $R = $Rs[pane]
  2607. , $T = $Ts[pane]
  2608. , o = options[pane]
  2609. , s = state[pane]
  2610. , c = _c[pane]
  2611. , doFX, isShowing;
  2612. // QUEUE in case another action/animation is in progress
  2613. $N.queue(function( queueNext ){
  2614. if ( !$P
  2615. || (!o.resizable && !o.closable && !s.isShowing) // invalid request
  2616. || (s.isVisible && !s.isSliding) // already open
  2617. ) return queueNext();
  2618. // pane can ALSO be unhidden by just calling show(), so handle this scenario
  2619. if (s.isHidden && !s.isShowing) {
  2620. show(pane, true);
  2621. return;
  2622. }
  2623. if (o.autoResize && s.size != o.size) // resize pane to original size set in options
  2624. sizePane(pane, o.size, true, true, true); // true=skipCallback/forceResize/noAnimation
  2625. else
  2626. setSizeLimits(pane, slide);
  2627. //setSizeLimits(pane, slide); // update pane-state
  2628. // onopen_start callback - will CANCEL hide if returns false
  2629. if (false === _execCallback(pane, o.onopen_start)) return;
  2630. // make sure there is enough space available to open the pane
  2631. setSizeLimits(pane, slide); // update pane-state
  2632. if (s.minSize > s.maxSize) { // INSUFFICIENT ROOM FOR PANE TO OPEN!
  2633. syncPinBtns(pane, false); // make sure pin-buttons are reset
  2634. if (!noAlert && o.noRoomToOpenTip)
  2635. alert(o.noRoomToOpenTip);
  2636. return queueNext(); // ABORT
  2637. }
  2638. if (slide) // START Sliding - will set isSliding=true
  2639. bindStopSlidingEvents(pane, true); // BIND trigger events to close sliding-pane
  2640. else if (s.isSliding) // PIN PANE (stop sliding) - open pane 'normally' instead
  2641. bindStopSlidingEvents(pane, false); // UNBIND trigger events - will set isSliding=false
  2642. else if (o.slidable)
  2643. bindStartSlidingEvent(pane, false); // UNBIND trigger events
  2644. s.noRoom = false; // will be reset by makePaneFit if 'noRoom'
  2645. makePaneFit(pane);
  2646. // transfer logic var to temp var
  2647. isShowing = s.isShowing;
  2648. // now clear the logic var
  2649. delete s.isShowing;
  2650. doFX = !noAnimation && s.isClosed && (o.fxName_open != "none");
  2651. s.isMoving = true;
  2652. s.isVisible = true;
  2653. s.isClosed = false;
  2654. // update isHidden BEFORE sizing panes - WHY??? Old?
  2655. if (isShowing) s.isHidden = false;
  2656. if (doFX) { // ANIMATE
  2657. // mask panes with objects
  2658. var masks = "center"+ (c.dir=="horz" ? ",west,east" : "");
  2659. if (s.isSliding) masks += ","+ _c.oppositeEdge[pane];
  2660. showMasks( masks, true ); // true = ONLY mask panes with maskObjects=true
  2661. lockPaneForFX(pane, true); // need to set left/top so animation will work
  2662. $P.show( o.fxName_open, o.fxSettings_open, o.fxSpeed_open, function() {
  2663. lockPaneForFX(pane, false); // undo
  2664. if (s.isVisible) open_2(); // continue
  2665. queueNext();
  2666. });
  2667. }
  2668. else {// no animation
  2669. $P.show(); // just show pane and...
  2670. open_2(); // continue
  2671. queueNext();
  2672. };
  2673. });
  2674. // SUBROUTINE
  2675. function open_2 () {
  2676. s.isMoving = false;
  2677. // cure iframe display issues
  2678. _fixIframe(pane);
  2679. // NOTE: if isSliding, then other panes are NOT 'resized'
  2680. if (!s.isSliding) { // resize all panes adjacent to this one
  2681. hideMasks(); // remove any masks shown while opening
  2682. sizeMidPanes(_c[pane].dir=="vert" ? "center" : "", false); // false = NOT skipCallback
  2683. }
  2684. // set classes, position handles and execute callbacks...
  2685. setAsOpen(pane);
  2686. };
  2687. }
  2688. /**
  2689. * @param {string} pane The pane just opened, ie: north, south, east, or west
  2690. * @param {boolean=} [skipCallback=false]
  2691. */
  2692. , setAsOpen = function (pane, skipCallback) {
  2693. var
  2694. $P = $Ps[pane]
  2695. , $R = $Rs[pane]
  2696. , $T = $Ts[pane]
  2697. , o = options[pane]
  2698. , s = state[pane]
  2699. , side = _c[pane].side.toLowerCase()
  2700. , inset = "inset"+ _c[pane].side
  2701. , rClass = o.resizerClass
  2702. , tClass = o.togglerClass
  2703. , _pane = "-"+ pane // used for classNames
  2704. , _open = "-open"
  2705. , _closed = "-closed"
  2706. , _sliding= "-sliding"
  2707. ;
  2708. $R
  2709. .css(side, sC[inset] + getPaneSize(pane)) // move the resizer
  2710. .removeClass( rClass+_closed +" "+ rClass+_pane+_closed )
  2711. .addClass( rClass+_open +" "+ rClass+_pane+_open )
  2712. ;
  2713. if (s.isSliding)
  2714. $R.addClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
  2715. else // in case 'was sliding'
  2716. $R.removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
  2717. if (o.resizerDblClickToggle)
  2718. $R.bind("dblclick", toggle );
  2719. removeHover( 0, $R ); // remove hover classes
  2720. if (o.resizable && $.layout.plugins.draggable)
  2721. $R .draggable("enable")
  2722. .css("cursor", o.resizerCursor)
  2723. .attr("title", o.resizerTip);
  2724. else if (!s.isSliding)
  2725. $R.css("cursor", "default"); // n-resize, s-resize, etc
  2726. // if pane also has a toggler button, adjust that too
  2727. if ($T) {
  2728. $T .removeClass( tClass+_closed +" "+ tClass+_pane+_closed )
  2729. .addClass( tClass+_open +" "+ tClass+_pane+_open )
  2730. .attr("title", o.togglerTip_open); // may be blank
  2731. removeHover( 0, $T ); // remove hover classes
  2732. // toggler-content - if exists
  2733. $T.children(".content-closed").hide();
  2734. $T.children(".content-open").css("display","block");
  2735. }
  2736. // sync any 'pin buttons'
  2737. syncPinBtns(pane, !s.isSliding);
  2738. // update pane-state dimensions - BEFORE resizing content
  2739. $.extend(s, elDims($P));
  2740. if (state.initialized) {
  2741. // resize resizer & toggler sizes for all panes
  2742. sizeHandles();
  2743. // resize content every time pane opens - to be sure
  2744. sizeContent(pane, true); // true = remeasure headers/footers, even if 'pane.isMoving'
  2745. }
  2746. if (!skipCallback && (state.initialized || o.triggerEventsOnLoad) && $P.is(":visible")) {
  2747. // onopen callback
  2748. _execCallback(pane, o.onopen_end || o.onopen);
  2749. // onshow callback - TODO: should this be here?
  2750. if (s.isShowing) _execCallback(pane, o.onshow_end || o.onshow);
  2751. // ALSO call onresize because layout-size *may* have changed while pane was closed
  2752. if (state.initialized) {
  2753. _execCallback(pane, o.onresize_end || o.onresize);
  2754. resizeChildLayout(pane);
  2755. }
  2756. }
  2757. }
  2758. /**
  2759. * slideOpen / slideClose / slideToggle
  2760. *
  2761. * Pass-though methods for sliding
  2762. */
  2763. , slideOpen = function (evt_or_pane) {
  2764. if (!isInitialized()) return;
  2765. var
  2766. evt = isStr(evt_or_pane) ? null : evt_or_pane
  2767. , pane = evt ? $(this).data("layoutEdge") : evt_or_pane
  2768. , s = state[pane]
  2769. , delay = options[pane].slideDelay_open
  2770. ;
  2771. // prevent event from triggering on NEW resizer binding created below
  2772. if (evt) evt.stopImmediatePropagation();
  2773. if (s.isClosed && evt && evt.type === "mouseenter" && delay > 0)
  2774. // trigger = mouseenter - use a delay
  2775. timer.set(pane+"_openSlider", open_NOW, delay);
  2776. else
  2777. open_NOW(); // will unbind events if is already open
  2778. /**
  2779. * SUBROUTINE for timed open
  2780. */
  2781. function open_NOW () {
  2782. if (!s.isClosed) // skip if no longer closed!
  2783. bindStopSlidingEvents(pane, true); // BIND trigger events to close sliding-pane
  2784. else if (!s.isMoving)
  2785. open(pane, true); // true = slide - open() will handle binding
  2786. };
  2787. }
  2788. , slideClose = function (evt_or_pane) {
  2789. if (!isInitialized()) return;
  2790. var
  2791. evt = isStr(evt_or_pane) ? null : evt_or_pane
  2792. , pane = evt ? $(this).data("layoutEdge") : evt_or_pane
  2793. , o = options[pane]
  2794. , s = state[pane]
  2795. , delay = s.isMoving ? 1000 : 300 // MINIMUM delay - option may override
  2796. ;
  2797. if (s.isClosed || s.isResizing)
  2798. return; // skip if already closed OR in process of resizing
  2799. else if (o.slideTrigger_close === "click")
  2800. close_NOW(); // close immediately onClick
  2801. else if (o.preventQuickSlideClose && s.isMoving)
  2802. return; // handle Chrome quick-close on slide-open
  2803. else if (o.preventPrematureSlideClose && evt && $.layout.isMouseOverElem(evt, $Ps[pane]))
  2804. return; // handle incorrect mouseleave trigger, like when over a SELECT-list in IE
  2805. else if (evt) // trigger = mouseleave - use a delay
  2806. // 1 sec delay if 'opening', else .3 sec
  2807. timer.set(pane+"_closeSlider", close_NOW, max(o.slideDelay_close, delay));
  2808. else // called programically
  2809. close_NOW();
  2810. /**
  2811. * SUBROUTINE for timed close
  2812. */
  2813. function close_NOW () {
  2814. if (s.isClosed) // skip 'close' if already closed!
  2815. bindStopSlidingEvents(pane, false); // UNBIND trigger events - TODO: is this needed here?
  2816. else if (!s.isMoving)
  2817. close(pane); // close will handle unbinding
  2818. };
  2819. }
  2820. /**
  2821. * @param {string} pane The pane being opened, ie: north, south, east, or west
  2822. */
  2823. , slideToggle = function (pane) { toggle(pane, true); }
  2824. /**
  2825. * Must set left/top on East/South panes so animation will work properly
  2826. *
  2827. * @param {string} pane The pane to lock, 'east' or 'south' - any other is ignored!
  2828. * @param {boolean} doLock true = set left/top, false = remove
  2829. */
  2830. , lockPaneForFX = function (pane, doLock) {
  2831. var $P = $Ps[pane]
  2832. , s = state[pane]
  2833. , o = options[pane]
  2834. , z = options.zIndexes
  2835. ;
  2836. if (doLock) {
  2837. $P.css({ zIndex: z.pane_animate }); // overlay all elements during animation
  2838. if (pane=="south")
  2839. $P.css({ top: sC.insetTop + sC.innerHeight - $P.outerHeight() });
  2840. else if (pane=="east")
  2841. $P.css({ left: sC.insetLeft + sC.innerWidth - $P.outerWidth() });
  2842. }
  2843. else { // animation DONE - RESET CSS
  2844. // TODO: see if this can be deleted. It causes a quick-close when sliding in Chrome
  2845. $P.css({ zIndex: (s.isSliding ? z.pane_sliding : z.pane_normal) });
  2846. if (pane=="south")
  2847. $P.css({ top: "auto" });
  2848. else if (pane=="east")
  2849. $P.css({ left: "auto" });
  2850. // fix anti-aliasing in IE - only needed for animations that change opacity
  2851. if (browser.msie && o.fxOpacityFix && o.fxName_open != "slide" && $P.css("filter") && $P.css("opacity") == 1)
  2852. $P[0].style.removeAttribute('filter');
  2853. }
  2854. }
  2855. /**
  2856. * Toggle sliding functionality of a specific pane on/off by adding removing 'slide open' trigger
  2857. *
  2858. * @see open(), close()
  2859. * @param {string} pane The pane to enable/disable, 'north', 'south', etc.
  2860. * @param {boolean} enable Enable or Disable sliding?
  2861. */
  2862. , bindStartSlidingEvent = function (pane, enable) {
  2863. var o = options[pane]
  2864. , $P = $Ps[pane]
  2865. , $R = $Rs[pane]
  2866. , trigger = o.slideTrigger_open.toLowerCase()
  2867. ;
  2868. if (!$R || (enable && !o.slidable)) return;
  2869. // make sure we have a valid event
  2870. if (trigger.match(/mouseover/))
  2871. trigger = o.slideTrigger_open = "mouseenter";
  2872. else if (!trigger.match(/click|dblclick|mouseenter/))
  2873. trigger = o.slideTrigger_open = "click";
  2874. $R
  2875. // add or remove trigger event
  2876. [enable ? "bind" : "unbind"](trigger +'.'+ sID, slideOpen)
  2877. // set the appropriate cursor & title/tip
  2878. .css("cursor", enable ? o.sliderCursor : "default")
  2879. .attr("title", enable ? o.sliderTip : "")
  2880. ;
  2881. }
  2882. /**
  2883. * Add or remove 'mouseleave' events to 'slide close' when pane is 'sliding' open or closed
  2884. * Also increases zIndex when pane is sliding open
  2885. * See bindStartSlidingEvent for code to control 'slide open'
  2886. *
  2887. * @see slideOpen(), slideClose()
  2888. * @param {string} pane The pane to process, 'north', 'south', etc.
  2889. * @param {boolean} enable Enable or Disable events?
  2890. */
  2891. , bindStopSlidingEvents = function (pane, enable) {
  2892. var o = options[pane]
  2893. , s = state[pane]
  2894. , c = _c[pane]
  2895. , z = options.zIndexes
  2896. , trigger = o.slideTrigger_close.toLowerCase()
  2897. , action = (enable ? "bind" : "unbind")
  2898. , $P = $Ps[pane]
  2899. , $R = $Rs[pane]
  2900. ;
  2901. s.isSliding = enable; // logic
  2902. timer.clear(pane+"_closeSlider"); // just in case
  2903. // remove 'slideOpen' trigger event from resizer
  2904. // ALSO will raise the zIndex of the pane & resizer
  2905. if (enable) bindStartSlidingEvent(pane, false);
  2906. // RE/SET zIndex - increases when pane is sliding-open, resets to normal when not
  2907. $P.css("zIndex", enable ? z.pane_sliding : z.pane_normal);
  2908. $R.css("zIndex", enable ? z.pane_sliding+2 : z.resizer_normal); // NOTE: mask = pane_sliding+1
  2909. // make sure we have a valid event
  2910. if (!trigger.match(/click|mouseleave/))
  2911. trigger = o.slideTrigger_close = "mouseleave"; // also catches 'mouseout'
  2912. // add/remove slide triggers
  2913. $R[action](trigger, slideClose); // base event on resize
  2914. // need extra events for mouseleave
  2915. if (trigger === "mouseleave") {
  2916. // also close on pane.mouseleave
  2917. $P[action]("mouseleave."+ sID, slideClose);
  2918. // cancel timer when mouse moves between 'pane' and 'resizer'
  2919. $R[action]("mouseenter."+ sID, cancelMouseOut);
  2920. $P[action]("mouseenter."+ sID, cancelMouseOut);
  2921. }
  2922. if (!enable)
  2923. timer.clear(pane+"_closeSlider");
  2924. else if (trigger === "click" && !o.resizable) {
  2925. // IF pane is not resizable (which already has a cursor and tip)
  2926. // then set the a cursor & title/tip on resizer when sliding
  2927. $R.css("cursor", enable ? o.sliderCursor : "default");
  2928. $R.attr("title", enable ? o.togglerTip_open : ""); // use Toggler-tip, eg: "Close Pane"
  2929. }
  2930. // SUBROUTINE for mouseleave timer clearing
  2931. function cancelMouseOut (evt) {
  2932. timer.clear(pane+"_closeSlider");
  2933. evt.stopPropagation();
  2934. }
  2935. }
  2936. /**
  2937. * Hides/closes a pane if there is insufficient room - reverses this when there is room again
  2938. * MUST have already called setSizeLimits() before calling this method
  2939. *
  2940. * @param {string} pane The pane being resized
  2941. * @param {boolean=} [isOpening=false] Called from onOpen?
  2942. * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
  2943. * @param {boolean=} [force=false]
  2944. */
  2945. , makePaneFit = function (pane, isOpening, skipCallback, force) {
  2946. var
  2947. o = options[pane]
  2948. , s = state[pane]
  2949. , c = _c[pane]
  2950. , $P = $Ps[pane]
  2951. , $R = $Rs[pane]
  2952. , isSidePane = c.dir==="vert"
  2953. , hasRoom = false
  2954. ;
  2955. // special handling for center & east/west panes
  2956. if (pane === "center" || (isSidePane && s.noVerticalRoom)) {
  2957. // see if there is enough room to display the pane
  2958. // ERROR: hasRoom = s.minHeight <= s.maxHeight && (isSidePane || s.minWidth <= s.maxWidth);
  2959. hasRoom = (s.maxHeight >= 0);
  2960. if (hasRoom && s.noRoom) { // previously hidden due to noRoom, so show now
  2961. $P.show();
  2962. if ($R) $R.show();
  2963. s.isVisible = true;
  2964. s.noRoom = false;
  2965. if (isSidePane) s.noVerticalRoom = false;
  2966. _fixIframe(pane);
  2967. }
  2968. else if (!hasRoom && !s.noRoom) { // not currently hidden, so hide now
  2969. $P.hide();
  2970. if ($R) $R.hide();
  2971. s.isVisible = false;
  2972. s.noRoom = true;
  2973. }
  2974. }
  2975. // see if there is enough room to fit the border-pane
  2976. if (pane === "center") {
  2977. // ignore center in this block
  2978. }
  2979. else if (s.minSize <= s.maxSize) { // pane CAN fit
  2980. hasRoom = true;
  2981. if (s.size > s.maxSize) // pane is too big - shrink it
  2982. sizePane(pane, s.maxSize, skipCallback, force, true); // true = noAnimation
  2983. else if (s.size < s.minSize) // pane is too small - enlarge it
  2984. sizePane(pane, s.minSize, skipCallback, force, true);
  2985. else if ($R && $P.is(":visible")) {
  2986. // make sure resizer-bar is positioned correctly
  2987. // handles situation where nested layout was 'hidden' when initialized
  2988. var
  2989. side = c.side.toLowerCase()
  2990. , pos = s.size + sC["inset"+ c.side]
  2991. ;
  2992. if ($.layout.cssNum($R, side) != pos) $R.css( side, pos );
  2993. }
  2994. // if was previously hidden due to noRoom, then RESET because NOW there is room
  2995. if (s.noRoom) {
  2996. // s.noRoom state will be set by open or show
  2997. if (s.wasOpen && o.closable) {
  2998. if (o.autoReopen)
  2999. open(pane, false, true, true); // true = noAnimation, true = noAlert
  3000. else // leave the pane closed, so just update state
  3001. s.noRoom = false;
  3002. }
  3003. else
  3004. show(pane, s.wasOpen, true, true); // true = noAnimation, true = noAlert
  3005. }
  3006. }
  3007. else { // !hasRoom - pane CANNOT fit
  3008. if (!s.noRoom) { // pane not set as noRoom yet, so hide or close it now...
  3009. s.noRoom = true; // update state
  3010. s.wasOpen = !s.isClosed && !s.isSliding;
  3011. if (s.isClosed){} // SKIP
  3012. else if (o.closable) // 'close' if possible
  3013. close(pane, true, true); // true = force, true = noAnimation
  3014. else // 'hide' pane if cannot just be closed
  3015. hide(pane, true); // true = noAnimation
  3016. }
  3017. }
  3018. }
  3019. /**
  3020. * sizePane / manualSizePane
  3021. * sizePane is called only by internal methods whenever a pane needs to be resized
  3022. * manualSizePane is an exposed flow-through method allowing extra code when pane is 'manually resized'
  3023. *
  3024. * @param {string} pane The pane being resized
  3025. * @param {number} size The *desired* new size for this pane - will be validated
  3026. * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
  3027. * @param {boolean=} [noAnimation=false]
  3028. */
  3029. , manualSizePane = function (pane, size, skipCallback, noAnimation) {
  3030. if (!isInitialized()) return;
  3031. var
  3032. o = options[pane]
  3033. , s = state[pane]
  3034. // if resizing callbacks have been delayed and resizing is now DONE, force resizing to complete...
  3035. , forceResize = o.livePaneResizing && !s.isResizing
  3036. ;
  3037. // ANY call to manualSizePane disables autoResize - ie, percentage sizing
  3038. o.autoResize = false;
  3039. // flow-through...
  3040. sizePane(pane, size, skipCallback, forceResize, noAnimation); // will animate resize if option enabled
  3041. }
  3042. /**
  3043. * @param {string} pane The pane being resized
  3044. * @param {number} size The *desired* new size for this pane - will be validated
  3045. * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
  3046. * @param {boolean=} [force=false] Force resizing even if does not seem necessary
  3047. * @param {boolean=} [noAnimation=false]
  3048. */
  3049. , sizePane = function (pane, size, skipCallback, force, noAnimation) {
  3050. if (!isInitialized()) return;
  3051. var
  3052. o = options[pane]
  3053. , s = state[pane]
  3054. , $P = $Ps[pane]
  3055. , $R = $Rs[pane]
  3056. , side = _c[pane].side.toLowerCase()
  3057. , dimName = _c[pane].sizeType.toLowerCase()
  3058. , inset = "inset"+ _c[pane].side
  3059. , skipResizeWhileDragging = s.isResizing && !o.triggerEventsDuringLiveResize
  3060. , doFX = noAnimation !== true && o.animatePaneSizing
  3061. , oldSize, newSize
  3062. ;
  3063. // QUEUE in case another action/animation is in progress
  3064. $N.queue(function( queueNext ){
  3065. // calculate 'current' min/max sizes
  3066. setSizeLimits(pane); // update pane-state
  3067. oldSize = s.size;
  3068. size = _parseSize(pane, size); // handle percentages & auto
  3069. size = max(size, _parseSize(pane, o.minSize));
  3070. size = min(size, s.maxSize);
  3071. if (size < s.minSize) { // not enough room for pane!
  3072. makePaneFit(pane, false, skipCallback); // will hide or close pane
  3073. return queueNext();
  3074. }
  3075. // IF newSize is same as oldSize, then nothing to do - abort
  3076. if (!force && size === oldSize)
  3077. return queueNext();
  3078. // onresize_start callback CANNOT cancel resizing because this would break the layout!
  3079. if (!skipCallback && state.initialized && s.isVisible)
  3080. _execCallback(pane, o.onresize_start);
  3081. // resize the pane, and make sure its visible
  3082. newSize = cssSize(pane, size);
  3083. if (doFX && $P.is(":visible")) { // ANIMATE
  3084. var fx = $.layout.effects.size[pane] || $.layout.effects.size.all
  3085. , easing = o.fxSettings_size.easing || fx.easing
  3086. , z = options.zIndexes
  3087. , props = {};
  3088. props[ dimName ] = newSize +'px';
  3089. s.isMoving = true;
  3090. // overlay all elements during animation
  3091. $P.css({ zIndex: z.pane_animate })
  3092. .show().animate( props, o.fxSpeed_size, easing, function(){
  3093. // reset zIndex after animation
  3094. $P.css({ zIndex: (s.isSliding ? z.pane_sliding : z.pane_normal) });
  3095. s.isMoving = false;
  3096. sizePane_2(); // continue
  3097. queueNext();
  3098. });
  3099. }
  3100. else { // no animation
  3101. $P.css( dimName, newSize ); // resize pane
  3102. // if pane is visible, then
  3103. if ($P.is(":visible"))
  3104. sizePane_2(); // continue
  3105. else {
  3106. // pane is NOT VISIBLE, so just update state data...
  3107. // when pane is *next opened*, it will have the new size
  3108. s.size = size; // update state.size
  3109. $.extend(s, elDims($P)); // update state dimensions
  3110. }
  3111. queueNext();
  3112. };
  3113. });
  3114. // SUBROUTINE
  3115. function sizePane_2 () {
  3116. /* Panes are sometimes not sized precisely in some browsers!?
  3117. * This code will resize the pane up to 3 times to nudge the pane to the correct size
  3118. */
  3119. var actual = dimName==='width' ? $P.outerWidth() : $P.outerHeight()
  3120. , tries = [{
  3121. pane: pane
  3122. , count: 1
  3123. , target: size
  3124. , actual: actual
  3125. , correct: (size === actual)
  3126. , attempt: size
  3127. , cssSize: newSize
  3128. }]
  3129. , lastTry = tries[0]
  3130. , msg = 'Inaccurate size after resizing the '+ pane +'-pane.'
  3131. ;
  3132. while ( !lastTry.correct ) {
  3133. thisTry = { pane: pane, count: lastTry.count+1, target: size };
  3134. if (lastTry.actual > size)
  3135. thisTry.attempt = max(0, lastTry.attempt - (lastTry.actual - size));
  3136. else // lastTry.actual < size
  3137. thisTry.attempt = max(0, lastTry.attempt + (size - lastTry.actual));
  3138. thisTry.cssSize = cssSize(pane, thisTry.attempt);
  3139. $P.css( dimName, thisTry.cssSize );
  3140. thisTry.actual = dimName=='width' ? $P.outerWidth() : $P.outerHeight();
  3141. thisTry.correct = (size === thisTry.actual);
  3142. // if showDebugMessages, log attempts and alert the user of this *non-fatal error*
  3143. if (options.showDebugMessages) {
  3144. if ( tries.length === 1) {
  3145. _log(msg, false);
  3146. _log(lastTry, false);
  3147. }
  3148. _log(thisTry, false);
  3149. }
  3150. // after 4 tries, is as close as its gonna get!
  3151. if (tries.length > 3) break;
  3152. tries.push( thisTry );
  3153. lastTry = tries[ tries.length - 1 ];
  3154. }
  3155. // END TESTING CODE
  3156. // update pane-state dimensions
  3157. s.size = size;
  3158. $.extend(s, elDims($P));
  3159. // reposition the resizer-bar
  3160. if ($R && $P.is(":visible")) $R.css( side, size + sC[inset] );
  3161. sizeContent(pane);
  3162. if (!skipCallback && !skipResizeWhileDragging && state.initialized && s.isVisible) {
  3163. _execCallback(pane, o.onresize_end || o.onresize);
  3164. resizeChildLayout(pane);
  3165. }
  3166. // resize all the adjacent panes, and adjust their toggler buttons
  3167. // when skipCallback passed, it means the controlling method will handle 'other panes'
  3168. if (!skipCallback) {
  3169. // also no callback if live-resize is in progress and NOT triggerEventsDuringLiveResize
  3170. if (!s.isSliding) sizeMidPanes(_c[pane].dir=="horz" ? "" : "center", skipResizeWhileDragging, force);
  3171. sizeHandles();
  3172. }
  3173. // if opposite-pane was autoClosed, see if it can be autoOpened now
  3174. var altPane = _c.oppositeEdge[pane];
  3175. if (size < oldSize && state[ altPane ].noRoom) {
  3176. setSizeLimits( altPane );
  3177. makePaneFit( altPane, false, skipCallback );
  3178. }
  3179. // DEBUG - ALERT user/developer so they know there was a sizing problem
  3180. if (options.showDebugMessages && tries.length > 1)
  3181. _log(msg +'\nSee the Error Console for details.', true);
  3182. }
  3183. }
  3184. /**
  3185. * @see initPanes(), sizePane(), resizeAll(), open(), close(), hide()
  3186. * @param {string} panes The pane(s) being resized, comma-delmited string
  3187. * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
  3188. * @param {boolean=} [force=false]
  3189. */
  3190. , sizeMidPanes = function (panes, skipCallback, force) {
  3191. panes = (panes ? panes : "east,west,center").split(",");
  3192. $.each(panes, function (i, pane) {
  3193. if (!$Ps[pane]) return; // NO PANE - skip
  3194. var
  3195. o = options[pane]
  3196. , s = state[pane]
  3197. , $P = $Ps[pane]
  3198. , $R = $Rs[pane]
  3199. , isCenter= (pane=="center")
  3200. , hasRoom = true
  3201. , CSS = {}
  3202. , newCenter = calcNewCenterPaneDims()
  3203. ;
  3204. // update pane-state dimensions
  3205. $.extend(s, elDims($P));
  3206. if (pane === "center") {
  3207. if (!force && s.isVisible && newCenter.width === s.outerWidth && newCenter.height === s.outerHeight)
  3208. return true; // SKIP - pane already the correct size
  3209. // set state for makePaneFit() logic
  3210. $.extend(s, cssMinDims(pane), {
  3211. maxWidth: newCenter.width
  3212. , maxHeight: newCenter.height
  3213. });
  3214. CSS = newCenter;
  3215. // convert OUTER width/height to CSS width/height
  3216. CSS.width = cssW($P, CSS.width);
  3217. // NEW - allow pane to extend 'below' visible area rather than hide it
  3218. CSS.height = cssH($P, CSS.height);
  3219. hasRoom = CSS.width >= 0 && CSS.height >= 0; // height >= 0 = ALWAYS TRUE NOW
  3220. // during layout init, try to shrink east/west panes to make room for center
  3221. if (!state.initialized && o.minWidth > s.outerWidth) {
  3222. var
  3223. reqPx = o.minWidth - s.outerWidth
  3224. , minE = options.east.minSize || 0
  3225. , minW = options.west.minSize || 0
  3226. , sizeE = state.east.size
  3227. , sizeW = state.west.size
  3228. , newE = sizeE
  3229. , newW = sizeW
  3230. ;
  3231. if (reqPx > 0 && state.east.isVisible && sizeE > minE) {
  3232. newE = max( sizeE-minE, sizeE-reqPx );
  3233. reqPx -= sizeE-newE;
  3234. }
  3235. if (reqPx > 0 && state.west.isVisible && sizeW > minW) {
  3236. newW = max( sizeW-minW, sizeW-reqPx );
  3237. reqPx -= sizeW-newW;
  3238. }
  3239. // IF we found enough extra space, then resize the border panes as calculated
  3240. if (reqPx === 0) {
  3241. if (sizeE != minE)
  3242. sizePane('east', newE, true, force, true); // true = skipCallback/noAnimation - initPanes will handle when done
  3243. if (sizeW != minW)
  3244. sizePane('west', newW, true, force, true);
  3245. // now start over!
  3246. sizeMidPanes('center', skipCallback, force);
  3247. return; // abort this loop
  3248. }
  3249. }
  3250. }
  3251. else { // for east and west, set only the height, which is same as center height
  3252. // set state.min/maxWidth/Height for makePaneFit() logic
  3253. if (s.isVisible && !s.noVerticalRoom)
  3254. $.extend(s, elDims($P), cssMinDims(pane))
  3255. if (!force && !s.noVerticalRoom && newCenter.height === s.outerHeight)
  3256. return true; // SKIP - pane already the correct size
  3257. // east/west have same top, bottom & height as center
  3258. CSS.top = newCenter.top;
  3259. CSS.bottom = newCenter.bottom;
  3260. // NEW - allow pane to extend 'below' visible area rather than hide it
  3261. CSS.height = cssH($P, newCenter.height);
  3262. s.maxHeight = CSS.height;
  3263. hasRoom = (s.maxHeight >= 0); // ALWAYS TRUE NOW
  3264. if (!hasRoom) s.noVerticalRoom = true; // makePaneFit() logic
  3265. }
  3266. if (hasRoom) {
  3267. // resizeAll passes skipCallback because it triggers callbacks after ALL panes are resized
  3268. if (!skipCallback && state.initialized)
  3269. _execCallback(pane, o.onresize_start);
  3270. $P.css(CSS); // apply the CSS to pane
  3271. if (s.noRoom && !s.isClosed && !s.isHidden)
  3272. makePaneFit(pane); // will re-open/show auto-closed/hidden pane
  3273. if (s.isVisible) {
  3274. $.extend(s, elDims($P)); // update pane dimensions
  3275. if (state.initialized) sizeContent(pane); // also resize the contents, if exists
  3276. }
  3277. }
  3278. else if (!s.noRoom && s.isVisible) // no room for pane
  3279. makePaneFit(pane); // will hide or close pane
  3280. if (!s.isVisible)
  3281. return true; // DONE - next pane
  3282. /*
  3283. * Extra CSS for IE6 or IE7 in Quirks-mode - add 'width' to NORTH/SOUTH panes
  3284. * Normally these panes have only 'left' & 'right' positions so pane auto-sizes
  3285. * ALSO required when pane is an IFRAME because will NOT default to 'full width'
  3286. */
  3287. if (pane === "center") { // finished processing midPanes
  3288. var b = $.layout.browser;
  3289. var fix = b.isIE6 || (b.msie && !$.support.boxModel);
  3290. if ($Ps.north && (fix || state.north.tagName=="IFRAME"))
  3291. $Ps.north.css("width", cssW($Ps.north, sC.innerWidth));
  3292. if ($Ps.south && (fix || state.south.tagName=="IFRAME"))
  3293. $Ps.south.css("width", cssW($Ps.south, sC.innerWidth));
  3294. }
  3295. // resizeAll passes skipCallback because it triggers callbacks after ALL panes are resized
  3296. if (!skipCallback && state.initialized) {
  3297. _execCallback(pane, o.onresize_end || o.onresize);
  3298. resizeChildLayout(pane);
  3299. }
  3300. });
  3301. }
  3302. /**
  3303. * @see window.onresize(), callbacks or custom code
  3304. */
  3305. , resizeAll = function () {
  3306. if (!state.initialized) {
  3307. _initLayoutElements();
  3308. return; // no need to resize since we just initialized!
  3309. }
  3310. var oldW = sC.innerWidth
  3311. , oldH = sC.innerHeight
  3312. ;
  3313. // cannot size layout when 'container' is hidden or collapsed
  3314. if (!$N.is(":visible:") ) return;
  3315. $.extend( state.container, elDims( $N ) ); // UPDATE container dimensions
  3316. if (!sC.outerHeight) return;
  3317. // onresizeall_start will CANCEL resizing if returns false
  3318. // state.container has already been set, so user can access this info for calcuations
  3319. if (false === _execCallback(null, options.onresizeall_start)) return false;
  3320. var // see if container is now 'smaller' than before
  3321. shrunkH = (sC.innerHeight < oldH)
  3322. , shrunkW = (sC.innerWidth < oldW)
  3323. , $P, o, s, dir
  3324. ;
  3325. // NOTE special order for sizing: S-N-E-W
  3326. $.each(["south","north","east","west"], function (i, pane) {
  3327. if (!$Ps[pane]) return; // no pane - SKIP
  3328. s = state[pane];
  3329. o = options[pane];
  3330. dir = _c[pane].dir;
  3331. if (o.autoResize && s.size != o.size) // resize pane to original size set in options
  3332. sizePane(pane, o.size, true, true, true); // true=skipCallback/forceResize/noAnimation
  3333. else {
  3334. setSizeLimits(pane);
  3335. makePaneFit(pane, false, true, true); // true=skipCallback/forceResize
  3336. }
  3337. });
  3338. sizeMidPanes("", true, true); // true=skipCallback, true=forceResize
  3339. sizeHandles(); // reposition the toggler elements
  3340. // trigger all individual pane callbacks AFTER layout has finished resizing
  3341. o = options; // reuse alias
  3342. $.each(_c.allPanes, function (i, pane) {
  3343. $P = $Ps[pane];
  3344. if (!$P) return; // SKIP
  3345. if (state[pane].isVisible) { // undefined for non-existent panes
  3346. _execCallback(pane, o[pane].onresize_end || o[pane].onresize); // callback - if exists
  3347. resizeChildLayout(pane);
  3348. }
  3349. });
  3350. _execCallback(null, o.onresizeall_end || o.onresizeall); // onresizeall callback, if exists
  3351. }
  3352. /**
  3353. * Whenever a pane resizes or opens that has a nested layout, trigger resizeAll
  3354. *
  3355. * @param {string} pane The pane just resized or opened
  3356. */
  3357. , resizeChildLayout = function (pane) {
  3358. if (!options[pane].resizeChildLayout) return;
  3359. var
  3360. $P = $Ps[pane]
  3361. , $C = $Cs[pane]
  3362. , d = "layout"
  3363. , P = Instance[pane]
  3364. , L = children[pane]
  3365. ;
  3366. // user may have manually set EITHER instance pointer, so handle that
  3367. if (P.child && !L) {
  3368. // have to reverse the pointers!
  3369. var el = P.child.container;
  3370. L = children[pane] = (el ? el.data(d) : 0) || null; // set pointer _directly_ to layout instance
  3371. }
  3372. // if a layout-pointer exists, see if child has been destroyed
  3373. if (L && L.destroyed)
  3374. L = children[pane] = null; // clear child pointers
  3375. // no child layout pointer is set - see if there is a child layout NOW
  3376. if (!L) L = children[pane] = $P.data(d) || ($C ? $C.data(d) : 0) || null; // set/update child pointers
  3377. // ALWAYS refresh the pane.child alias
  3378. P.child = children[pane];
  3379. if (L) L.resizeAll();
  3380. }
  3381. /**
  3382. * IF pane has a content-div, then resize all elements inside pane to fit pane-height
  3383. *
  3384. * @param {string=} [panes=""] The pane(s) being resized
  3385. * @param {boolean=} [remeasure=false] Should the content (header/footer) be remeasured?
  3386. */
  3387. , sizeContent = function (panes, remeasure) {
  3388. if (!isInitialized()) return;
  3389. panes = panes ? panes.split(",") : _c.allPanes;
  3390. $.each(panes, function (idx, pane) {
  3391. var
  3392. $P = $Ps[pane]
  3393. , $C = $Cs[pane]
  3394. , o = options[pane]
  3395. , s = state[pane]
  3396. , m = s.content // m = measurements
  3397. ;
  3398. if (!$P || !$C || !$P.is(":visible")) return true; // NOT VISIBLE - skip
  3399. // onsizecontent_start will CANCEL resizing if returns false
  3400. if (false === _execCallback(null, o.onsizecontent_start)) return;
  3401. // skip re-measuring offsets if live-resizing
  3402. if ((!s.isMoving && !s.isResizing) || o.liveContentResizing || remeasure || m.top == undefined) {
  3403. _measure();
  3404. // if any footers are below pane-bottom, they may not measure correctly,
  3405. // so allow pane overflow and re-measure
  3406. if (m.hiddenFooters > 0 && $P.css("overflow") === "hidden") {
  3407. $P.css("overflow", "visible");
  3408. _measure(); // remeasure while overflowing
  3409. $P.css("overflow", "hidden");
  3410. }
  3411. }
  3412. // NOTE: spaceAbove/Below *includes* the pane paddingTop/Bottom, but not pane.borders
  3413. var newH = s.innerHeight - (m.spaceAbove - s.css.paddingTop) - (m.spaceBelow - s.css.paddingBottom);
  3414. if (!$C.is(":visible") || m.height != newH) {
  3415. // size the Content element to fit new pane-size - will autoHide if not enough room
  3416. setOuterHeight($C, newH, true); // true=autoHide
  3417. m.height = newH; // save new height
  3418. };
  3419. if (state.initialized) {
  3420. _execCallback(pane, o.onsizecontent_end || o.onsizecontent);
  3421. resizeChildLayout(pane);
  3422. }
  3423. function _below ($E) {
  3424. return max(s.css.paddingBottom, (parseInt($E.css("marginBottom"), 10) || 0));
  3425. };
  3426. function _measure () {
  3427. var
  3428. ignore = options[pane].contentIgnoreSelector
  3429. , $Fs = $C.nextAll().not(ignore || ':lt(0)') // not :lt(0) = ALL
  3430. , $Fs_vis = $Fs.filter(':visible')
  3431. , $F = $Fs_vis.filter(':last')
  3432. ;
  3433. m = {
  3434. top: $C[0].offsetTop
  3435. , height: $C.outerHeight()
  3436. , numFooters: $Fs.length
  3437. , hiddenFooters: $Fs.length - $Fs_vis.length
  3438. , spaceBelow: 0 // correct if no content footer ($E)
  3439. }
  3440. m.spaceAbove = m.top; // just for state - not used in calc
  3441. m.bottom = m.top + m.height;
  3442. if ($F.length)
  3443. //spaceBelow = (LastFooter.top + LastFooter.height) [footerBottom] - Content.bottom + max(LastFooter.marginBottom, pane.paddingBotom)
  3444. m.spaceBelow = ($F[0].offsetTop + $F.outerHeight()) - m.bottom + _below($F);
  3445. else // no footer - check marginBottom on Content element itself
  3446. m.spaceBelow = _below($C);
  3447. };
  3448. });
  3449. }
  3450. /**
  3451. * Called every time a pane is opened, closed, or resized to slide the togglers to 'center' and adjust their length if necessary
  3452. *
  3453. * @see initHandles(), open(), close(), resizeAll()
  3454. * @param {string=} [panes=""] The pane(s) being resized
  3455. */
  3456. , sizeHandles = function (panes) {
  3457. panes = panes ? panes.split(",") : _c.borderPanes;
  3458. $.each(panes, function (i, pane) {
  3459. var
  3460. o = options[pane]
  3461. , s = state[pane]
  3462. , $P = $Ps[pane]
  3463. , $R = $Rs[pane]
  3464. , $T = $Ts[pane]
  3465. , $TC
  3466. ;
  3467. if (!$P || !$R) return;
  3468. var
  3469. dir = _c[pane].dir
  3470. , _state = (s.isClosed ? "_closed" : "_open")
  3471. , spacing = o["spacing"+ _state]
  3472. , togAlign = o["togglerAlign"+ _state]
  3473. , togLen = o["togglerLength"+ _state]
  3474. , paneLen
  3475. , offset
  3476. , CSS = {}
  3477. ;
  3478. if (spacing === 0) {
  3479. $R.hide();
  3480. return;
  3481. }
  3482. else if (!s.noRoom && !s.isHidden) // skip if resizer was hidden for any reason
  3483. $R.show(); // in case was previously hidden
  3484. // Resizer Bar is ALWAYS same width/height of pane it is attached to
  3485. if (dir === "horz") { // north/south
  3486. paneLen = $P.outerWidth(); // s.outerWidth ||
  3487. s.resizerLength = paneLen;
  3488. $R.css({
  3489. width: cssW($R, paneLen) // account for borders & padding
  3490. , height: cssH($R, spacing) // ditto
  3491. , left: $.layout.cssNum($P, "left")
  3492. });
  3493. }
  3494. else { // east/west
  3495. paneLen = $P.outerHeight(); // s.outerHeight ||
  3496. s.resizerLength = paneLen;
  3497. $R.css({
  3498. height: cssH($R, paneLen) // account for borders & padding
  3499. , width: cssW($R, spacing) // ditto
  3500. , top: sC.insetTop + getPaneSize("north", true) // TODO: what if no North pane?
  3501. //, top: $.layout.cssNum($Ps["center"], "top")
  3502. });
  3503. }
  3504. // remove hover classes
  3505. removeHover( o, $R );
  3506. if ($T) {
  3507. if (togLen === 0 || (s.isSliding && o.hideTogglerOnSlide)) {
  3508. $T.hide(); // always HIDE the toggler when 'sliding'
  3509. return;
  3510. }
  3511. else
  3512. $T.show(); // in case was previously hidden
  3513. if (!(togLen > 0) || togLen === "100%" || togLen > paneLen) {
  3514. togLen = paneLen;
  3515. offset = 0;
  3516. }
  3517. else { // calculate 'offset' based on options.PANE.togglerAlign_open/closed
  3518. if (isStr(togAlign)) {
  3519. switch (togAlign) {
  3520. case "top":
  3521. case "left": offset = 0;
  3522. break;
  3523. case "bottom":
  3524. case "right": offset = paneLen - togLen;
  3525. break;
  3526. case "middle":
  3527. case "center":
  3528. default: offset = round((paneLen - togLen) / 2); // 'default' catches typos
  3529. }
  3530. }
  3531. else { // togAlign = number
  3532. var x = parseInt(togAlign, 10); //
  3533. if (togAlign >= 0) offset = x;
  3534. else offset = paneLen - togLen + x; // NOTE: x is negative!
  3535. }
  3536. }
  3537. if (dir === "horz") { // north/south
  3538. var width = cssW($T, togLen);
  3539. $T.css({
  3540. width: width // account for borders & padding
  3541. , height: cssH($T, spacing) // ditto
  3542. , left: offset // TODO: VERIFY that toggler positions correctly for ALL values
  3543. , top: 0
  3544. });
  3545. // CENTER the toggler content SPAN
  3546. $T.children(".content").each(function(){
  3547. $TC = $(this);
  3548. $TC.css("marginLeft", round((width-$TC.outerWidth())/2)); // could be negative
  3549. });
  3550. }
  3551. else { // east/west
  3552. var height = cssH($T, togLen);
  3553. $T.css({
  3554. height: height // account for borders & padding
  3555. , width: cssW($T, spacing) // ditto
  3556. , top: offset // POSITION the toggler
  3557. , left: 0
  3558. });
  3559. // CENTER the toggler content SPAN
  3560. $T.children(".content").each(function(){
  3561. $TC = $(this);
  3562. $TC.css("marginTop", round((height-$TC.outerHeight())/2)); // could be negative
  3563. });
  3564. }
  3565. // remove ALL hover classes
  3566. removeHover( 0, $T );
  3567. }
  3568. // DONE measuring and sizing this resizer/toggler, so can be 'hidden' now
  3569. if (!state.initialized && (o.initHidden || s.noRoom)) {
  3570. $R.hide();
  3571. if ($T) $T.hide();
  3572. }
  3573. });
  3574. }
  3575. /**
  3576. * @param {string} pane
  3577. */
  3578. , enableClosable = function (pane) {
  3579. if (!isInitialized()) return;
  3580. var $T = $Ts[pane], o = options[pane];
  3581. if (!$T) return;
  3582. o.closable = true;
  3583. $T .bind("click."+ sID, function(evt){ evt.stopPropagation(); toggle(pane); })
  3584. .css("visibility", "visible")
  3585. .css("cursor", "pointer")
  3586. .attr("title", state[pane].isClosed ? o.togglerTip_closed : o.togglerTip_open) // may be blank
  3587. .show();
  3588. }
  3589. /**
  3590. * @param {string} pane
  3591. * @param {boolean=} [hide=false]
  3592. */
  3593. , disableClosable = function (pane, hide) {
  3594. if (!isInitialized()) return;
  3595. var $T = $Ts[pane];
  3596. if (!$T) return;
  3597. options[pane].closable = false;
  3598. // is closable is disable, then pane MUST be open!
  3599. if (state[pane].isClosed) open(pane, false, true);
  3600. $T .unbind("."+ sID)
  3601. .css("visibility", hide ? "hidden" : "visible") // instead of hide(), which creates logic issues
  3602. .css("cursor", "default")
  3603. .attr("title", "");
  3604. }
  3605. /**
  3606. * @param {string} pane
  3607. */
  3608. , enableSlidable = function (pane) {
  3609. if (!isInitialized()) return;
  3610. var $R = $Rs[pane], o = options[pane];
  3611. if (!$R || !$R.data('draggable')) return;
  3612. options[pane].slidable = true;
  3613. if (s.isClosed)
  3614. bindStartSlidingEvent(pane, true);
  3615. }
  3616. /**
  3617. * @param {string} pane
  3618. */
  3619. , disableSlidable = function (pane) {
  3620. if (!isInitialized()) return;
  3621. var $R = $Rs[pane];
  3622. if (!$R) return;
  3623. options[pane].slidable = false;
  3624. if (state[pane].isSliding)
  3625. close(pane, false, true);
  3626. else {
  3627. bindStartSlidingEvent(pane, false);
  3628. $R .css("cursor", "default")
  3629. .attr("title", "");
  3630. removeHover(null, $R[0]); // in case currently hovered
  3631. }
  3632. }
  3633. /**
  3634. * @param {string} pane
  3635. */
  3636. , enableResizable = function (pane) {
  3637. if (!isInitialized()) return;
  3638. var $R = $Rs[pane], o = options[pane];
  3639. if (!$R || !$R.data('draggable')) return;
  3640. o.resizable = true;
  3641. $R.draggable("enable");
  3642. if (!state[pane].isClosed)
  3643. $R .css("cursor", o.resizerCursor)
  3644. .attr("title", o.resizerTip);
  3645. }
  3646. /**
  3647. * @param {string} pane
  3648. */
  3649. , disableResizable = function (pane) {
  3650. if (!isInitialized()) return;
  3651. var $R = $Rs[pane];
  3652. if (!$R || !$R.data('draggable')) return;
  3653. options[pane].resizable = false;
  3654. $R .draggable("disable")
  3655. .css("cursor", "default")
  3656. .attr("title", "");
  3657. removeHover(null, $R[0]); // in case currently hovered
  3658. }
  3659. /**
  3660. * Move a pane from source-side (eg, west) to target-side (eg, east)
  3661. * If pane exists on target-side, move that to source-side, ie, 'swap' the panes
  3662. *
  3663. * @param {string} pane1 The pane/edge being swapped
  3664. * @param {string} pane2 ditto
  3665. */
  3666. , swapPanes = function (pane1, pane2) {
  3667. if (!isInitialized()) return;
  3668. // change state.edge NOW so callbacks can know where pane is headed...
  3669. state[pane1].edge = pane2;
  3670. state[pane2].edge = pane1;
  3671. // run these even if NOT state.initialized
  3672. var cancelled = false;
  3673. if (false === _execCallback(pane1, options[pane1].onswap_start)) cancelled = true;
  3674. if (!cancelled && false === _execCallback(pane2, options[pane2].onswap_start)) cancelled = true;
  3675. if (cancelled) {
  3676. state[pane1].edge = pane1; // reset
  3677. state[pane2].edge = pane2;
  3678. return;
  3679. }
  3680. var
  3681. oPane1 = copy( pane1 )
  3682. , oPane2 = copy( pane2 )
  3683. , sizes = {}
  3684. ;
  3685. sizes[pane1] = oPane1 ? oPane1.state.size : 0;
  3686. sizes[pane2] = oPane2 ? oPane2.state.size : 0;
  3687. // clear pointers & state
  3688. $Ps[pane1] = false;
  3689. $Ps[pane2] = false;
  3690. state[pane1] = {};
  3691. state[pane2] = {};
  3692. // ALWAYS remove the resizer & toggler elements
  3693. if ($Ts[pane1]) $Ts[pane1].remove();
  3694. if ($Ts[pane2]) $Ts[pane2].remove();
  3695. if ($Rs[pane1]) $Rs[pane1].remove();
  3696. if ($Rs[pane2]) $Rs[pane2].remove();
  3697. $Rs[pane1] = $Rs[pane2] = $Ts[pane1] = $Ts[pane2] = false;
  3698. // transfer element pointers and data to NEW Layout keys
  3699. move( oPane1, pane2 );
  3700. move( oPane2, pane1 );
  3701. // cleanup objects
  3702. oPane1 = oPane2 = sizes = null;
  3703. // make panes 'visible' again
  3704. if ($Ps[pane1]) $Ps[pane1].css(_c.visible);
  3705. if ($Ps[pane2]) $Ps[pane2].css(_c.visible);
  3706. // fix any size discrepancies caused by swap
  3707. resizeAll();
  3708. // run these even if NOT state.initialized
  3709. _execCallback(pane1, options[pane1].onswap_end || options[pane1].onswap);
  3710. _execCallback(pane2, options[pane2].onswap_end || options[pane2].onswap);
  3711. return;
  3712. function copy (n) { // n = pane
  3713. var
  3714. $P = $Ps[n]
  3715. , $C = $Cs[n]
  3716. ;
  3717. return !$P ? false : {
  3718. pane: n
  3719. , P: $P ? $P[0] : false
  3720. , C: $C ? $C[0] : false
  3721. , state: $.extend(true, {}, state[n])
  3722. , options: $.extend(true, {}, options[n])
  3723. }
  3724. };
  3725. function move (oPane, pane) {
  3726. if (!oPane) return;
  3727. var
  3728. P = oPane.P
  3729. , C = oPane.C
  3730. , oldPane = oPane.pane
  3731. , c = _c[pane]
  3732. , side = c.side.toLowerCase()
  3733. , inset = "inset"+ c.side
  3734. // save pane-options that should be retained
  3735. , s = $.extend({}, state[pane])
  3736. , o = options[pane]
  3737. // RETAIN side-specific FX Settings - more below
  3738. , fx = { resizerCursor: o.resizerCursor }
  3739. , re, size, pos
  3740. ;
  3741. $.each("fxName,fxSpeed,fxSettings".split(","), function (i, k) {
  3742. fx[k +"_open"] = o[k +"_open"];
  3743. fx[k +"_close"] = o[k +"_close"];
  3744. fx[k +"_size"] = o[k +"_size"];
  3745. });
  3746. // update object pointers and attributes
  3747. $Ps[pane] = $(P)
  3748. .data("layoutEdge", pane)
  3749. .css(_c.hidden)
  3750. .css(c.cssReq)
  3751. ;
  3752. $Cs[pane] = C ? $(C) : false;
  3753. // set options and state
  3754. options[pane] = $.extend({}, oPane.options, fx);
  3755. state[pane] = $.extend({}, oPane.state);
  3756. // change classNames on the pane, eg: ui-layout-pane-east ==> ui-layout-pane-west
  3757. re = new RegExp(o.paneClass +"-"+ oldPane, "g");
  3758. P.className = P.className.replace(re, o.paneClass +"-"+ pane);
  3759. // ALWAYS regenerate the resizer & toggler elements
  3760. initHandles(pane); // create the required resizer & toggler
  3761. // if moving to different orientation, then keep 'target' pane size
  3762. if (c.dir != _c[oldPane].dir) {
  3763. size = sizes[pane] || 0;
  3764. setSizeLimits(pane); // update pane-state
  3765. size = max(size, state[pane].minSize);
  3766. // use manualSizePane to disable autoResize - not useful after panes are swapped
  3767. manualSizePane(pane, size, true, true); // true/true = skipCallback/noAnimation)
  3768. }
  3769. else // move the resizer here
  3770. $Rs[pane].css(side, sC[inset] + (state[pane].isVisible ? getPaneSize(pane) : 0));
  3771. // ADD CLASSNAMES & SLIDE-BINDINGS
  3772. if (oPane.state.isVisible && !s.isVisible)
  3773. setAsOpen(pane, true); // true = skipCallback
  3774. else {
  3775. setAsClosed(pane);
  3776. bindStartSlidingEvent(pane, true); // will enable events IF option is set
  3777. }
  3778. // DESTROY the object
  3779. oPane = null;
  3780. };
  3781. }
  3782. /**
  3783. * INTERNAL method to sync pin-buttons when pane is opened or closed
  3784. * Unpinned means the pane is 'sliding' - ie, over-top of the adjacent panes
  3785. *
  3786. * @see open(), setAsOpen(), setAsClosed()
  3787. * @param {string} pane These are the params returned to callbacks by layout()
  3788. * @param {boolean} doPin True means set the pin 'down', False means 'up'
  3789. */
  3790. , syncPinBtns = function (pane, doPin) {
  3791. if ($.layout.plugins.buttons)
  3792. $.each(state[pane].pins, function (i, selector) {
  3793. $.layout.buttons.setPinState(Instance, $(selector), pane, doPin);
  3794. });
  3795. }
  3796. ; // END var DECLARATIONS
  3797. /**
  3798. * Capture keys when enableCursorHotkey - toggle pane if hotkey pressed
  3799. *
  3800. * @see document.keydown()
  3801. */
  3802. function keyDown (evt) {
  3803. if (!evt) return true;
  3804. var code = evt.keyCode;
  3805. if (code < 33) return true; // ignore special keys: ENTER, TAB, etc
  3806. var
  3807. PANE = {
  3808. 38: "north" // Up Cursor - $.ui.keyCode.UP
  3809. , 40: "south" // Down Cursor - $.ui.keyCode.DOWN
  3810. , 37: "west" // Left Cursor - $.ui.keyCode.LEFT
  3811. , 39: "east" // Right Cursor - $.ui.keyCode.RIGHT
  3812. }
  3813. , ALT = evt.altKey // no worky!
  3814. , SHIFT = evt.shiftKey
  3815. , CTRL = evt.ctrlKey
  3816. , CURSOR = (CTRL && code >= 37 && code <= 40)
  3817. , o, k, m, pane
  3818. ;
  3819. if (CURSOR && options[PANE[code]].enableCursorHotkey) // valid cursor-hotkey
  3820. pane = PANE[code];
  3821. else if (CTRL || SHIFT) // check to see if this matches a custom-hotkey
  3822. $.each(_c.borderPanes, function (i, p) { // loop each pane to check its hotkey
  3823. o = options[p];
  3824. k = o.customHotkey;
  3825. m = o.customHotkeyModifier; // if missing or invalid, treated as "CTRL+SHIFT"
  3826. if ((SHIFT && m=="SHIFT") || (CTRL && m=="CTRL") || (CTRL && SHIFT)) { // Modifier matches
  3827. if (k && code === (isNaN(k) || k <= 9 ? k.toUpperCase().charCodeAt(0) : k)) { // Key matches
  3828. pane = p;
  3829. return false; // BREAK
  3830. }
  3831. }
  3832. });
  3833. // validate pane
  3834. if (!pane || !$Ps[pane] || !options[pane].closable || state[pane].isHidden)
  3835. return true;
  3836. toggle(pane);
  3837. evt.stopPropagation();
  3838. evt.returnValue = false; // CANCEL key
  3839. return false;
  3840. };
  3841. /*
  3842. * ######################################
  3843. * UTILITY METHODS
  3844. * called externally or by initButtons
  3845. * ######################################
  3846. */
  3847. /**
  3848. * Change/reset a pane overflow setting & zIndex to allow popups/drop-downs to work
  3849. *
  3850. * @param {Object=} [el] (optional) Can also be 'bound' to a click, mouseOver, or other event
  3851. */
  3852. function allowOverflow (el) {
  3853. if (!isInitialized()) return;
  3854. if (this && this.tagName) el = this; // BOUND to element
  3855. var $P;
  3856. if (isStr(el))
  3857. $P = $Ps[el];
  3858. else if ($(el).data("layoutRole"))
  3859. $P = $(el);
  3860. else
  3861. $(el).parents().each(function(){
  3862. if ($(this).data("layoutRole")) {
  3863. $P = $(this);
  3864. return false; // BREAK
  3865. }
  3866. });
  3867. if (!$P || !$P.length) return; // INVALID
  3868. var
  3869. pane = $P.data("layoutEdge")
  3870. , s = state[pane]
  3871. ;
  3872. // if pane is already raised, then reset it before doing it again!
  3873. // this would happen if allowOverflow is attached to BOTH the pane and an element
  3874. if (s.cssSaved)
  3875. resetOverflow(pane); // reset previous CSS before continuing
  3876. // if pane is raised by sliding or resizing, or its closed, then abort
  3877. if (s.isSliding || s.isResizing || s.isClosed) {
  3878. s.cssSaved = false;
  3879. return;
  3880. }
  3881. var
  3882. newCSS = { zIndex: (options.zIndexes.resizer_normal + 1) }
  3883. , curCSS = {}
  3884. , of = $P.css("overflow")
  3885. , ofX = $P.css("overflowX")
  3886. , ofY = $P.css("overflowY")
  3887. ;
  3888. // determine which, if any, overflow settings need to be changed
  3889. if (of != "visible") {
  3890. curCSS.overflow = of;
  3891. newCSS.overflow = "visible";
  3892. }
  3893. if (ofX && !ofX.match(/visible|auto/)) {
  3894. curCSS.overflowX = ofX;
  3895. newCSS.overflowX = "visible";
  3896. }
  3897. if (ofY && !ofY.match(/visible|auto/)) {
  3898. curCSS.overflowY = ofX;
  3899. newCSS.overflowY = "visible";
  3900. }
  3901. // save the current overflow settings - even if blank!
  3902. s.cssSaved = curCSS;
  3903. // apply new CSS to raise zIndex and, if necessary, make overflow 'visible'
  3904. $P.css( newCSS );
  3905. // make sure the zIndex of all other panes is normal
  3906. $.each(_c.allPanes, function(i, p) {
  3907. if (p != pane) resetOverflow(p);
  3908. });
  3909. };
  3910. /**
  3911. * @param {Object=} [el] (optional) Can also be 'bound' to a click, mouseOver, or other event
  3912. */
  3913. function resetOverflow (el) {
  3914. if (!isInitialized()) return;
  3915. if (this && this.tagName) el = this; // BOUND to element
  3916. var $P;
  3917. if (isStr(el))
  3918. $P = $Ps[el];
  3919. else if ($(el).data("layoutRole"))
  3920. $P = $(el);
  3921. else
  3922. $(el).parents().each(function(){
  3923. if ($(this).data("layoutRole")) {
  3924. $P = $(this);
  3925. return false; // BREAK
  3926. }
  3927. });
  3928. if (!$P || !$P.length) return; // INVALID
  3929. var
  3930. pane = $P.data("layoutEdge")
  3931. , s = state[pane]
  3932. , CSS = s.cssSaved || {}
  3933. ;
  3934. // reset the zIndex
  3935. if (!s.isSliding && !s.isResizing)
  3936. $P.css("zIndex", options.zIndexes.pane_normal);
  3937. // reset Overflow - if necessary
  3938. $P.css( CSS );
  3939. // clear var
  3940. s.cssSaved = false;
  3941. };
  3942. /*
  3943. * #####################
  3944. * CREATE/RETURN LAYOUT
  3945. * #####################
  3946. */
  3947. // validate that container exists
  3948. var $N = $(this).eq(0); // FIRST matching Container element
  3949. if (!$N.length) {
  3950. if (options.showErrorMessages)
  3951. _log( lang.errContainerMissing, true );
  3952. return null;
  3953. };
  3954. // Users retrieve Instance of a layout with: $N.layout() OR $N.data("layout")
  3955. // return the Instance-pointer if layout has already been initialized
  3956. if ($N.data("layoutContainer") && $N.data("layout"))
  3957. return $N.data("layout"); // cached pointer
  3958. // init global vars
  3959. var
  3960. $Ps = {} // Panes x5 - set in initPanes()
  3961. , $Cs = {} // Content x5 - set in initPanes()
  3962. , $Rs = {} // Resizers x4 - set in initHandles()
  3963. , $Ts = {} // Togglers x4 - set in initHandles()
  3964. , $Ms = $([]) // Masks - up to 2 masks per pane (IFRAME + DIV)
  3965. // aliases for code brevity
  3966. , sC = state.container // alias for easy access to 'container dimensions'
  3967. , sID = state.id // alias for unique layout ID/namespace - eg: "layout435"
  3968. ;
  3969. // create Instance object to expose data & option Properties, and primary action Methods
  3970. var Instance = {
  3971. // layout data
  3972. options: options // property - options hash
  3973. , state: state // property - dimensions hash
  3974. // object pointers
  3975. , container: $N // property - object pointers for layout container
  3976. , panes: $Ps // property - object pointers for ALL Panes: panes.north, panes.center
  3977. , contents: $Cs // property - object pointers for ALL Content: contents.north, contents.center
  3978. , resizers: $Rs // property - object pointers for ALL Resizers, eg: resizers.north
  3979. , togglers: $Ts // property - object pointers for ALL Togglers, eg: togglers.north
  3980. // border-pane open/close
  3981. , hide: hide // method - ditto
  3982. , show: show // method - ditto
  3983. , toggle: toggle // method - pass a 'pane' ("north", "west", etc)
  3984. , open: open // method - ditto
  3985. , close: close // method - ditto
  3986. , slideOpen: slideOpen // method - ditto
  3987. , slideClose: slideClose // method - ditto
  3988. , slideToggle: slideToggle // method - ditto
  3989. // pane actions
  3990. , setSizeLimits: setSizeLimits // method - pass a 'pane' - update state min/max data
  3991. , _sizePane: sizePane // method -intended for user by plugins only!
  3992. , sizePane: manualSizePane // method - pass a 'pane' AND an 'outer-size' in pixels or percent, or 'auto'
  3993. , sizeContent: sizeContent // method - ditto
  3994. , swapPanes: swapPanes // method - pass TWO 'panes' - will swap them
  3995. // layout control
  3996. , createChildLayout: createChildLayout// method - pass a 'pane' and (optional) layout-options (OVERRIDES options[pane].childOptions
  3997. , destroy: destroy // method - no parameters
  3998. , addPane: addPane // method - pass a 'pane'
  3999. , removePane: removePane // method - pass a 'pane' to remove from layout, add 'true' to delete the pane-elem
  4000. , initPanes: isInitialized // method - no parameters
  4001. , initContent: initContent // method - ditto
  4002. , resizeAll: resizeAll // method - no parameters
  4003. , allowOverflow: allowOverflow // utility - pass calling element (this)
  4004. , resetOverflow: resetOverflow // utility - ditto
  4005. // special option setting
  4006. , enableClosable: enableClosable
  4007. , disableClosable: disableClosable
  4008. , enableSlidable: enableSlidable
  4009. , disableSlidable: disableSlidable
  4010. , enableResizable: enableResizable
  4011. , disableResizable: disableResizable
  4012. // event triggering
  4013. , trigger: trigger
  4014. // alias collections of options, state and children - created in addPane and extended elsewhere
  4015. , Layout: false
  4016. , children: children
  4017. , north: false
  4018. , south: false
  4019. , west: false
  4020. , east: false
  4021. , center: false
  4022. };
  4023. // create the border layout NOW
  4024. if (_create() === 'cancel') // onload_start callback returned false to CANCEL layout creation
  4025. return null;
  4026. else // true OR false -- if layout-elements did NOT init (hidden or do not exist), can auto-init later
  4027. return Instance; // return the Instance object
  4028. }
  4029. /**
  4030. * jquery.layout.state 1.0
  4031. * $Date: 2011-07-16 08:00:00 (Sat, 16 July 2011) $
  4032. *
  4033. * Copyright (c) 2010
  4034. * Kevin Dalman (http://allpro.net)
  4035. *
  4036. * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
  4037. * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
  4038. *
  4039. * @dependancies: UI Layout 1.3.0.rc30.1 or higher
  4040. * @dependancies: $.ui.cookie (above)
  4041. *
  4042. * @support: http://groups.google.com/group/jquery-ui-layout
  4043. */
  4044. /*
  4045. * State-management options stored in options.stateManagement, which includes a .cookie hash
  4046. * Default options saves ALL KEYS for ALL PANES, ie: pane.size, pane.isClosed, pane.isHidden
  4047. *
  4048. * // STATE/COOKIE OPTIONS
  4049. * @example $(el).layout({
  4050. stateManagement: {
  4051. enabled: true
  4052. , stateKeys: "east.size,west.size,east.isClosed,west.isClosed"
  4053. , cookie: { name: "appLayout", path: "/" }
  4054. }
  4055. })
  4056. * @example $(el).layout({ stateManagement__enabled: true }) // enable auto-state-management using cookies
  4057. * @example $(el).layout({ stateManagement__cookie: { name: "appLayout", path: "/" } })
  4058. * @example $(el).layout({ stateManagement__cookie__name: "appLayout", stateManagement__cookie__path: "/" })
  4059. *
  4060. * // STATE/COOKIE METHODS
  4061. * @example myLayout.saveCookie( "west.isClosed,north.size,south.isHidden", {expires: 7} );
  4062. * @example myLayout.loadCookie();
  4063. * @example myLayout.deleteCookie();
  4064. * @example var JSON = myLayout.readState(); // CURRENT Layout State
  4065. * @example var JSON = myLayout.readCookie(); // SAVED Layout State (from cookie)
  4066. * @example var JSON = myLayout.state.stateData; // LAST LOADED Layout State (cookie saved in layout.state hash)
  4067. *
  4068. * CUSTOM STATE-MANAGEMENT (eg, saved in a database)
  4069. * @example var JSON = myLayout.readState( "west.isClosed,north.size,south.isHidden" );
  4070. * @example myLayout.loadState( JSON );
  4071. */
  4072. /**
  4073. * UI COOKIE UTILITY
  4074. *
  4075. * A $.cookie OR $.ui.cookie namespace *should be standard*, but until then...
  4076. * This creates $.ui.cookie so Layout does not need the cookie.jquery.js plugin
  4077. * NOTE: This utility is REQUIRED by the layout.state plugin
  4078. *
  4079. * Cookie methods in Layout are created as part of State Management
  4080. */
  4081. if (!$.ui) $.ui = {};
  4082. $.ui.cookie = {
  4083. // TODO: is the cookieEnabled property fully cross-browser???
  4084. acceptsCookies: !!navigator.cookieEnabled
  4085. , read: function (name) {
  4086. var
  4087. c = document.cookie
  4088. , cs = c ? c.split(';') : []
  4089. , pair // loop var
  4090. ;
  4091. for (var i=0, n=cs.length; i < n; i++) {
  4092. pair = $.trim(cs[i]).split('='); // name=value pair
  4093. if (pair[0] == name) // found the layout cookie
  4094. return decodeURIComponent(pair[1]);
  4095. }
  4096. return null;
  4097. }
  4098. , write: function (name, val, cookieOpts) {
  4099. var
  4100. params = ''
  4101. , date = ''
  4102. , clear = false
  4103. , o = cookieOpts || {}
  4104. , x = o.expires
  4105. ;
  4106. if (x && x.toUTCString)
  4107. date = x;
  4108. else if (x === null || typeof x === 'number') {
  4109. date = new Date();
  4110. if (x > 0)
  4111. date.setDate(date.getDate() + x);
  4112. else {
  4113. date.setFullYear(1970);
  4114. clear = true;
  4115. }
  4116. }
  4117. if (date) params += ';expires='+ date.toUTCString();
  4118. if (o.path) params += ';path='+ o.path;
  4119. if (o.domain) params += ';domain='+ o.domain;
  4120. if (o.secure) params += ';secure';
  4121. document.cookie = name +'='+ (clear ? "" : encodeURIComponent( val )) + params; // write or clear cookie
  4122. }
  4123. , clear: function (name) {
  4124. $.ui.cookie.write(name, '', {expires: -1});
  4125. }
  4126. };
  4127. // if cookie.jquery.js is not loaded, create an alias to replicate it
  4128. // this may be useful to other plugins or code dependent on that plugin
  4129. if (!$.cookie) $.cookie = function (k, v, o) {
  4130. var C = $.ui.cookie;
  4131. if (v === null)
  4132. C.clear(k);
  4133. else if (v === undefined)
  4134. return C.read(k);
  4135. else
  4136. C.write(k, v, o);
  4137. };
  4138. // tell Layout that the state plugin is available
  4139. $.layout.plugins.stateManagement = true;
  4140. // Add State-Management options to layout.defaults
  4141. $.layout.config.optionRootKeys.push("stateManagement");
  4142. $.layout.defaults.stateManagement = {
  4143. enabled: false // true = enable state-management, even if not using cookies
  4144. , autoSave: true // Save a state-cookie when page exits?
  4145. , autoLoad: true // Load the state-cookie when Layout inits?
  4146. // List state-data to save - must be pane-specific
  4147. , stateKeys: "north.size,south.size,east.size,west.size,"+
  4148. "north.isClosed,south.isClosed,east.isClosed,west.isClosed,"+
  4149. "north.isHidden,south.isHidden,east.isHidden,west.isHidden"
  4150. , cookie: {
  4151. name: "" // If not specified, will use Layout.name, else just "Layout"
  4152. , domain: "" // blank = current domain
  4153. , path: "" // blank = current page, '/' = entire website
  4154. , expires: "" // 'days' to keep cookie - leave blank for 'session cookie'
  4155. , secure: false
  4156. }
  4157. };
  4158. // Set stateManagement as a layout-option, NOT a pane-option
  4159. $.layout.optionsMap.layout.push("stateManagement");
  4160. /*
  4161. * State Management methods
  4162. */
  4163. $.layout.state = {
  4164. /**
  4165. * Get the current layout state and save it to a cookie
  4166. *
  4167. * myLayout.saveCookie( keys, cookieOpts )
  4168. *
  4169. * @param {Object} inst
  4170. * @param {(string|Array)=} keys
  4171. * @param {Object=} opts
  4172. */
  4173. saveCookie: function (inst, keys, cookieOpts) {
  4174. var o = inst.options
  4175. , oS = o.stateManagement
  4176. , oC = $.extend(true, {}, oS.cookie, cookieOpts || null)
  4177. , data = inst.state.stateData = inst.readState( keys || oS.stateKeys ) // read current panes-state
  4178. ;
  4179. $.ui.cookie.write( oC.name || o.name || "Layout", $.layout.state.encodeJSON(data), oC );
  4180. return $.extend(true, {}, data); // return COPY of state.stateData data
  4181. }
  4182. /**
  4183. * Remove the state cookie
  4184. *
  4185. * @param {Object} inst
  4186. */
  4187. , deleteCookie: function (inst) {
  4188. var o = inst.options;
  4189. $.ui.cookie.clear( o.stateManagement.cookie.name || o.name || "Layout" );
  4190. }
  4191. /**
  4192. * Read & return data from the cookie - as JSON
  4193. *
  4194. * @param {Object} inst
  4195. */
  4196. , readCookie: function (inst) {
  4197. var o = inst.options;
  4198. var c = $.ui.cookie.read( o.stateManagement.cookie.name || o.name || "Layout" );
  4199. // convert cookie string back to a hash and return it
  4200. return c ? $.layout.state.decodeJSON(c) : {};
  4201. }
  4202. /**
  4203. * Get data from the cookie and USE IT to loadState
  4204. *
  4205. * @param {Object} inst
  4206. */
  4207. , loadCookie: function (inst) {
  4208. var c = $.layout.state.readCookie(inst); // READ the cookie
  4209. if (c) {
  4210. inst.state.stateData = $.extend(true, {}, c); // SET state.stateData
  4211. inst.loadState(c); // LOAD the retrieved state
  4212. }
  4213. return c;
  4214. }
  4215. /**
  4216. * Update layout options from the cookie, if one exists
  4217. *
  4218. * @param {Object} inst
  4219. * @param {Object=} stateData
  4220. * @param {boolean=} animate
  4221. */
  4222. , loadState: function (inst, stateData, animate) {
  4223. stateData = $.layout.transformData( stateData ); // panes = default subkey
  4224. if ($.isEmptyObject( stateData )) return;
  4225. $.extend(true, inst.options, stateData); // update layout options
  4226. // if layout has already been initialized, then UPDATE layout state
  4227. if (inst.state.initialized) {
  4228. var pane, vis, o, s, h, c
  4229. , noAnimate = (animate===false)
  4230. ;
  4231. $.each($.layout.config.borderPanes, function (idx, pane) {
  4232. state = inst.state[pane];
  4233. o = stateData[ pane ];
  4234. if (typeof o != 'object') return; // no key, continue
  4235. s = o.size;
  4236. c = o.initClosed;
  4237. h = o.initHidden;
  4238. vis = state.isVisible;
  4239. // resize BEFORE opening
  4240. // resize BEFORE opening
  4241. if (!vis)
  4242. inst.sizePane(pane, s, false, false);
  4243. if (h === true) inst.hide(pane, noAnimate);
  4244. else if (c === false) inst.open (pane, false, noAnimate);
  4245. else if (c === true) inst.close(pane, false, noAnimate);
  4246. else if (h === false) inst.show (pane, false, noAnimate);
  4247. // resize AFTER any other actions
  4248. if (vis)
  4249. inst.sizePane(pane, s, false, noAnimate); // animate resize if option passed
  4250. });
  4251. };
  4252. }
  4253. /**
  4254. * Get the *current layout state* and return it as a hash
  4255. *
  4256. * @param {Object=} inst
  4257. * @param {(string|Array)=} keys
  4258. */
  4259. , readState: function (inst, keys) {
  4260. var
  4261. data = {}
  4262. , alt = { isClosed: 'initClosed', isHidden: 'initHidden' }
  4263. , state = inst.state
  4264. , panes = $.layout.config.allPanes
  4265. , pair, pane, key, val
  4266. ;
  4267. if (!keys) keys = inst.options.stateManagement.stateKeys; // if called by user
  4268. if ($.isArray(keys)) keys = keys.join(",");
  4269. // convert keys to an array and change delimiters from '__' to '.'
  4270. keys = keys.replace(/__/g, ".").split(',');
  4271. // loop keys and create a data hash
  4272. for (var i=0, n=keys.length; i < n; i++) {
  4273. pair = keys[i].split(".");
  4274. pane = pair[0];
  4275. key = pair[1];
  4276. if ($.inArray(pane, panes) < 0) continue; // bad pane!
  4277. val = state[ pane ][ key ];
  4278. if (val == undefined) continue;
  4279. if (key=="isClosed" && state[pane]["isSliding"])
  4280. val = true; // if sliding, then *really* isClosed
  4281. ( data[pane] || (data[pane]={}) )[ alt[key] ? alt[key] : key ] = val;
  4282. }
  4283. return data;
  4284. }
  4285. /**
  4286. * Stringify a JSON hash so can save in a cookie or db-field
  4287. */
  4288. , encodeJSON: function (JSON) {
  4289. return parse(JSON);
  4290. function parse (h) {
  4291. var D=[], i=0, k, v, t; // k = key, v = value
  4292. for (k in h) {
  4293. v = h[k];
  4294. t = typeof v;
  4295. if (t == 'string') // STRING - add quotes
  4296. v = '"'+ v +'"';
  4297. else if (t == 'object') // SUB-KEY - recurse into it
  4298. v = parse(v);
  4299. D[i++] = '"'+ k +'":'+ v;
  4300. }
  4301. return '{'+ D.join(',') +'}';
  4302. };
  4303. }
  4304. /**
  4305. * Convert stringified JSON back to a hash object
  4306. * @see $.parseJSON(), adding in jQuery 1.4.1
  4307. */
  4308. , decodeJSON: function (str) {
  4309. try { return $.parseJSON ? $.parseJSON(str) : window["eval"]("("+ str +")") || {}; }
  4310. catch (e) { return {}; }
  4311. }
  4312. , _create: function (inst) {
  4313. var _ = $.layout.state;
  4314. // ADD State-Management plugin methods to inst
  4315. $.extend( inst, {
  4316. // readCookie - update options from cookie - returns hash of cookie data
  4317. readCookie: function () { return _.readCookie(inst); }
  4318. // deleteCookie
  4319. , deleteCookie: function () { _.deleteCookie(inst); }
  4320. // saveCookie - optionally pass keys-list and cookie-options (hash)
  4321. , saveCookie: function (keys, cookieOpts) { return _.saveCookie(inst, keys, cookieOpts); }
  4322. // loadCookie - readCookie and use to loadState() - returns hash of cookie data
  4323. , loadCookie: function () { return _.loadCookie(inst); }
  4324. // loadState - pass a hash of state to use to update options
  4325. , loadState: function (stateData, animate) { _.loadState(inst, stateData, animate); }
  4326. // readState - returns hash of current layout-state
  4327. , readState: function (keys) { return _.readState(inst, keys); }
  4328. // add JSON utility methods too...
  4329. , encodeJSON: _.encodeJSON
  4330. , decodeJSON: _.decodeJSON
  4331. });
  4332. // init state.stateData key, even if plugin is initially disabled
  4333. inst.state.stateData = {};
  4334. // read and load cookie-data per options
  4335. var oS = inst.options.stateManagement;
  4336. if (oS.enabled) {
  4337. if (oS.autoLoad) // update the options from the cookie
  4338. inst.loadCookie();
  4339. else // don't modify options - just store cookie data in state.stateData
  4340. inst.state.stateData = inst.readCookie();
  4341. }
  4342. }
  4343. , _unload: function (inst) {
  4344. var oS = inst.options.stateManagement;
  4345. if (oS.enabled) {
  4346. if (oS.autoSave) // save a state-cookie automatically
  4347. inst.saveCookie();
  4348. else // don't save a cookie, but do store state-data in state.stateData key
  4349. inst.state.stateData = inst.readState();
  4350. }
  4351. }
  4352. };
  4353. // add state initialization method to Layout's onCreate array of functions
  4354. $.layout.onCreate.push( $.layout.state._create );
  4355. $.layout.onUnload.push( $.layout.state._unload );
  4356. /**
  4357. * jquery.layout.buttons 1.0
  4358. * $Date: 2011-07-16 08:00:00 (Sat, 16 July 2011) $
  4359. *
  4360. * Copyright (c) 2010
  4361. * Kevin Dalman (http://allpro.net)
  4362. *
  4363. * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
  4364. * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
  4365. *
  4366. * @dependancies: UI Layout 1.3.0.rc30.1 or higher
  4367. *
  4368. * @support: http://groups.google.com/group/jquery-ui-layout
  4369. *
  4370. * Docs: [ to come ]
  4371. * Tips: [ to come ]
  4372. */
  4373. // tell Layout that the state plugin is available
  4374. $.layout.plugins.buttons = true;
  4375. // Add buttons options to layout.defaults
  4376. $.layout.defaults.autoBindCustomButtons = false;
  4377. // Specify autoBindCustomButtons as a layout-option, NOT a pane-option
  4378. $.layout.optionsMap.layout.push("autoBindCustomButtons");
  4379. var lang = $.layout.language;
  4380. /*
  4381. * Button methods
  4382. */
  4383. $.layout.buttons = {
  4384. /**
  4385. * Searches for .ui-layout-button-xxx elements and auto-binds them as layout-buttons
  4386. *
  4387. * @see _create()
  4388. *
  4389. * @param {Object} inst Layout Instance object
  4390. */
  4391. init: function (inst) {
  4392. var pre = "ui-layout-button-"
  4393. , layout = inst.options.name || ""
  4394. , name;
  4395. $.each("toggle,open,close,pin,toggle-slide,open-slide".split(","), function (i, action) {
  4396. $.each($.layout.config.borderPanes, function (ii, pane) {
  4397. $("."+pre+action+"-"+pane).each(function(){
  4398. // if button was previously 'bound', data.layoutName was set, but is blank if layout has no 'name'
  4399. name = $(this).data("layoutName") || $(this).attr("layoutName");
  4400. if (name == undefined || name === layout)
  4401. inst.bindButton(this, action, pane);
  4402. });
  4403. });
  4404. });
  4405. }
  4406. /**
  4407. * Helper function to validate params received by addButton utilities
  4408. *
  4409. * Two classes are added to the element, based on the buttonClass...
  4410. * The type of button is appended to create the 2nd className:
  4411. * - ui-layout-button-pin // action btnClass
  4412. * - ui-layout-button-pin-west // action btnClass + pane
  4413. * - ui-layout-button-toggle
  4414. * - ui-layout-button-open
  4415. * - ui-layout-button-close
  4416. *
  4417. * @param {Object} inst Layout Instance object
  4418. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  4419. * @param {string} pane Name of the pane the button is for: 'north', 'south', etc.
  4420. *
  4421. * @return {Array.<Object>} If both params valid, the element matching 'selector' in a jQuery wrapper - otherwise returns null
  4422. */
  4423. , get: function (inst, selector, pane, action) {
  4424. var $E = $(selector)
  4425. , o = inst.options
  4426. , err = o.showErrorMessages
  4427. ;
  4428. if (!$E.length) { // element not found
  4429. if (err) $.layout.msg(lang.errButton + lang.selector +": "+ selector, true);
  4430. }
  4431. else if ($.inArray(pane, $.layout.config.borderPanes) < 0) { // invalid 'pane' sepecified
  4432. if (err) $.layout.msg(lang.errButton + lang.pane +": "+ pane, true);
  4433. $E = $(""); // NO BUTTON
  4434. }
  4435. else { // VALID
  4436. var btn = o[pane].buttonClass +"-"+ action;
  4437. $E .addClass( btn +" "+ btn +"-"+ pane )
  4438. .data("layoutName", o.name); // add layout identifier - even if blank!
  4439. }
  4440. return $E;
  4441. }
  4442. /**
  4443. * NEW syntax for binding layout-buttons - will eventually replace addToggle, addOpen, etc.
  4444. *
  4445. * @param {Object} inst Layout Instance object
  4446. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  4447. * @param {string} action
  4448. * @param {string} pane
  4449. */
  4450. , bind: function (inst, selector, action, pane) {
  4451. var _ = $.layout.buttons;
  4452. switch (action.toLowerCase()) {
  4453. case "toggle": _.addToggle (inst, selector, pane); break;
  4454. case "open": _.addOpen (inst, selector, pane); break;
  4455. case "close": _.addClose (inst, selector, pane); break;
  4456. case "pin": _.addPin (inst, selector, pane); break;
  4457. case "toggle-slide": _.addToggle (inst, selector, pane, true); break;
  4458. case "open-slide": _.addOpen (inst, selector, pane, true); break;
  4459. }
  4460. return inst;
  4461. }
  4462. /**
  4463. * Add a custom Toggler button for a pane
  4464. *
  4465. * @param {Object} inst Layout Instance object
  4466. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  4467. * @param {string} pane Name of the pane the button is for: 'north', 'south', etc.
  4468. * @param {boolean=} slide true = slide-open, false = pin-open
  4469. */
  4470. , addToggle: function (inst, selector, pane, slide) {
  4471. $.layout.buttons.get(inst, selector, pane, "toggle")
  4472. .click(function(evt){
  4473. inst.toggle(pane, !!slide);
  4474. evt.stopPropagation();
  4475. });
  4476. return inst;
  4477. }
  4478. /**
  4479. * Add a custom Open button for a pane
  4480. *
  4481. * @param {Object} inst Layout Instance object
  4482. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  4483. * @param {string} pane Name of the pane the button is for: 'north', 'south', etc.
  4484. * @param {boolean=} slide true = slide-open, false = pin-open
  4485. */
  4486. , addOpen: function (inst, selector, pane, slide) {
  4487. $.layout.buttons.get(inst, selector, pane, "open")
  4488. .attr("title", lang.Open)
  4489. .click(function (evt) {
  4490. inst.open(pane, !!slide);
  4491. evt.stopPropagation();
  4492. });
  4493. return inst;
  4494. }
  4495. /**
  4496. * Add a custom Close button for a pane
  4497. *
  4498. * @param {Object} inst Layout Instance object
  4499. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  4500. * @param {string} pane Name of the pane the button is for: 'north', 'south', etc.
  4501. */
  4502. , addClose: function (inst, selector, pane) {
  4503. $.layout.buttons.get(inst, selector, pane, "close")
  4504. .attr("title", lang.Close)
  4505. .click(function (evt) {
  4506. inst.close(pane);
  4507. evt.stopPropagation();
  4508. });
  4509. return inst;
  4510. }
  4511. /**
  4512. * Add a custom Pin button for a pane
  4513. *
  4514. * Four classes are added to the element, based on the paneClass for the associated pane...
  4515. * Assuming the default paneClass and the pin is 'up', these classes are added for a west-pane pin:
  4516. * - ui-layout-pane-pin
  4517. * - ui-layout-pane-west-pin
  4518. * - ui-layout-pane-pin-up
  4519. * - ui-layout-pane-west-pin-up
  4520. *
  4521. * @param {Object} inst Layout Instance object
  4522. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  4523. * @param {string} pane Name of the pane the pin is for: 'north', 'south', etc.
  4524. */
  4525. , addPin: function (inst, selector, pane) {
  4526. var _ = $.layout.buttons
  4527. , $E = _.get(inst, selector, pane, "pin");
  4528. if ($E.length) {
  4529. var s = inst.state[pane];
  4530. $E.click(function (evt) {
  4531. _.setPinState(inst, $(this), pane, (s.isSliding || s.isClosed));
  4532. if (s.isSliding || s.isClosed) inst.open( pane ); // change from sliding to open
  4533. else inst.close( pane ); // slide-closed
  4534. evt.stopPropagation();
  4535. });
  4536. // add up/down pin attributes and classes
  4537. _.setPinState(inst, $E, pane, (!s.isClosed && !s.isSliding));
  4538. // add this pin to the pane data so we can 'sync it' automatically
  4539. // PANE.pins key is an array so we can store multiple pins for each pane
  4540. s.pins.push( selector ); // just save the selector string
  4541. }
  4542. return inst;
  4543. }
  4544. /**
  4545. * Change the class of the pin button to make it look 'up' or 'down'
  4546. *
  4547. * @see addPin(), syncPins()
  4548. *
  4549. * @param {Object} inst Layout Instance object
  4550. * @param {Array.<Object>} $Pin The pin-span element in a jQuery wrapper
  4551. * @param {string} pane These are the params returned to callbacks by layout()
  4552. * @param {boolean} doPin true = set the pin 'down', false = set it 'up'
  4553. */
  4554. , setPinState: function (inst, $Pin, pane, doPin) {
  4555. var updown = $Pin.attr("pin");
  4556. if (updown && doPin === (updown=="down")) return; // already in correct state
  4557. var
  4558. pin = inst.options[pane].buttonClass +"-pin"
  4559. , side = pin +"-"+ pane
  4560. , UP = pin +"-up "+ side +"-up"
  4561. , DN = pin +"-down "+side +"-down"
  4562. ;
  4563. $Pin
  4564. .attr("pin", doPin ? "down" : "up") // logic
  4565. .attr("title", doPin ? lang.Unpin : lang.Pin)
  4566. .removeClass( doPin ? UP : DN )
  4567. .addClass( doPin ? DN : UP )
  4568. ;
  4569. }
  4570. /**
  4571. * INTERNAL function to sync 'pin buttons' when pane is opened or closed
  4572. * Unpinned means the pane is 'sliding' - ie, over-top of the adjacent panes
  4573. *
  4574. * @see open(), close()
  4575. *
  4576. * @param {Object} inst Layout Instance object
  4577. * @param {string} pane These are the params returned to callbacks by layout()
  4578. * @param {boolean} doPin True means set the pin 'down', False means 'up'
  4579. */
  4580. , syncPinBtns: function (inst, pane, doPin) {
  4581. // REAL METHOD IS _INSIDE_ LAYOUT - THIS IS HERE JUST FOR REFERENCE
  4582. $.each(state[pane].pins, function (i, selector) {
  4583. $.layout.buttons.setPinState(inst, $(selector), pane, doPin);
  4584. });
  4585. }
  4586. , _load: function (inst) {
  4587. var _ = $.layout.buttons;
  4588. // ADD Button methods to Layout Instance
  4589. // Note: sel = jQuery Selector string
  4590. $.extend( inst, {
  4591. bindButton: function (sel, action, pane) { return _.bind(inst, sel, action, pane); }
  4592. // DEPRECATED METHODS
  4593. , addToggleBtn: function (sel, pane, slide) { return _.addToggle(inst, sel, pane, slide); }
  4594. , addOpenBtn: function (sel, pane, slide) { return _.addOpen(inst, sel, pane, slide); }
  4595. , addCloseBtn: function (sel, pane) { return _.addClose(inst, sel, pane); }
  4596. , addPinBtn: function (sel, pane) { return _.addPin(inst, sel, pane); }
  4597. });
  4598. // init state array to hold pin-buttons
  4599. for (var i=0; i<4; i++) {
  4600. var pane = $.layout.config.borderPanes[i];
  4601. inst.state[pane].pins = [];
  4602. }
  4603. // auto-init buttons onLoad if option is enabled
  4604. if ( inst.options.autoBindCustomButtons )
  4605. _.init(inst);
  4606. }
  4607. , _unload: function (inst) {
  4608. // TODO: unbind all buttons???
  4609. }
  4610. };
  4611. // add initialization method to Layout's onLoad array of functions
  4612. $.layout.onLoad.push( $.layout.buttons._load );
  4613. //$.layout.onUnload.push( $.layout.buttons._unload );
  4614. /**
  4615. * jquery.layout.browserZoom 1.0
  4616. * $Date: 2011-12-29 08:00:00 (Thu, 29 Dec 2011) $
  4617. *
  4618. * Copyright (c) 2012
  4619. * Kevin Dalman (http://allpro.net)
  4620. *
  4621. * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
  4622. * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
  4623. *
  4624. * @dependancies: UI Layout 1.3.0.rc30.1 or higher
  4625. *
  4626. * @support: http://groups.google.com/group/jquery-ui-layout
  4627. *
  4628. * @todo: Extend logic to handle other problematic zooming in browsers
  4629. * @todo: Add hotkey/mousewheel bindings to _instantly_ respond to these zoom event
  4630. */
  4631. // tell Layout that the plugin is available
  4632. $.layout.plugins.browserZoom = true;
  4633. $.layout.defaults.browserZoomCheckInterval = 1000;
  4634. $.layout.optionsMap.layout.push("browserZoomCheckInterval");
  4635. /*
  4636. * browserZoom methods
  4637. */
  4638. $.layout.browserZoom = {
  4639. _init: function (inst) {
  4640. $.layout.browserZoom._setTimer(inst);
  4641. }
  4642. , _setTimer: function (inst) {
  4643. if (inst.destroyed) return;
  4644. var o = inst.options
  4645. , s = inst.state
  4646. , z = s.browserZoom = $.layout.browserZoom.ratio()
  4647. ;
  4648. if (o.resizeWithWindow && z !== false) {
  4649. setTimeout(function(){
  4650. if (inst.destroyed) return;
  4651. var d = $.layout.browserZoom.ratio();
  4652. if (d !== s.browserZoom) {
  4653. s.browserZoom = d;
  4654. inst.resizeAll();
  4655. }
  4656. $.layout.browserZoom._setTimer(inst); // set a NEW timeout
  4657. }, Math.max( o.browserZoomCheckInterval, 100 )); // MINIMUM 100ms interval, for performance
  4658. }
  4659. }
  4660. , ratio: function () {
  4661. var w = window
  4662. , s = screen
  4663. , d = document
  4664. , dE = d.documentElement || d.body
  4665. , b = $.layout.browser
  4666. , v = b.version
  4667. , r, sW, cW
  4668. ;
  4669. // we can ignore all browsers that fire window.resize event onZoom
  4670. if ((b.msie && v > 8)
  4671. || !b.msie
  4672. ) return false; // don't need to track zoom
  4673. if (s.deviceXDPI)
  4674. return calc(s.deviceXDPI, s.systemXDPI);
  4675. // everything below is just for future reference!
  4676. if (b.webkit && (r = d.body.getBoundingClientRect))
  4677. return calc((r.left - r.right), d.body.offsetWidth);
  4678. if (b.webkit && (sW = w.outerWidth))
  4679. return calc(sW, w.innerWidth);
  4680. if ((sW = s.width) && (cW = dE.clientWidth))
  4681. return calc(sW, cW);
  4682. return false; // no match, so cannot - or don't need to - track zoom
  4683. function calc (x,y) { return (parseInt(x,10) / parseInt(y,10) * 100).toFixed(); }
  4684. }
  4685. };
  4686. // add initialization method to Layout's onLoad array of functions
  4687. $.layout.onReady.push( $.layout.browserZoom._init );
  4688. })( jQuery );