jquery.layout.js 186 KB

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