jquery-layout.js 304 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442
  1. // (function(LST) {
  2. // LST.rethrow = false;
  3. // var currentTraceError = null;
  4. // var filename = new Error().stack.split("\n")[1].match(/^ at ((?:\w+:\/\/)?[^:]+)/)[1];
  5. // function filterInternalFrames(frames) {
  6. // return frames.split("\n").filter(function(frame) { return frame.indexOf(filename) < 0; }).join("\n");
  7. // }
  8. // Error.prepareStackTrace = function(error, structuredStackTrace) {
  9. // if (!error.__cachedTrace) {
  10. // error.__cachedTrace = filterInternalFrames(FormatStackTrace(error, structuredStackTrace));
  11. // if (!has.call(error, "__previous")) {
  12. // var previous = currentTraceError;
  13. // while (previous) {
  14. // var previousTrace = previous.stack;
  15. // error.__cachedTrace += "\n----------------------------------------\n" +
  16. // " at " + previous.__location + "\n" +
  17. // previousTrace.substring(previousTrace.indexOf("\n") + 1);
  18. // previous = previous.__previous;
  19. // }
  20. // }
  21. // }
  22. // return error.__cachedTrace;
  23. // }
  24. // var slice = Array.prototype.slice;
  25. // var has = Object.prototype.hasOwnProperty;
  26. // // Takes an object, a property name for the callback function to wrap, and an argument position
  27. // // and overwrites the function with a wrapper that captures the stack at the time of callback registration
  28. // function wrapRegistrationFunction(object, property, callbackArg) {
  29. // if (typeof object[property] !== "function") {
  30. // console.error("(long-stack-traces) Object", object, "does not contain function", property);
  31. // return;
  32. // }
  33. // if (!has.call(object, property)) {
  34. // console.warn("(long-stack-traces) Object", object, "does not directly contain function", property);
  35. // }
  36. // // TODO: better source position detection
  37. // var sourcePosition = (object.constructor.name || Object.prototype.toString.call(object)) + "." + property;
  38. // // capture the original registration function
  39. // var fn = object[property];
  40. // // overwrite it with a wrapped registration function that modifies the supplied callback argument
  41. // object[property] = function() {
  42. // // replace the callback argument with a wrapped version that captured the current stack trace
  43. // arguments[callbackArg] = makeWrappedCallback(arguments[callbackArg], sourcePosition);
  44. // // call the original registration function with the modified arguments
  45. // return fn.apply(this, arguments);
  46. // }
  47. // // check that the registration function was indeed overwritten
  48. // if (object[property] === fn)
  49. // console.warn("(long-stack-traces) Couldn't replace ", property, "on", object);
  50. // }
  51. // // Takes a callback function and name, and captures a stack trace, returning a new callback that restores the stack frame
  52. // // This function adds a single function call overhead during callback registration vs. inlining it in wrapRegistationFunction
  53. // function makeWrappedCallback(callback, frameLocation) {
  54. // // add a fake stack frame. we can't get a real one since we aren't inside the original function
  55. // var traceError = new Error();
  56. // traceError.__location = frameLocation;
  57. // traceError.__previous = currentTraceError;
  58. // return function() {
  59. // // if (currentTraceError) {
  60. // // FIXME: This shouldn't normally happen, but it often does. Do we actually need a stack instead?
  61. // // console.warn("(long-stack-traces) Internal Error: currentTrace already set.");
  62. // // }
  63. // // restore the trace
  64. // currentTraceError = traceError;
  65. // try {
  66. // return callback.apply(this, arguments);
  67. // } catch (e) {
  68. // console.error("Uncaught " + e.stack);
  69. // if (LST.rethrow)
  70. // throw ""; // TODO: throw the original error, or undefined?
  71. // } finally {
  72. // // clear the trace so we can check that none is set above.
  73. // // TODO: could we remove this for slightly better performace?
  74. // currentTraceError = null;
  75. // }
  76. // }
  77. // }
  78. // // Chrome
  79. // if (typeof window !== "undefined") {
  80. // wrapRegistrationFunction(window.constructor.prototype, "setTimeout", 0);
  81. // wrapRegistrationFunction(window.constructor.prototype, "setInterval", 0);
  82. // [
  83. // window.Node.prototype,
  84. // window.MessagePort.prototype,
  85. // window.SVGElementInstance.prototype,
  86. // window.WebSocket.prototype,
  87. // window.XMLHttpRequest.prototype,
  88. // window.EventSource.prototype,
  89. // window.XMLHttpRequestUpload.prototype,
  90. // window.SharedWorker.prototype.__proto__,
  91. // window.constructor.prototype,
  92. // window.applicationCache.constructor.prototype
  93. // ].forEach(function(object) {
  94. // wrapRegistrationFunction(object, "addEventListener", 1);
  95. // });
  96. // // this actually captures the stack when "send" is called, which isn't ideal,
  97. // // but it's the best we can do without hooking onreadystatechange assignments
  98. // var _send = XMLHttpRequest.prototype.send;
  99. // XMLHttpRequest.prototype.send = function() {
  100. // this.onreadystatechange = makeWrappedCallback(this.onreadystatechange, "onreadystatechange");
  101. // return _send.apply(this, arguments);
  102. // }
  103. // // FIXME: experimental XHR wrapper for hooking onreadystatechange
  104. // // Based on https://gist.github.com/796032
  105. // // var _XMLHttpRequest = XMLHttpRequest;
  106. // // XMLHttpRequest = function () {
  107. // // Object.defineProperty(this, "onreadystatechange", {
  108. // // get: function() {
  109. // // return this.__onreadystatechange;
  110. // // },
  111. // // set: function(onreadystatechange) {
  112. // // if (this.__onreadystatechange && typeof this.__onreadystatechange.call === "function")
  113. // // this.removeEventListener("readystatechange", this.__onreadystatechange);
  114. // // this.__onreadystatechange = makeWrappedCallback(onreadystatechange, "onreadystatechange");
  115. // // if (this.__onreadystatechange && typeof this.__onreadystatechange.call === "function")
  116. // // this.addEventListener("readystatechange", this.__onreadystatechange);
  117. // // },
  118. // // enumerable: true
  119. // // });
  120. // // Object.defineProperty(this, "__onreadystatechange", {
  121. // // value: null,
  122. // // writable: true,
  123. // // enumerable: false
  124. // // });
  125. // // }
  126. // // XMLHttpRequest.prototype = new _XMLHttpRequest();
  127. // }
  128. // // Node.js
  129. // else if (typeof process !== "undefined") {
  130. // LST.rethrow = true;
  131. // var global = (function() { return this; })();
  132. // wrapRegistrationFunction(global, "setTimeout", 0);
  133. // wrapRegistrationFunction(global, "setInterval", 0);
  134. // wrapRegistrationFunction(global, "setImmediate", 0);
  135. // var EventEmitter = require('events').EventEmitter;
  136. // wrapRegistrationFunction(EventEmitter.prototype, "addListener", 1);
  137. // wrapRegistrationFunction(EventEmitter.prototype, "on", 1);
  138. // wrapRegistrationFunction(process, "nextTick", 0);
  139. // }
  140. // // Copyright 2006-2008 the V8 project authors. All rights reserved.
  141. // // Redistribution and use in source and binary forms, with or without
  142. // // modification, are permitted provided that the following conditions are
  143. // // met:
  144. // //
  145. // // * Redistributions of source code must retain the above copyright
  146. // // notice, this list of conditions and the following disclaimer.
  147. // // * Redistributions in binary form must reproduce the above
  148. // // copyright notice, this list of conditions and the following
  149. // // disclaimer in the documentation and/or other materials provided
  150. // // with the distribution.
  151. // // * Neither the name of Google Inc. nor the names of its
  152. // // contributors may be used to endorse or promote products derived
  153. // // from this software without specific prior written permission.
  154. // //
  155. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  156. // // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  157. // // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  158. // // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  159. // // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  160. // // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  161. // // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  162. // // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  163. // // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  164. // // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  165. // // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  166. // function FormatStackTrace(error, frames) {
  167. // var lines = [];
  168. // try {
  169. // lines.push(error.toString());
  170. // } catch (e) {
  171. // try {
  172. // lines.push("<error: " + e + ">");
  173. // } catch (ee) {
  174. // lines.push("<error>");
  175. // }
  176. // }
  177. // for (var i = 0; i < frames.length; i++) {
  178. // var frame = frames[i];
  179. // var line;
  180. // try {
  181. // line = FormatSourcePosition(frame);
  182. // } catch (e) {
  183. // try {
  184. // line = "<error: " + e + ">";
  185. // } catch (ee) {
  186. // // Any code that reaches this point is seriously nasty!
  187. // line = "<error>";
  188. // }
  189. // }
  190. // lines.push(" at " + line);
  191. // }
  192. // return lines.join("\n");
  193. // }
  194. // function FormatSourcePosition(frame) {
  195. // var fileLocation = "";
  196. // if (frame.isNative()) {
  197. // fileLocation = "native";
  198. // } else if (frame.isEval()) {
  199. // fileLocation = "eval at " + frame.getEvalOrigin();
  200. // } else {
  201. // var fileName = frame.getFileName();
  202. // if (fileName) {
  203. // fileLocation += fileName;
  204. // var lineNumber = frame.getLineNumber();
  205. // if (lineNumber != null) {
  206. // fileLocation += ":" + lineNumber;
  207. // var columnNumber = frame.getColumnNumber();
  208. // if (columnNumber) {
  209. // fileLocation += ":" + columnNumber;
  210. // }
  211. // }
  212. // }
  213. // }
  214. // if (!fileLocation) {
  215. // fileLocation = "unknown source";
  216. // }
  217. // var line = "";
  218. // var functionName = frame.getFunction().name;
  219. // var addPrefix = true;
  220. // var isConstructor = frame.isConstructor();
  221. // var isMethodCall = !(frame.isToplevel() || isConstructor);
  222. // if (isMethodCall) {
  223. // var methodName = frame.getMethodName();
  224. // line += frame.getTypeName() + ".";
  225. // if (functionName) {
  226. // line += functionName;
  227. // if (methodName && (methodName != functionName)) {
  228. // line += " [as " + methodName + "]";
  229. // }
  230. // } else {
  231. // line += methodName || "<anonymous>";
  232. // }
  233. // } else if (isConstructor) {
  234. // line += "new " + (functionName || "<anonymous>");
  235. // } else if (functionName) {
  236. // line += functionName;
  237. // } else {
  238. // line += fileLocation;
  239. // addPrefix = false;
  240. // }
  241. // if (addPrefix) {
  242. // line += " (" + fileLocation + ")";
  243. // }
  244. // return line;
  245. // }
  246. // })(typeof exports !== "undefined" ? exports : {});
  247. /*! jQuery UI - v1.10.3 - 2013-10-24
  248. * http://jqueryui.com
  249. * Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js
  250. * Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */
  251. (function( $, undefined ) {
  252. var uuid = 0,
  253. runiqueId = /^ui-id-\d+$/;
  254. // $.ui might exist from components with no dependencies, e.g., $.ui.position
  255. $.ui = $.ui || {};
  256. $.extend( $.ui, {
  257. version: "1.10.3",
  258. keyCode: {
  259. BACKSPACE: 8,
  260. COMMA: 188,
  261. DELETE: 46,
  262. DOWN: 40,
  263. END: 35,
  264. ENTER: 13,
  265. ESCAPE: 27,
  266. HOME: 36,
  267. LEFT: 37,
  268. NUMPAD_ADD: 107,
  269. NUMPAD_DECIMAL: 110,
  270. NUMPAD_DIVIDE: 111,
  271. NUMPAD_ENTER: 108,
  272. NUMPAD_MULTIPLY: 106,
  273. NUMPAD_SUBTRACT: 109,
  274. PAGE_DOWN: 34,
  275. PAGE_UP: 33,
  276. PERIOD: 190,
  277. RIGHT: 39,
  278. SPACE: 32,
  279. TAB: 9,
  280. UP: 38
  281. }
  282. });
  283. // plugins
  284. $.fn.extend({
  285. focus: (function( orig ) {
  286. return function( delay, fn ) {
  287. return typeof delay === "number" ?
  288. this.each(function() {
  289. var elem = this;
  290. setTimeout(function() {
  291. $( elem ).focus();
  292. if ( fn ) {
  293. fn.call( elem );
  294. }
  295. }, delay );
  296. }) :
  297. orig.apply( this, arguments );
  298. };
  299. })( $.fn.focus ),
  300. scrollParent: function() {
  301. var scrollParent;
  302. if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) {
  303. scrollParent = this.parents().filter(function() {
  304. return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
  305. }).eq(0);
  306. } else {
  307. scrollParent = this.parents().filter(function() {
  308. return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
  309. }).eq(0);
  310. }
  311. return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent;
  312. },
  313. zIndex: function( zIndex ) {
  314. if ( zIndex !== undefined ) {
  315. return this.css( "zIndex", zIndex );
  316. }
  317. if ( this.length ) {
  318. var elem = $( this[ 0 ] ), position, value;
  319. while ( elem.length && elem[ 0 ] !== document ) {
  320. // Ignore z-index if position is set to a value where z-index is ignored by the browser
  321. // This makes behavior of this function consistent across browsers
  322. // WebKit always returns auto if the element is positioned
  323. position = elem.css( "position" );
  324. if ( position === "absolute" || position === "relative" || position === "fixed" ) {
  325. // IE returns 0 when zIndex is not specified
  326. // other browsers return a string
  327. // we ignore the case of nested elements with an explicit value of 0
  328. // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
  329. value = parseInt( elem.css( "zIndex" ), 10 );
  330. if ( !isNaN( value ) && value !== 0 ) {
  331. return value;
  332. }
  333. }
  334. elem = elem.parent();
  335. }
  336. }
  337. return 0;
  338. },
  339. uniqueId: function() {
  340. return this.each(function() {
  341. if ( !this.id ) {
  342. this.id = "ui-id-" + (++uuid);
  343. }
  344. });
  345. },
  346. removeUniqueId: function() {
  347. return this.each(function() {
  348. if ( runiqueId.test( this.id ) ) {
  349. $( this ).removeAttr( "id" );
  350. }
  351. });
  352. }
  353. });
  354. // selectors
  355. function focusable( element, isTabIndexNotNaN ) {
  356. var map, mapName, img,
  357. nodeName = element.nodeName.toLowerCase();
  358. if ( "area" === nodeName ) {
  359. map = element.parentNode;
  360. mapName = map.name;
  361. if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
  362. return false;
  363. }
  364. img = $( "img[usemap=#" + mapName + "]" )[0];
  365. return !!img && visible( img );
  366. }
  367. return ( /input|select|textarea|button|object/.test( nodeName ) ?
  368. !element.disabled :
  369. "a" === nodeName ?
  370. element.href || isTabIndexNotNaN :
  371. isTabIndexNotNaN) &&
  372. // the element and all of its ancestors must be visible
  373. visible( element );
  374. }
  375. function visible( element ) {
  376. return $.expr.filters.visible( element ) &&
  377. !$( element ).parents().addBack().filter(function() {
  378. return $.css( this, "visibility" ) === "hidden";
  379. }).length;
  380. }
  381. $.extend( $.expr[ ":" ], {
  382. data: $.expr.createPseudo ?
  383. $.expr.createPseudo(function( dataName ) {
  384. return function( elem ) {
  385. return !!$.data( elem, dataName );
  386. };
  387. }) :
  388. // support: jQuery <1.8
  389. function( elem, i, match ) {
  390. return !!$.data( elem, match[ 3 ] );
  391. },
  392. focusable: function( element ) {
  393. return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
  394. },
  395. tabbable: function( element ) {
  396. var tabIndex = $.attr( element, "tabindex" ),
  397. isTabIndexNaN = isNaN( tabIndex );
  398. return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
  399. }
  400. });
  401. // support: jQuery <1.8
  402. if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
  403. $.each( [ "Width", "Height" ], function( i, name ) {
  404. var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
  405. type = name.toLowerCase(),
  406. orig = {
  407. innerWidth: $.fn.innerWidth,
  408. innerHeight: $.fn.innerHeight,
  409. outerWidth: $.fn.outerWidth,
  410. outerHeight: $.fn.outerHeight
  411. };
  412. function reduce( elem, size, border, margin ) {
  413. $.each( side, function() {
  414. size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
  415. if ( border ) {
  416. size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
  417. }
  418. if ( margin ) {
  419. size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
  420. }
  421. });
  422. return size;
  423. }
  424. $.fn[ "inner" + name ] = function( size ) {
  425. if ( size === undefined ) {
  426. return orig[ "inner" + name ].call( this );
  427. }
  428. return this.each(function() {
  429. $( this ).css( type, reduce( this, size ) + "px" );
  430. });
  431. };
  432. $.fn[ "outer" + name] = function( size, margin ) {
  433. if ( typeof size !== "number" ) {
  434. return orig[ "outer" + name ].call( this, size );
  435. }
  436. return this.each(function() {
  437. $( this).css( type, reduce( this, size, true, margin ) + "px" );
  438. });
  439. };
  440. });
  441. }
  442. // support: jQuery <1.8
  443. if ( !$.fn.addBack ) {
  444. $.fn.addBack = function( selector ) {
  445. return this.add( selector == null ?
  446. this.prevObject : this.prevObject.filter( selector )
  447. );
  448. };
  449. }
  450. // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
  451. if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
  452. $.fn.removeData = (function( removeData ) {
  453. return function( key ) {
  454. if ( arguments.length ) {
  455. return removeData.call( this, $.camelCase( key ) );
  456. } else {
  457. return removeData.call( this );
  458. }
  459. };
  460. })( $.fn.removeData );
  461. }
  462. // deprecated
  463. $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
  464. $.support.selectstart = "onselectstart" in document.createElement( "div" );
  465. $.fn.extend({
  466. disableSelection: function() {
  467. return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
  468. ".ui-disableSelection", function( event ) {
  469. event.preventDefault();
  470. });
  471. },
  472. enableSelection: function() {
  473. return this.unbind( ".ui-disableSelection" );
  474. }
  475. });
  476. $.extend( $.ui, {
  477. // $.ui.plugin is deprecated. Use $.widget() extensions instead.
  478. plugin: {
  479. add: function( module, option, set ) {
  480. var i,
  481. proto = $.ui[ module ].prototype;
  482. for ( i in set ) {
  483. proto.plugins[ i ] = proto.plugins[ i ] || [];
  484. proto.plugins[ i ].push( [ option, set[ i ] ] );
  485. }
  486. },
  487. call: function( instance, name, args ) {
  488. var i,
  489. set = instance.plugins[ name ];
  490. if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
  491. return;
  492. }
  493. for ( i = 0; i < set.length; i++ ) {
  494. if ( instance.options[ set[ i ][ 0 ] ] ) {
  495. set[ i ][ 1 ].apply( instance.element, args );
  496. }
  497. }
  498. }
  499. },
  500. // only used by resizable
  501. hasScroll: function( el, a ) {
  502. //If overflow is hidden, the element might have extra content, but the user wants to hide it
  503. if ( $( el ).css( "overflow" ) === "hidden") {
  504. return false;
  505. }
  506. var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
  507. has = false;
  508. if ( el[ scroll ] > 0 ) {
  509. return true;
  510. }
  511. // TODO: determine which cases actually cause this to happen
  512. // if the element doesn't have the scroll set, see if it's possible to
  513. // set the scroll
  514. el[ scroll ] = 1;
  515. has = ( el[ scroll ] > 0 );
  516. el[ scroll ] = 0;
  517. return has;
  518. }
  519. });
  520. })( jQuery );
  521. (function( $, undefined ) {
  522. var uuid = 0,
  523. slice = Array.prototype.slice,
  524. _cleanData = $.cleanData;
  525. $.cleanData = function( elems ) {
  526. for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
  527. try {
  528. $( elem ).triggerHandler( "remove" );
  529. // http://bugs.jquery.com/ticket/8235
  530. } catch( e ) {}
  531. }
  532. _cleanData( elems );
  533. };
  534. $.widget = function( name, base, prototype ) {
  535. var fullName, existingConstructor, constructor, basePrototype,
  536. // proxiedPrototype allows the provided prototype to remain unmodified
  537. // so that it can be used as a mixin for multiple widgets (#8876)
  538. proxiedPrototype = {},
  539. namespace = name.split( "." )[ 0 ];
  540. name = name.split( "." )[ 1 ];
  541. fullName = namespace + "-" + name;
  542. if ( !prototype ) {
  543. prototype = base;
  544. base = $.Widget;
  545. }
  546. // create selector for plugin
  547. $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
  548. return !!$.data( elem, fullName );
  549. };
  550. $[ namespace ] = $[ namespace ] || {};
  551. existingConstructor = $[ namespace ][ name ];
  552. constructor = $[ namespace ][ name ] = function( options, element ) {
  553. // allow instantiation without "new" keyword
  554. if ( !this._createWidget ) {
  555. return new constructor( options, element );
  556. }
  557. // allow instantiation without initializing for simple inheritance
  558. // must use "new" keyword (the code above always passes args)
  559. if ( arguments.length ) {
  560. this._createWidget( options, element );
  561. }
  562. };
  563. // extend with the existing constructor to carry over any static properties
  564. $.extend( constructor, existingConstructor, {
  565. version: prototype.version,
  566. // copy the object used to create the prototype in case we need to
  567. // redefine the widget later
  568. _proto: $.extend( {}, prototype ),
  569. // track widgets that inherit from this widget in case this widget is
  570. // redefined after a widget inherits from it
  571. _childConstructors: []
  572. });
  573. basePrototype = new base();
  574. // we need to make the options hash a property directly on the new instance
  575. // otherwise we'll modify the options hash on the prototype that we're
  576. // inheriting from
  577. basePrototype.options = $.widget.extend( {}, basePrototype.options );
  578. $.each( prototype, function( prop, value ) {
  579. if ( !$.isFunction( value ) ) {
  580. proxiedPrototype[ prop ] = value;
  581. return;
  582. }
  583. proxiedPrototype[ prop ] = (function() {
  584. var _super = function() {
  585. return base.prototype[ prop ].apply( this, arguments );
  586. },
  587. _superApply = function( args ) {
  588. return base.prototype[ prop ].apply( this, args );
  589. };
  590. return function() {
  591. var __super = this._super,
  592. __superApply = this._superApply,
  593. returnValue;
  594. this._super = _super;
  595. this._superApply = _superApply;
  596. returnValue = value.apply( this, arguments );
  597. this._super = __super;
  598. this._superApply = __superApply;
  599. return returnValue;
  600. };
  601. })();
  602. });
  603. constructor.prototype = $.widget.extend( basePrototype, {
  604. // TODO: remove support for widgetEventPrefix
  605. // always use the name + a colon as the prefix, e.g., draggable:start
  606. // don't prefix for widgets that aren't DOM-based
  607. widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
  608. }, proxiedPrototype, {
  609. constructor: constructor,
  610. namespace: namespace,
  611. widgetName: name,
  612. widgetFullName: fullName
  613. });
  614. // If this widget is being redefined then we need to find all widgets that
  615. // are inheriting from it and redefine all of them so that they inherit from
  616. // the new version of this widget. We're essentially trying to replace one
  617. // level in the prototype chain.
  618. if ( existingConstructor ) {
  619. $.each( existingConstructor._childConstructors, function( i, child ) {
  620. var childPrototype = child.prototype;
  621. // redefine the child widget using the same prototype that was
  622. // originally used, but inherit from the new version of the base
  623. $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
  624. });
  625. // remove the list of existing child constructors from the old constructor
  626. // so the old child constructors can be garbage collected
  627. delete existingConstructor._childConstructors;
  628. } else {
  629. base._childConstructors.push( constructor );
  630. }
  631. $.widget.bridge( name, constructor );
  632. };
  633. $.widget.extend = function( target ) {
  634. var input = slice.call( arguments, 1 ),
  635. inputIndex = 0,
  636. inputLength = input.length,
  637. key,
  638. value;
  639. for ( ; inputIndex < inputLength; inputIndex++ ) {
  640. for ( key in input[ inputIndex ] ) {
  641. value = input[ inputIndex ][ key ];
  642. if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
  643. // Clone objects
  644. if ( $.isPlainObject( value ) ) {
  645. target[ key ] = $.isPlainObject( target[ key ] ) ?
  646. $.widget.extend( {}, target[ key ], value ) :
  647. // Don't extend strings, arrays, etc. with objects
  648. $.widget.extend( {}, value );
  649. // Copy everything else by reference
  650. } else {
  651. target[ key ] = value;
  652. }
  653. }
  654. }
  655. }
  656. return target;
  657. };
  658. $.widget.bridge = function( name, object ) {
  659. var fullName = object.prototype.widgetFullName || name;
  660. $.fn[ name ] = function( options ) {
  661. var isMethodCall = typeof options === "string",
  662. args = slice.call( arguments, 1 ),
  663. returnValue = this;
  664. // allow multiple hashes to be passed on init
  665. options = !isMethodCall && args.length ?
  666. $.widget.extend.apply( null, [ options ].concat(args) ) :
  667. options;
  668. if ( isMethodCall ) {
  669. this.each(function() {
  670. var methodValue,
  671. instance = $.data( this, fullName );
  672. if ( !instance ) {
  673. return $.error( "cannot call methods on " + name + " prior to initialization; " +
  674. "attempted to call method '" + options + "'" );
  675. }
  676. if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
  677. return $.error( "no such method '" + options + "' for " + name + " widget instance" );
  678. }
  679. methodValue = instance[ options ].apply( instance, args );
  680. if ( methodValue !== instance && methodValue !== undefined ) {
  681. returnValue = methodValue && methodValue.jquery ?
  682. returnValue.pushStack( methodValue.get() ) :
  683. methodValue;
  684. return false;
  685. }
  686. });
  687. } else {
  688. this.each(function() {
  689. var instance = $.data( this, fullName );
  690. if ( instance ) {
  691. instance.option( options || {} )._init();
  692. } else {
  693. $.data( this, fullName, new object( options, this ) );
  694. }
  695. });
  696. }
  697. return returnValue;
  698. };
  699. };
  700. $.Widget = function( /* options, element */ ) {};
  701. $.Widget._childConstructors = [];
  702. $.Widget.prototype = {
  703. widgetName: "widget",
  704. widgetEventPrefix: "",
  705. defaultElement: "<div>",
  706. options: {
  707. disabled: false,
  708. // callbacks
  709. create: null
  710. },
  711. _createWidget: function( options, element ) {
  712. element = $( element || this.defaultElement || this )[ 0 ];
  713. this.element = $( element );
  714. this.uuid = uuid++;
  715. this.eventNamespace = "." + this.widgetName + this.uuid;
  716. this.options = $.widget.extend( {},
  717. this.options,
  718. this._getCreateOptions(),
  719. options );
  720. this.bindings = $();
  721. this.hoverable = $();
  722. this.focusable = $();
  723. if ( element !== this ) {
  724. $.data( element, this.widgetFullName, this );
  725. this._on( true, this.element, {
  726. remove: function( event ) {
  727. if ( event.target === element ) {
  728. this.destroy();
  729. }
  730. }
  731. });
  732. this.document = $( element.style ?
  733. // element within the document
  734. element.ownerDocument :
  735. // element is window or document
  736. element.document || element );
  737. this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
  738. }
  739. this._create();
  740. this._trigger( "create", null, this._getCreateEventData() );
  741. this._init();
  742. },
  743. _getCreateOptions: $.noop,
  744. _getCreateEventData: $.noop,
  745. _create: $.noop,
  746. _init: $.noop,
  747. destroy: function() {
  748. this._destroy();
  749. // we can probably remove the unbind calls in 2.0
  750. // all event bindings should go through this._on()
  751. this.element
  752. .unbind( this.eventNamespace )
  753. // 1.9 BC for #7810
  754. // TODO remove dual storage
  755. .removeData( this.widgetName )
  756. .removeData( this.widgetFullName )
  757. // support: jquery <1.6.3
  758. // http://bugs.jquery.com/ticket/9413
  759. .removeData( $.camelCase( this.widgetFullName ) );
  760. this.widget()
  761. .unbind( this.eventNamespace )
  762. .removeAttr( "aria-disabled" )
  763. .removeClass(
  764. this.widgetFullName + "-disabled " +
  765. "ui-state-disabled" );
  766. // clean up events and states
  767. this.bindings.unbind( this.eventNamespace );
  768. this.hoverable.removeClass( "ui-state-hover" );
  769. this.focusable.removeClass( "ui-state-focus" );
  770. },
  771. _destroy: $.noop,
  772. widget: function() {
  773. return this.element;
  774. },
  775. option: function( key, value ) {
  776. var options = key,
  777. parts,
  778. curOption,
  779. i;
  780. if ( arguments.length === 0 ) {
  781. // don't return a reference to the internal hash
  782. return $.widget.extend( {}, this.options );
  783. }
  784. if ( typeof key === "string" ) {
  785. // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
  786. options = {};
  787. parts = key.split( "." );
  788. key = parts.shift();
  789. if ( parts.length ) {
  790. curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
  791. for ( i = 0; i < parts.length - 1; i++ ) {
  792. curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
  793. curOption = curOption[ parts[ i ] ];
  794. }
  795. key = parts.pop();
  796. if ( value === undefined ) {
  797. return curOption[ key ] === undefined ? null : curOption[ key ];
  798. }
  799. curOption[ key ] = value;
  800. } else {
  801. if ( value === undefined ) {
  802. return this.options[ key ] === undefined ? null : this.options[ key ];
  803. }
  804. options[ key ] = value;
  805. }
  806. }
  807. this._setOptions( options );
  808. return this;
  809. },
  810. _setOptions: function( options ) {
  811. var key;
  812. for ( key in options ) {
  813. this._setOption( key, options[ key ] );
  814. }
  815. return this;
  816. },
  817. _setOption: function( key, value ) {
  818. this.options[ key ] = value;
  819. if ( key === "disabled" ) {
  820. this.widget()
  821. .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
  822. .attr( "aria-disabled", value );
  823. this.hoverable.removeClass( "ui-state-hover" );
  824. this.focusable.removeClass( "ui-state-focus" );
  825. }
  826. return this;
  827. },
  828. enable: function() {
  829. return this._setOption( "disabled", false );
  830. },
  831. disable: function() {
  832. return this._setOption( "disabled", true );
  833. },
  834. _on: function( suppressDisabledCheck, element, handlers ) {
  835. var delegateElement,
  836. instance = this;
  837. // no suppressDisabledCheck flag, shuffle arguments
  838. if ( typeof suppressDisabledCheck !== "boolean" ) {
  839. handlers = element;
  840. element = suppressDisabledCheck;
  841. suppressDisabledCheck = false;
  842. }
  843. // no element argument, shuffle and use this.element
  844. if ( !handlers ) {
  845. handlers = element;
  846. element = this.element;
  847. delegateElement = this.widget();
  848. } else {
  849. // accept selectors, DOM elements
  850. element = delegateElement = $( element );
  851. this.bindings = this.bindings.add( element );
  852. }
  853. $.each( handlers, function( event, handler ) {
  854. function handlerProxy() {
  855. // allow widgets to customize the disabled handling
  856. // - disabled as an array instead of boolean
  857. // - disabled class as method for disabling individual parts
  858. if ( !suppressDisabledCheck &&
  859. ( instance.options.disabled === true ||
  860. $( this ).hasClass( "ui-state-disabled" ) ) ) {
  861. return;
  862. }
  863. return ( typeof handler === "string" ? instance[ handler ] : handler )
  864. .apply( instance, arguments );
  865. }
  866. // copy the guid so direct unbinding works
  867. if ( typeof handler !== "string" ) {
  868. handlerProxy.guid = handler.guid =
  869. handler.guid || handlerProxy.guid || $.guid++;
  870. }
  871. var match = event.match( /^(\w+)\s*(.*)$/ ),
  872. eventName = match[1] + instance.eventNamespace,
  873. selector = match[2];
  874. if ( selector ) {
  875. delegateElement.delegate( selector, eventName, handlerProxy );
  876. } else {
  877. element.bind( eventName, handlerProxy );
  878. }
  879. });
  880. },
  881. _off: function( element, eventName ) {
  882. eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
  883. element.unbind( eventName ).undelegate( eventName );
  884. },
  885. _delay: function( handler, delay ) {
  886. function handlerProxy() {
  887. return ( typeof handler === "string" ? instance[ handler ] : handler )
  888. .apply( instance, arguments );
  889. }
  890. var instance = this;
  891. return setTimeout( handlerProxy, delay || 0 );
  892. },
  893. _hoverable: function( element ) {
  894. this.hoverable = this.hoverable.add( element );
  895. this._on( element, {
  896. mouseenter: function( event ) {
  897. $( event.currentTarget ).addClass( "ui-state-hover" );
  898. },
  899. mouseleave: function( event ) {
  900. $( event.currentTarget ).removeClass( "ui-state-hover" );
  901. }
  902. });
  903. },
  904. _focusable: function( element ) {
  905. this.focusable = this.focusable.add( element );
  906. this._on( element, {
  907. focusin: function( event ) {
  908. $( event.currentTarget ).addClass( "ui-state-focus" );
  909. },
  910. focusout: function( event ) {
  911. $( event.currentTarget ).removeClass( "ui-state-focus" );
  912. }
  913. });
  914. },
  915. _trigger: function( type, event, data ) {
  916. var prop, orig,
  917. callback = this.options[ type ];
  918. data = data || {};
  919. event = $.Event( event );
  920. event.type = ( type === this.widgetEventPrefix ?
  921. type :
  922. this.widgetEventPrefix + type ).toLowerCase();
  923. // the original event may come from any element
  924. // so we need to reset the target on the new event
  925. event.target = this.element[ 0 ];
  926. // copy original event properties over to the new event
  927. orig = event.originalEvent;
  928. if ( orig ) {
  929. for ( prop in orig ) {
  930. if ( !( prop in event ) ) {
  931. event[ prop ] = orig[ prop ];
  932. }
  933. }
  934. }
  935. this.element.trigger( event, data );
  936. return !( $.isFunction( callback ) &&
  937. callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
  938. event.isDefaultPrevented() );
  939. }
  940. };
  941. $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
  942. $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
  943. if ( typeof options === "string" ) {
  944. options = { effect: options };
  945. }
  946. var hasOptions,
  947. effectName = !options ?
  948. method :
  949. options === true || typeof options === "number" ?
  950. defaultEffect :
  951. options.effect || defaultEffect;
  952. options = options || {};
  953. if ( typeof options === "number" ) {
  954. options = { duration: options };
  955. }
  956. hasOptions = !$.isEmptyObject( options );
  957. options.complete = callback;
  958. if ( options.delay ) {
  959. element.delay( options.delay );
  960. }
  961. if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
  962. element[ method ]( options );
  963. } else if ( effectName !== method && element[ effectName ] ) {
  964. element[ effectName ]( options.duration, options.easing, callback );
  965. } else {
  966. element.queue(function( next ) {
  967. $( this )[ method ]();
  968. if ( callback ) {
  969. callback.call( element[ 0 ] );
  970. }
  971. next();
  972. });
  973. }
  974. };
  975. });
  976. })( jQuery );
  977. (function( $, undefined ) {
  978. var mouseHandled = false;
  979. $( document ).mouseup( function() {
  980. mouseHandled = false;
  981. });
  982. $.widget("ui.mouse", {
  983. version: "1.10.3",
  984. options: {
  985. cancel: "input,textarea,button,select,option",
  986. distance: 1,
  987. delay: 0
  988. },
  989. _mouseInit: function() {
  990. var that = this;
  991. this.element
  992. .bind("mousedown."+this.widgetName, function(event) {
  993. return that._mouseDown(event);
  994. })
  995. .bind("click."+this.widgetName, function(event) {
  996. if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
  997. $.removeData(event.target, that.widgetName + ".preventClickEvent");
  998. event.stopImmediatePropagation();
  999. return false;
  1000. }
  1001. });
  1002. this.started = false;
  1003. },
  1004. // TODO: make sure destroying one instance of mouse doesn't mess with
  1005. // other instances of mouse
  1006. _mouseDestroy: function() {
  1007. this.element.unbind("."+this.widgetName);
  1008. if ( this._mouseMoveDelegate ) {
  1009. $(document)
  1010. .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
  1011. .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
  1012. }
  1013. },
  1014. _mouseDown: function(event) {
  1015. // don't let more than one widget handle mouseStart
  1016. if( mouseHandled ) { return; }
  1017. // we may have missed mouseup (out of window)
  1018. (this._mouseStarted && this._mouseUp(event));
  1019. this._mouseDownEvent = event;
  1020. var that = this,
  1021. btnIsLeft = (event.which === 1),
  1022. // event.target.nodeName works around a bug in IE 8 with
  1023. // disabled inputs (#7620)
  1024. elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
  1025. if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
  1026. return true;
  1027. }
  1028. this.mouseDelayMet = !this.options.delay;
  1029. if (!this.mouseDelayMet) {
  1030. this._mouseDelayTimer = setTimeout(function() {
  1031. that.mouseDelayMet = true;
  1032. }, this.options.delay);
  1033. }
  1034. if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
  1035. this._mouseStarted = (this._mouseStart(event) !== false);
  1036. if (!this._mouseStarted) {
  1037. event.preventDefault();
  1038. return true;
  1039. }
  1040. }
  1041. // Click event may never have fired (Gecko & Opera)
  1042. if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
  1043. $.removeData(event.target, this.widgetName + ".preventClickEvent");
  1044. }
  1045. // these delegates are required to keep context
  1046. this._mouseMoveDelegate = function(event) {
  1047. return that._mouseMove(event);
  1048. };
  1049. this._mouseUpDelegate = function(event) {
  1050. return that._mouseUp(event);
  1051. };
  1052. $(document)
  1053. .bind("mousemove."+this.widgetName, this._mouseMoveDelegate)
  1054. .bind("mouseup."+this.widgetName, this._mouseUpDelegate);
  1055. event.preventDefault();
  1056. mouseHandled = true;
  1057. return true;
  1058. },
  1059. _mouseMove: function(event) {
  1060. // IE mouseup check - mouseup happened when mouse was out of window
  1061. if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) {
  1062. return this._mouseUp(event);
  1063. }
  1064. if (this._mouseStarted) {
  1065. this._mouseDrag(event);
  1066. return event.preventDefault();
  1067. }
  1068. if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
  1069. this._mouseStarted =
  1070. (this._mouseStart(this._mouseDownEvent, event) !== false);
  1071. (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
  1072. }
  1073. return !this._mouseStarted;
  1074. },
  1075. _mouseUp: function(event) {
  1076. $(document)
  1077. .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
  1078. .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
  1079. if (this._mouseStarted) {
  1080. this._mouseStarted = false;
  1081. if (event.target === this._mouseDownEvent.target) {
  1082. $.data(event.target, this.widgetName + ".preventClickEvent", true);
  1083. }
  1084. this._mouseStop(event);
  1085. }
  1086. return false;
  1087. },
  1088. _mouseDistanceMet: function(event) {
  1089. return (Math.max(
  1090. Math.abs(this._mouseDownEvent.pageX - event.pageX),
  1091. Math.abs(this._mouseDownEvent.pageY - event.pageY)
  1092. ) >= this.options.distance
  1093. );
  1094. },
  1095. _mouseDelayMet: function(/* event */) {
  1096. return this.mouseDelayMet;
  1097. },
  1098. // These are placeholder methods, to be overriden by extending plugin
  1099. _mouseStart: function(/* event */) {},
  1100. _mouseDrag: function(/* event */) {},
  1101. _mouseStop: function(/* event */) {},
  1102. _mouseCapture: function(/* event */) { return true; }
  1103. });
  1104. })(jQuery);
  1105. (function( $, undefined ) {
  1106. $.widget("ui.draggable", $.ui.mouse, {
  1107. version: "1.10.3",
  1108. widgetEventPrefix: "drag",
  1109. options: {
  1110. addClasses: true,
  1111. appendTo: "parent",
  1112. axis: false,
  1113. connectToSortable: false,
  1114. containment: false,
  1115. cursor: "auto",
  1116. cursorAt: false,
  1117. grid: false,
  1118. handle: false,
  1119. helper: "original",
  1120. iframeFix: false,
  1121. opacity: false,
  1122. refreshPositions: false,
  1123. revert: false,
  1124. revertDuration: 500,
  1125. scope: "default",
  1126. scroll: true,
  1127. scrollSensitivity: 20,
  1128. scrollSpeed: 20,
  1129. snap: false,
  1130. snapMode: "both",
  1131. snapTolerance: 20,
  1132. stack: false,
  1133. zIndex: false,
  1134. // callbacks
  1135. drag: null,
  1136. start: null,
  1137. stop: null
  1138. },
  1139. _create: function() {
  1140. if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) {
  1141. this.element[0].style.position = "relative";
  1142. }
  1143. if (this.options.addClasses){
  1144. this.element.addClass("ui-draggable");
  1145. }
  1146. if (this.options.disabled){
  1147. this.element.addClass("ui-draggable-disabled");
  1148. }
  1149. this._mouseInit();
  1150. },
  1151. _destroy: function() {
  1152. this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
  1153. this._mouseDestroy();
  1154. },
  1155. _mouseCapture: function(event) {
  1156. var o = this.options;
  1157. // among others, prevent a drag on a resizable-handle
  1158. if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
  1159. return false;
  1160. }
  1161. //Quit if we're not on a valid handle
  1162. this.handle = this._getHandle(event);
  1163. if (!this.handle) {
  1164. return false;
  1165. }
  1166. $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
  1167. $("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>")
  1168. .css({
  1169. width: this.offsetWidth+"px", height: this.offsetHeight+"px",
  1170. position: "absolute", opacity: "0.001", zIndex: 1000
  1171. })
  1172. .css($(this).offset())
  1173. .appendTo("body");
  1174. });
  1175. return true;
  1176. },
  1177. _mouseStart: function(event) {
  1178. var o = this.options;
  1179. //Create and append the visible helper
  1180. this.helper = this._createHelper(event);
  1181. this.helper.addClass("ui-draggable-dragging");
  1182. //Cache the helper size
  1183. this._cacheHelperProportions();
  1184. //If ddmanager is used for droppables, set the global draggable
  1185. if($.ui.ddmanager) {
  1186. $.ui.ddmanager.current = this;
  1187. }
  1188. /*
  1189. * - Position generation -
  1190. * This block generates everything position related - it's the core of draggables.
  1191. */
  1192. //Cache the margins of the original element
  1193. this._cacheMargins();
  1194. //Store the helper's css position
  1195. this.cssPosition = this.helper.css( "position" );
  1196. this.scrollParent = this.helper.scrollParent();
  1197. this.offsetParent = this.helper.offsetParent();
  1198. this.offsetParentCssPosition = this.offsetParent.css( "position" );
  1199. //The element's absolute position on the page minus margins
  1200. this.offset = this.positionAbs = this.element.offset();
  1201. this.offset = {
  1202. top: this.offset.top - this.margins.top,
  1203. left: this.offset.left - this.margins.left
  1204. };
  1205. //Reset scroll cache
  1206. this.offset.scroll = false;
  1207. $.extend(this.offset, {
  1208. click: { //Where the click happened, relative to the element
  1209. left: event.pageX - this.offset.left,
  1210. top: event.pageY - this.offset.top
  1211. },
  1212. parent: this._getParentOffset(),
  1213. relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
  1214. });
  1215. //Generate the original position
  1216. this.originalPosition = this.position = this._generatePosition(event);
  1217. this.originalPageX = event.pageX;
  1218. this.originalPageY = event.pageY;
  1219. //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
  1220. (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
  1221. //Set a containment if given in the options
  1222. this._setContainment();
  1223. //Trigger event + callbacks
  1224. if(this._trigger("start", event) === false) {
  1225. this._clear();
  1226. return false;
  1227. }
  1228. //Recache the helper size
  1229. this._cacheHelperProportions();
  1230. //Prepare the droppable offsets
  1231. if ($.ui.ddmanager && !o.dropBehaviour) {
  1232. $.ui.ddmanager.prepareOffsets(this, event);
  1233. }
  1234. this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
  1235. //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
  1236. if ( $.ui.ddmanager ) {
  1237. $.ui.ddmanager.dragStart(this, event);
  1238. }
  1239. return true;
  1240. },
  1241. _mouseDrag: function(event, noPropagation) {
  1242. // reset any necessary cached properties (see #5009)
  1243. if ( this.offsetParentCssPosition === "fixed" ) {
  1244. this.offset.parent = this._getParentOffset();
  1245. }
  1246. //Compute the helpers position
  1247. this.position = this._generatePosition(event);
  1248. this.positionAbs = this._convertPositionTo("absolute");
  1249. //Call plugins and callbacks and use the resulting position if something is returned
  1250. if (!noPropagation) {
  1251. var ui = this._uiHash();
  1252. if(this._trigger("drag", event, ui) === false) {
  1253. this._mouseUp({});
  1254. return false;
  1255. }
  1256. this.position = ui.position;
  1257. }
  1258. if(!this.options.axis || this.options.axis !== "y") {
  1259. this.helper[0].style.left = this.position.left+"px";
  1260. }
  1261. if(!this.options.axis || this.options.axis !== "x") {
  1262. this.helper[0].style.top = this.position.top+"px";
  1263. }
  1264. if($.ui.ddmanager) {
  1265. $.ui.ddmanager.drag(this, event);
  1266. }
  1267. return false;
  1268. },
  1269. _mouseStop: function(event) {
  1270. //If we are using droppables, inform the manager about the drop
  1271. var that = this,
  1272. dropped = false;
  1273. if ($.ui.ddmanager && !this.options.dropBehaviour) {
  1274. dropped = $.ui.ddmanager.drop(this, event);
  1275. }
  1276. //if a drop comes from outside (a sortable)
  1277. if(this.dropped) {
  1278. dropped = this.dropped;
  1279. this.dropped = false;
  1280. }
  1281. //if the original element is no longer in the DOM don't bother to continue (see #8269)
  1282. if ( this.options.helper === "original" && !$.contains( this.element[ 0 ].ownerDocument, this.element[ 0 ] ) ) {
  1283. return false;
  1284. }
  1285. if((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
  1286. $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
  1287. if(that._trigger("stop", event) !== false) {
  1288. that._clear();
  1289. }
  1290. });
  1291. } else {
  1292. if(this._trigger("stop", event) !== false) {
  1293. this._clear();
  1294. }
  1295. }
  1296. return false;
  1297. },
  1298. _mouseUp: function(event) {
  1299. //Remove frame helpers
  1300. $("div.ui-draggable-iframeFix").each(function() {
  1301. this.parentNode.removeChild(this);
  1302. });
  1303. //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
  1304. if( $.ui.ddmanager ) {
  1305. $.ui.ddmanager.dragStop(this, event);
  1306. }
  1307. return $.ui.mouse.prototype._mouseUp.call(this, event);
  1308. },
  1309. cancel: function() {
  1310. if(this.helper.is(".ui-draggable-dragging")) {
  1311. this._mouseUp({});
  1312. } else {
  1313. this._clear();
  1314. }
  1315. return this;
  1316. },
  1317. _getHandle: function(event) {
  1318. return this.options.handle ?
  1319. !!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
  1320. true;
  1321. },
  1322. _createHelper: function(event) {
  1323. var o = this.options,
  1324. helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element);
  1325. if(!helper.parents("body").length) {
  1326. helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
  1327. }
  1328. if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
  1329. helper.css("position", "absolute");
  1330. }
  1331. return helper;
  1332. },
  1333. _adjustOffsetFromHelper: function(obj) {
  1334. if (typeof obj === "string") {
  1335. obj = obj.split(" ");
  1336. }
  1337. if ($.isArray(obj)) {
  1338. obj = {left: +obj[0], top: +obj[1] || 0};
  1339. }
  1340. if ("left" in obj) {
  1341. this.offset.click.left = obj.left + this.margins.left;
  1342. }
  1343. if ("right" in obj) {
  1344. this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
  1345. }
  1346. if ("top" in obj) {
  1347. this.offset.click.top = obj.top + this.margins.top;
  1348. }
  1349. if ("bottom" in obj) {
  1350. this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
  1351. }
  1352. },
  1353. _getParentOffset: function() {
  1354. //Get the offsetParent and cache its position
  1355. var po = this.offsetParent.offset();
  1356. // This is a special case where we need to modify a offset calculated on start, since the following happened:
  1357. // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
  1358. // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
  1359. // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
  1360. if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
  1361. po.left += this.scrollParent.scrollLeft();
  1362. po.top += this.scrollParent.scrollTop();
  1363. }
  1364. //This needs to be actually done for all browsers, since pageX/pageY includes this information
  1365. //Ugly IE fix
  1366. if((this.offsetParent[0] === document.body) ||
  1367. (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
  1368. po = { top: 0, left: 0 };
  1369. }
  1370. return {
  1371. top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
  1372. left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
  1373. };
  1374. },
  1375. _getRelativeOffset: function() {
  1376. if(this.cssPosition === "relative") {
  1377. var p = this.element.position();
  1378. return {
  1379. top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
  1380. left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
  1381. };
  1382. } else {
  1383. return { top: 0, left: 0 };
  1384. }
  1385. },
  1386. _cacheMargins: function() {
  1387. this.margins = {
  1388. left: (parseInt(this.element.css("marginLeft"),10) || 0),
  1389. top: (parseInt(this.element.css("marginTop"),10) || 0),
  1390. right: (parseInt(this.element.css("marginRight"),10) || 0),
  1391. bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
  1392. };
  1393. },
  1394. _cacheHelperProportions: function() {
  1395. this.helperProportions = {
  1396. width: this.helper.outerWidth(),
  1397. height: this.helper.outerHeight()
  1398. };
  1399. },
  1400. _setContainment: function() {
  1401. var over, c, ce,
  1402. o = this.options;
  1403. if ( !o.containment ) {
  1404. this.containment = null;
  1405. return;
  1406. }
  1407. if ( o.containment === "window" ) {
  1408. this.containment = [
  1409. $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
  1410. $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
  1411. $( window ).scrollLeft() + $( window ).width() - this.helperProportions.width - this.margins.left,
  1412. $( window ).scrollTop() + ( $( window ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
  1413. ];
  1414. return;
  1415. }
  1416. if ( o.containment === "document") {
  1417. this.containment = [
  1418. 0,
  1419. 0,
  1420. $( document ).width() - this.helperProportions.width - this.margins.left,
  1421. ( $( document ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
  1422. ];
  1423. return;
  1424. }
  1425. if ( o.containment.constructor === Array ) {
  1426. this.containment = o.containment;
  1427. return;
  1428. }
  1429. if ( o.containment === "parent" ) {
  1430. o.containment = this.helper[ 0 ].parentNode;
  1431. }
  1432. c = $( o.containment );
  1433. ce = c[ 0 ];
  1434. if( !ce ) {
  1435. return;
  1436. }
  1437. over = c.css( "overflow" ) !== "hidden";
  1438. this.containment = [
  1439. ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
  1440. ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ) ,
  1441. ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) - ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) - this.helperProportions.width - this.margins.left - this.margins.right,
  1442. ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) - ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) - this.helperProportions.height - this.margins.top - this.margins.bottom
  1443. ];
  1444. this.relative_container = c;
  1445. },
  1446. _convertPositionTo: function(d, pos) {
  1447. if(!pos) {
  1448. pos = this.position;
  1449. }
  1450. var mod = d === "absolute" ? 1 : -1,
  1451. scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent;
  1452. //Cache the scroll
  1453. if (!this.offset.scroll) {
  1454. this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
  1455. }
  1456. return {
  1457. top: (
  1458. pos.top + // The absolute mouse position
  1459. this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
  1460. this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
  1461. ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top ) * mod )
  1462. ),
  1463. left: (
  1464. pos.left + // The absolute mouse position
  1465. this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
  1466. this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
  1467. ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left ) * mod )
  1468. )
  1469. };
  1470. },
  1471. _generatePosition: function(event) {
  1472. var containment, co, top, left,
  1473. o = this.options,
  1474. scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent,
  1475. pageX = event.pageX,
  1476. pageY = event.pageY;
  1477. //Cache the scroll
  1478. if (!this.offset.scroll) {
  1479. this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
  1480. }
  1481. /*
  1482. * - Position constraining -
  1483. * Constrain the position to a mix of grid, containment.
  1484. */
  1485. // If we are not dragging yet, we won't check for options
  1486. if ( this.originalPosition ) {
  1487. if ( this.containment ) {
  1488. if ( this.relative_container ){
  1489. co = this.relative_container.offset();
  1490. containment = [
  1491. this.containment[ 0 ] + co.left,
  1492. this.containment[ 1 ] + co.top,
  1493. this.containment[ 2 ] + co.left,
  1494. this.containment[ 3 ] + co.top
  1495. ];
  1496. }
  1497. else {
  1498. containment = this.containment;
  1499. }
  1500. if(event.pageX - this.offset.click.left < containment[0]) {
  1501. pageX = containment[0] + this.offset.click.left;
  1502. }
  1503. if(event.pageY - this.offset.click.top < containment[1]) {
  1504. pageY = containment[1] + this.offset.click.top;
  1505. }
  1506. if(event.pageX - this.offset.click.left > containment[2]) {
  1507. pageX = containment[2] + this.offset.click.left;
  1508. }
  1509. if(event.pageY - this.offset.click.top > containment[3]) {
  1510. pageY = containment[3] + this.offset.click.top;
  1511. }
  1512. }
  1513. if(o.grid) {
  1514. //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
  1515. top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
  1516. pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
  1517. left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
  1518. pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
  1519. }
  1520. }
  1521. return {
  1522. top: (
  1523. pageY - // The absolute mouse position
  1524. this.offset.click.top - // Click offset (relative to the element)
  1525. this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
  1526. this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
  1527. ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top )
  1528. ),
  1529. left: (
  1530. pageX - // The absolute mouse position
  1531. this.offset.click.left - // Click offset (relative to the element)
  1532. this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
  1533. this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
  1534. ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left )
  1535. )
  1536. };
  1537. },
  1538. _clear: function() {
  1539. this.helper.removeClass("ui-draggable-dragging");
  1540. if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
  1541. this.helper.remove();
  1542. }
  1543. this.helper = null;
  1544. this.cancelHelperRemoval = false;
  1545. },
  1546. // From now on bulk stuff - mainly helpers
  1547. _trigger: function(type, event, ui) {
  1548. ui = ui || this._uiHash();
  1549. $.ui.plugin.call(this, type, [event, ui]);
  1550. //The absolute position has to be recalculated after plugins
  1551. if(type === "drag") {
  1552. this.positionAbs = this._convertPositionTo("absolute");
  1553. }
  1554. return $.Widget.prototype._trigger.call(this, type, event, ui);
  1555. },
  1556. plugins: {},
  1557. _uiHash: function() {
  1558. return {
  1559. helper: this.helper,
  1560. position: this.position,
  1561. originalPosition: this.originalPosition,
  1562. offset: this.positionAbs
  1563. };
  1564. }
  1565. });
  1566. $.ui.plugin.add("draggable", "connectToSortable", {
  1567. start: function(event, ui) {
  1568. var inst = $(this).data("ui-draggable"), o = inst.options,
  1569. uiSortable = $.extend({}, ui, { item: inst.element });
  1570. inst.sortables = [];
  1571. $(o.connectToSortable).each(function() {
  1572. var sortable = $.data(this, "ui-sortable");
  1573. if (sortable && !sortable.options.disabled) {
  1574. inst.sortables.push({
  1575. instance: sortable,
  1576. shouldRevert: sortable.options.revert
  1577. });
  1578. sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
  1579. sortable._trigger("activate", event, uiSortable);
  1580. }
  1581. });
  1582. },
  1583. stop: function(event, ui) {
  1584. //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
  1585. var inst = $(this).data("ui-draggable"),
  1586. uiSortable = $.extend({}, ui, { item: inst.element });
  1587. $.each(inst.sortables, function() {
  1588. if(this.instance.isOver) {
  1589. this.instance.isOver = 0;
  1590. inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
  1591. this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
  1592. //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid"
  1593. if(this.shouldRevert) {
  1594. this.instance.options.revert = this.shouldRevert;
  1595. }
  1596. //Trigger the stop of the sortable
  1597. this.instance._mouseStop(event);
  1598. this.instance.options.helper = this.instance.options._helper;
  1599. //If the helper has been the original item, restore properties in the sortable
  1600. if(inst.options.helper === "original") {
  1601. this.instance.currentItem.css({ top: "auto", left: "auto" });
  1602. }
  1603. } else {
  1604. this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
  1605. this.instance._trigger("deactivate", event, uiSortable);
  1606. }
  1607. });
  1608. },
  1609. drag: function(event, ui) {
  1610. var inst = $(this).data("ui-draggable"), that = this;
  1611. $.each(inst.sortables, function() {
  1612. var innermostIntersecting = false,
  1613. thisSortable = this;
  1614. //Copy over some variables to allow calling the sortable's native _intersectsWith
  1615. this.instance.positionAbs = inst.positionAbs;
  1616. this.instance.helperProportions = inst.helperProportions;
  1617. this.instance.offset.click = inst.offset.click;
  1618. if(this.instance._intersectsWith(this.instance.containerCache)) {
  1619. innermostIntersecting = true;
  1620. $.each(inst.sortables, function () {
  1621. this.instance.positionAbs = inst.positionAbs;
  1622. this.instance.helperProportions = inst.helperProportions;
  1623. this.instance.offset.click = inst.offset.click;
  1624. if (this !== thisSortable &&
  1625. this.instance._intersectsWith(this.instance.containerCache) &&
  1626. $.contains(thisSortable.instance.element[0], this.instance.element[0])
  1627. ) {
  1628. innermostIntersecting = false;
  1629. }
  1630. return innermostIntersecting;
  1631. });
  1632. }
  1633. if(innermostIntersecting) {
  1634. //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
  1635. if(!this.instance.isOver) {
  1636. this.instance.isOver = 1;
  1637. //Now we fake the start of dragging for the sortable instance,
  1638. //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
  1639. //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
  1640. this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true);
  1641. this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
  1642. this.instance.options.helper = function() { return ui.helper[0]; };
  1643. event.target = this.instance.currentItem[0];
  1644. this.instance._mouseCapture(event, true);
  1645. this.instance._mouseStart(event, true, true);
  1646. //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
  1647. this.instance.offset.click.top = inst.offset.click.top;
  1648. this.instance.offset.click.left = inst.offset.click.left;
  1649. this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
  1650. this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
  1651. inst._trigger("toSortable", event);
  1652. inst.dropped = this.instance.element; //draggable revert needs that
  1653. //hack so receive/update callbacks work (mostly)
  1654. inst.currentItem = inst.element;
  1655. this.instance.fromOutside = inst;
  1656. }
  1657. //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
  1658. if(this.instance.currentItem) {
  1659. this.instance._mouseDrag(event);
  1660. }
  1661. } else {
  1662. //If it doesn't intersect with the sortable, and it intersected before,
  1663. //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
  1664. if(this.instance.isOver) {
  1665. this.instance.isOver = 0;
  1666. this.instance.cancelHelperRemoval = true;
  1667. //Prevent reverting on this forced stop
  1668. this.instance.options.revert = false;
  1669. // The out event needs to be triggered independently
  1670. this.instance._trigger("out", event, this.instance._uiHash(this.instance));
  1671. this.instance._mouseStop(event, true);
  1672. this.instance.options.helper = this.instance.options._helper;
  1673. //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
  1674. this.instance.currentItem.remove();
  1675. if(this.instance.placeholder) {
  1676. this.instance.placeholder.remove();
  1677. }
  1678. inst._trigger("fromSortable", event);
  1679. inst.dropped = false; //draggable revert needs that
  1680. }
  1681. }
  1682. });
  1683. }
  1684. });
  1685. $.ui.plugin.add("draggable", "cursor", {
  1686. start: function() {
  1687. var t = $("body"), o = $(this).data("ui-draggable").options;
  1688. if (t.css("cursor")) {
  1689. o._cursor = t.css("cursor");
  1690. }
  1691. t.css("cursor", o.cursor);
  1692. },
  1693. stop: function() {
  1694. var o = $(this).data("ui-draggable").options;
  1695. if (o._cursor) {
  1696. $("body").css("cursor", o._cursor);
  1697. }
  1698. }
  1699. });
  1700. $.ui.plugin.add("draggable", "opacity", {
  1701. start: function(event, ui) {
  1702. var t = $(ui.helper), o = $(this).data("ui-draggable").options;
  1703. if(t.css("opacity")) {
  1704. o._opacity = t.css("opacity");
  1705. }
  1706. t.css("opacity", o.opacity);
  1707. },
  1708. stop: function(event, ui) {
  1709. var o = $(this).data("ui-draggable").options;
  1710. if(o._opacity) {
  1711. $(ui.helper).css("opacity", o._opacity);
  1712. }
  1713. }
  1714. });
  1715. $.ui.plugin.add("draggable", "scroll", {
  1716. start: function() {
  1717. var i = $(this).data("ui-draggable");
  1718. if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
  1719. i.overflowOffset = i.scrollParent.offset();
  1720. }
  1721. },
  1722. drag: function( event ) {
  1723. var i = $(this).data("ui-draggable"), o = i.options, scrolled = false;
  1724. if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
  1725. if(!o.axis || o.axis !== "x") {
  1726. if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
  1727. i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
  1728. } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) {
  1729. i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
  1730. }
  1731. }
  1732. if(!o.axis || o.axis !== "y") {
  1733. if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
  1734. i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
  1735. } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) {
  1736. i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
  1737. }
  1738. }
  1739. } else {
  1740. if(!o.axis || o.axis !== "x") {
  1741. if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
  1742. scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
  1743. } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
  1744. scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
  1745. }
  1746. }
  1747. if(!o.axis || o.axis !== "y") {
  1748. if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
  1749. scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
  1750. } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
  1751. scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
  1752. }
  1753. }
  1754. }
  1755. if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
  1756. $.ui.ddmanager.prepareOffsets(i, event);
  1757. }
  1758. }
  1759. });
  1760. $.ui.plugin.add("draggable", "snap", {
  1761. start: function() {
  1762. var i = $(this).data("ui-draggable"),
  1763. o = i.options;
  1764. i.snapElements = [];
  1765. $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
  1766. var $t = $(this),
  1767. $o = $t.offset();
  1768. if(this !== i.element[0]) {
  1769. i.snapElements.push({
  1770. item: this,
  1771. width: $t.outerWidth(), height: $t.outerHeight(),
  1772. top: $o.top, left: $o.left
  1773. });
  1774. }
  1775. });
  1776. },
  1777. drag: function(event, ui) {
  1778. var ts, bs, ls, rs, l, r, t, b, i, first,
  1779. inst = $(this).data("ui-draggable"),
  1780. o = inst.options,
  1781. d = o.snapTolerance,
  1782. x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
  1783. y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
  1784. for (i = inst.snapElements.length - 1; i >= 0; i--){
  1785. l = inst.snapElements[i].left;
  1786. r = l + inst.snapElements[i].width;
  1787. t = inst.snapElements[i].top;
  1788. b = t + inst.snapElements[i].height;
  1789. if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || !$.contains( inst.snapElements[ i ].item.ownerDocument, inst.snapElements[ i ].item ) ) {
  1790. if(inst.snapElements[i].snapping) {
  1791. (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
  1792. }
  1793. inst.snapElements[i].snapping = false;
  1794. continue;
  1795. }
  1796. if(o.snapMode !== "inner") {
  1797. ts = Math.abs(t - y2) <= d;
  1798. bs = Math.abs(b - y1) <= d;
  1799. ls = Math.abs(l - x2) <= d;
  1800. rs = Math.abs(r - x1) <= d;
  1801. if(ts) {
  1802. ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
  1803. }
  1804. if(bs) {
  1805. ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
  1806. }
  1807. if(ls) {
  1808. ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
  1809. }
  1810. if(rs) {
  1811. ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
  1812. }
  1813. }
  1814. first = (ts || bs || ls || rs);
  1815. if(o.snapMode !== "outer") {
  1816. ts = Math.abs(t - y1) <= d;
  1817. bs = Math.abs(b - y2) <= d;
  1818. ls = Math.abs(l - x1) <= d;
  1819. rs = Math.abs(r - x2) <= d;
  1820. if(ts) {
  1821. ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
  1822. }
  1823. if(bs) {
  1824. ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
  1825. }
  1826. if(ls) {
  1827. ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
  1828. }
  1829. if(rs) {
  1830. ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
  1831. }
  1832. }
  1833. if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
  1834. (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
  1835. }
  1836. inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
  1837. }
  1838. }
  1839. });
  1840. $.ui.plugin.add("draggable", "stack", {
  1841. start: function() {
  1842. var min,
  1843. o = this.data("ui-draggable").options,
  1844. group = $.makeArray($(o.stack)).sort(function(a,b) {
  1845. return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
  1846. });
  1847. if (!group.length) { return; }
  1848. min = parseInt($(group[0]).css("zIndex"), 10) || 0;
  1849. $(group).each(function(i) {
  1850. $(this).css("zIndex", min + i);
  1851. });
  1852. this.css("zIndex", (min + group.length));
  1853. }
  1854. });
  1855. $.ui.plugin.add("draggable", "zIndex", {
  1856. start: function(event, ui) {
  1857. var t = $(ui.helper), o = $(this).data("ui-draggable").options;
  1858. if(t.css("zIndex")) {
  1859. o._zIndex = t.css("zIndex");
  1860. }
  1861. t.css("zIndex", o.zIndex);
  1862. },
  1863. stop: function(event, ui) {
  1864. var o = $(this).data("ui-draggable").options;
  1865. if(o._zIndex) {
  1866. $(ui.helper).css("zIndex", o._zIndex);
  1867. }
  1868. }
  1869. });
  1870. })(jQuery);
  1871. (function( $, undefined ) {
  1872. function isOverAxis( x, reference, size ) {
  1873. return ( x > reference ) && ( x < ( reference + size ) );
  1874. }
  1875. $.widget("ui.droppable", {
  1876. version: "1.10.3",
  1877. widgetEventPrefix: "drop",
  1878. options: {
  1879. accept: "*",
  1880. activeClass: false,
  1881. addClasses: true,
  1882. greedy: false,
  1883. hoverClass: false,
  1884. scope: "default",
  1885. tolerance: "intersect",
  1886. // callbacks
  1887. activate: null,
  1888. deactivate: null,
  1889. drop: null,
  1890. out: null,
  1891. over: null
  1892. },
  1893. _create: function() {
  1894. var o = this.options,
  1895. accept = o.accept;
  1896. this.isover = false;
  1897. this.isout = true;
  1898. this.accept = $.isFunction(accept) ? accept : function(d) {
  1899. return d.is(accept);
  1900. };
  1901. //Store the droppable's proportions
  1902. this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
  1903. // Add the reference and positions to the manager
  1904. $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
  1905. $.ui.ddmanager.droppables[o.scope].push(this);
  1906. (o.addClasses && this.element.addClass("ui-droppable"));
  1907. },
  1908. _destroy: function() {
  1909. var i = 0,
  1910. drop = $.ui.ddmanager.droppables[this.options.scope];
  1911. for ( ; i < drop.length; i++ ) {
  1912. if ( drop[i] === this ) {
  1913. drop.splice(i, 1);
  1914. }
  1915. }
  1916. this.element.removeClass("ui-droppable ui-droppable-disabled");
  1917. },
  1918. _setOption: function(key, value) {
  1919. if(key === "accept") {
  1920. this.accept = $.isFunction(value) ? value : function(d) {
  1921. return d.is(value);
  1922. };
  1923. }
  1924. $.Widget.prototype._setOption.apply(this, arguments);
  1925. },
  1926. _activate: function(event) {
  1927. var draggable = $.ui.ddmanager.current;
  1928. if(this.options.activeClass) {
  1929. this.element.addClass(this.options.activeClass);
  1930. }
  1931. if(draggable){
  1932. this._trigger("activate", event, this.ui(draggable));
  1933. }
  1934. },
  1935. _deactivate: function(event) {
  1936. var draggable = $.ui.ddmanager.current;
  1937. if(this.options.activeClass) {
  1938. this.element.removeClass(this.options.activeClass);
  1939. }
  1940. if(draggable){
  1941. this._trigger("deactivate", event, this.ui(draggable));
  1942. }
  1943. },
  1944. _over: function(event) {
  1945. var draggable = $.ui.ddmanager.current;
  1946. // Bail if draggable and droppable are same element
  1947. if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
  1948. return;
  1949. }
  1950. if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
  1951. if(this.options.hoverClass) {
  1952. this.element.addClass(this.options.hoverClass);
  1953. }
  1954. this._trigger("over", event, this.ui(draggable));
  1955. }
  1956. },
  1957. _out: function(event) {
  1958. var draggable = $.ui.ddmanager.current;
  1959. // Bail if draggable and droppable are same element
  1960. if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
  1961. return;
  1962. }
  1963. if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
  1964. if(this.options.hoverClass) {
  1965. this.element.removeClass(this.options.hoverClass);
  1966. }
  1967. this._trigger("out", event, this.ui(draggable));
  1968. }
  1969. },
  1970. _drop: function(event,custom) {
  1971. var draggable = custom || $.ui.ddmanager.current,
  1972. childrenIntersection = false;
  1973. // Bail if draggable and droppable are same element
  1974. if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
  1975. return false;
  1976. }
  1977. this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() {
  1978. var inst = $.data(this, "ui-droppable");
  1979. if(
  1980. inst.options.greedy &&
  1981. !inst.options.disabled &&
  1982. inst.options.scope === draggable.options.scope &&
  1983. inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) &&
  1984. $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
  1985. ) { childrenIntersection = true; return false; }
  1986. });
  1987. if(childrenIntersection) {
  1988. return false;
  1989. }
  1990. if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
  1991. if(this.options.activeClass) {
  1992. this.element.removeClass(this.options.activeClass);
  1993. }
  1994. if(this.options.hoverClass) {
  1995. this.element.removeClass(this.options.hoverClass);
  1996. }
  1997. this._trigger("drop", event, this.ui(draggable));
  1998. return this.element;
  1999. }
  2000. return false;
  2001. },
  2002. ui: function(c) {
  2003. return {
  2004. draggable: (c.currentItem || c.element),
  2005. helper: c.helper,
  2006. position: c.position,
  2007. offset: c.positionAbs
  2008. };
  2009. }
  2010. });
  2011. $.ui.intersect = function(draggable, droppable, toleranceMode) {
  2012. if (!droppable.offset) {
  2013. return false;
  2014. }
  2015. var draggableLeft, draggableTop,
  2016. x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
  2017. y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height,
  2018. l = droppable.offset.left, r = l + droppable.proportions.width,
  2019. t = droppable.offset.top, b = t + droppable.proportions.height;
  2020. switch (toleranceMode) {
  2021. case "fit":
  2022. return (l <= x1 && x2 <= r && t <= y1 && y2 <= b);
  2023. case "intersect":
  2024. return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half
  2025. x2 - (draggable.helperProportions.width / 2) < r && // Left Half
  2026. t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half
  2027. y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
  2028. case "pointer":
  2029. draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left);
  2030. draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top);
  2031. return isOverAxis( draggableTop, t, droppable.proportions.height ) && isOverAxis( draggableLeft, l, droppable.proportions.width );
  2032. case "touch":
  2033. return (
  2034. (y1 >= t && y1 <= b) || // Top edge touching
  2035. (y2 >= t && y2 <= b) || // Bottom edge touching
  2036. (y1 < t && y2 > b) // Surrounded vertically
  2037. ) && (
  2038. (x1 >= l && x1 <= r) || // Left edge touching
  2039. (x2 >= l && x2 <= r) || // Right edge touching
  2040. (x1 < l && x2 > r) // Surrounded horizontally
  2041. );
  2042. default:
  2043. return false;
  2044. }
  2045. };
  2046. /*
  2047. This manager tracks offsets of draggables and droppables
  2048. */
  2049. $.ui.ddmanager = {
  2050. current: null,
  2051. droppables: { "default": [] },
  2052. prepareOffsets: function(t, event) {
  2053. var i, j,
  2054. m = $.ui.ddmanager.droppables[t.options.scope] || [],
  2055. type = event ? event.type : null, // workaround for #2317
  2056. list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack();
  2057. droppablesLoop: for (i = 0; i < m.length; i++) {
  2058. //No disabled and non-accepted
  2059. if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) {
  2060. continue;
  2061. }
  2062. // Filter out elements in the current dragged item
  2063. for (j=0; j < list.length; j++) {
  2064. if(list[j] === m[i].element[0]) {
  2065. m[i].proportions.height = 0;
  2066. continue droppablesLoop;
  2067. }
  2068. }
  2069. m[i].visible = m[i].element.css("display") !== "none";
  2070. if(!m[i].visible) {
  2071. continue;
  2072. }
  2073. //Activate the droppable if used directly from draggables
  2074. if(type === "mousedown") {
  2075. m[i]._activate.call(m[i], event);
  2076. }
  2077. m[i].offset = m[i].element.offset();
  2078. m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
  2079. }
  2080. },
  2081. drop: function(draggable, event) {
  2082. var dropped = false;
  2083. // Create a copy of the droppables in case the list changes during the drop (#9116)
  2084. $.each(($.ui.ddmanager.droppables[draggable.options.scope] || []).slice(), function() {
  2085. if(!this.options) {
  2086. return;
  2087. }
  2088. if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) {
  2089. dropped = this._drop.call(this, event) || dropped;
  2090. }
  2091. if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
  2092. this.isout = true;
  2093. this.isover = false;
  2094. this._deactivate.call(this, event);
  2095. }
  2096. });
  2097. return dropped;
  2098. },
  2099. dragStart: function( draggable, event ) {
  2100. //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
  2101. draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
  2102. if( !draggable.options.refreshPositions ) {
  2103. $.ui.ddmanager.prepareOffsets( draggable, event );
  2104. }
  2105. });
  2106. },
  2107. drag: function(draggable, event) {
  2108. //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
  2109. if(draggable.options.refreshPositions) {
  2110. $.ui.ddmanager.prepareOffsets(draggable, event);
  2111. }
  2112. //Run through all droppables and check their positions based on specific tolerance options
  2113. $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
  2114. if(this.options.disabled || this.greedyChild || !this.visible) {
  2115. return;
  2116. }
  2117. var parentInstance, scope, parent,
  2118. intersects = $.ui.intersect(draggable, this, this.options.tolerance),
  2119. c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null);
  2120. if(!c) {
  2121. return;
  2122. }
  2123. if (this.options.greedy) {
  2124. // find droppable parents with same scope
  2125. scope = this.options.scope;
  2126. parent = this.element.parents(":data(ui-droppable)").filter(function () {
  2127. return $.data(this, "ui-droppable").options.scope === scope;
  2128. });
  2129. if (parent.length) {
  2130. parentInstance = $.data(parent[0], "ui-droppable");
  2131. parentInstance.greedyChild = (c === "isover");
  2132. }
  2133. }
  2134. // we just moved into a greedy child
  2135. if (parentInstance && c === "isover") {
  2136. parentInstance.isover = false;
  2137. parentInstance.isout = true;
  2138. parentInstance._out.call(parentInstance, event);
  2139. }
  2140. this[c] = true;
  2141. this[c === "isout" ? "isover" : "isout"] = false;
  2142. this[c === "isover" ? "_over" : "_out"].call(this, event);
  2143. // we just moved out of a greedy child
  2144. if (parentInstance && c === "isout") {
  2145. parentInstance.isout = false;
  2146. parentInstance.isover = true;
  2147. parentInstance._over.call(parentInstance, event);
  2148. }
  2149. });
  2150. },
  2151. dragStop: function( draggable, event ) {
  2152. draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
  2153. //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
  2154. if( !draggable.options.refreshPositions ) {
  2155. $.ui.ddmanager.prepareOffsets( draggable, event );
  2156. }
  2157. }
  2158. };
  2159. })(jQuery);
  2160. (function( $, undefined ) {
  2161. function num(v) {
  2162. return parseInt(v, 10) || 0;
  2163. }
  2164. function isNumber(value) {
  2165. return !isNaN(parseInt(value, 10));
  2166. }
  2167. $.widget("ui.resizable", $.ui.mouse, {
  2168. version: "1.10.3",
  2169. widgetEventPrefix: "resize",
  2170. options: {
  2171. alsoResize: false,
  2172. animate: false,
  2173. animateDuration: "slow",
  2174. animateEasing: "swing",
  2175. aspectRatio: false,
  2176. autoHide: false,
  2177. containment: false,
  2178. ghost: false,
  2179. grid: false,
  2180. handles: "e,s,se",
  2181. helper: false,
  2182. maxHeight: null,
  2183. maxWidth: null,
  2184. minHeight: 10,
  2185. minWidth: 10,
  2186. // See #7960
  2187. zIndex: 90,
  2188. // callbacks
  2189. resize: null,
  2190. start: null,
  2191. stop: null
  2192. },
  2193. _create: function() {
  2194. var n, i, handle, axis, hname,
  2195. that = this,
  2196. o = this.options;
  2197. this.element.addClass("ui-resizable");
  2198. $.extend(this, {
  2199. _aspectRatio: !!(o.aspectRatio),
  2200. aspectRatio: o.aspectRatio,
  2201. originalElement: this.element,
  2202. _proportionallyResizeElements: [],
  2203. _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
  2204. });
  2205. //Wrap the element if it cannot hold child nodes
  2206. if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
  2207. //Create a wrapper element and set the wrapper to the new current internal element
  2208. this.element.wrap(
  2209. $("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({
  2210. position: this.element.css("position"),
  2211. width: this.element.outerWidth(),
  2212. height: this.element.outerHeight(),
  2213. top: this.element.css("top"),
  2214. left: this.element.css("left")
  2215. })
  2216. );
  2217. //Overwrite the original this.element
  2218. this.element = this.element.parent().data(
  2219. "ui-resizable", this.element.data("ui-resizable")
  2220. );
  2221. this.elementIsWrapper = true;
  2222. //Move margins to the wrapper
  2223. this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
  2224. this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
  2225. //Prevent Safari textarea resize
  2226. this.originalResizeStyle = this.originalElement.css("resize");
  2227. this.originalElement.css("resize", "none");
  2228. //Push the actual element to our proportionallyResize internal array
  2229. this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" }));
  2230. // avoid IE jump (hard set the margin)
  2231. this.originalElement.css({ margin: this.originalElement.css("margin") });
  2232. // fix handlers offset
  2233. this._proportionallyResize();
  2234. }
  2235. this.handles = o.handles || (!$(".ui-resizable-handle", this.element).length ? "e,s,se" : { n: ".ui-resizable-n", e: ".ui-resizable-e", s: ".ui-resizable-s", w: ".ui-resizable-w", se: ".ui-resizable-se", sw: ".ui-resizable-sw", ne: ".ui-resizable-ne", nw: ".ui-resizable-nw" });
  2236. if(this.handles.constructor === String) {
  2237. if ( this.handles === "all") {
  2238. this.handles = "n,e,s,w,se,sw,ne,nw";
  2239. }
  2240. n = this.handles.split(",");
  2241. this.handles = {};
  2242. for(i = 0; i < n.length; i++) {
  2243. handle = $.trim(n[i]);
  2244. hname = "ui-resizable-"+handle;
  2245. axis = $("<div class='ui-resizable-handle " + hname + "'></div>");
  2246. // Apply zIndex to all handles - see #7960
  2247. axis.css({ zIndex: o.zIndex });
  2248. //TODO : What's going on here?
  2249. if ("se" === handle) {
  2250. axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se");
  2251. }
  2252. //Insert into internal handles object and append to element
  2253. this.handles[handle] = ".ui-resizable-"+handle;
  2254. this.element.append(axis);
  2255. }
  2256. }
  2257. this._renderAxis = function(target) {
  2258. var i, axis, padPos, padWrapper;
  2259. target = target || this.element;
  2260. for(i in this.handles) {
  2261. if(this.handles[i].constructor === String) {
  2262. this.handles[i] = $(this.handles[i], this.element).show();
  2263. }
  2264. //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
  2265. if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
  2266. axis = $(this.handles[i], this.element);
  2267. //Checking the correct pad and border
  2268. padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
  2269. //The padding type i have to apply...
  2270. padPos = [ "padding",
  2271. /ne|nw|n/.test(i) ? "Top" :
  2272. /se|sw|s/.test(i) ? "Bottom" :
  2273. /^e$/.test(i) ? "Right" : "Left" ].join("");
  2274. target.css(padPos, padWrapper);
  2275. this._proportionallyResize();
  2276. }
  2277. //TODO: What's that good for? There's not anything to be executed left
  2278. if(!$(this.handles[i]).length) {
  2279. continue;
  2280. }
  2281. }
  2282. };
  2283. //TODO: make renderAxis a prototype function
  2284. this._renderAxis(this.element);
  2285. this._handles = $(".ui-resizable-handle", this.element)
  2286. .disableSelection();
  2287. //Matching axis name
  2288. this._handles.mouseover(function() {
  2289. if (!that.resizing) {
  2290. if (this.className) {
  2291. axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
  2292. }
  2293. //Axis, default = se
  2294. that.axis = axis && axis[1] ? axis[1] : "se";
  2295. }
  2296. });
  2297. //If we want to auto hide the elements
  2298. if (o.autoHide) {
  2299. this._handles.hide();
  2300. $(this.element)
  2301. .addClass("ui-resizable-autohide")
  2302. .mouseenter(function() {
  2303. if (o.disabled) {
  2304. return;
  2305. }
  2306. $(this).removeClass("ui-resizable-autohide");
  2307. that._handles.show();
  2308. })
  2309. .mouseleave(function(){
  2310. if (o.disabled) {
  2311. return;
  2312. }
  2313. if (!that.resizing) {
  2314. $(this).addClass("ui-resizable-autohide");
  2315. that._handles.hide();
  2316. }
  2317. });
  2318. }
  2319. //Initialize the mouse interaction
  2320. this._mouseInit();
  2321. },
  2322. _destroy: function() {
  2323. this._mouseDestroy();
  2324. var wrapper,
  2325. _destroy = function(exp) {
  2326. $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
  2327. .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove();
  2328. };
  2329. //TODO: Unwrap at same DOM position
  2330. if (this.elementIsWrapper) {
  2331. _destroy(this.element);
  2332. wrapper = this.element;
  2333. this.originalElement.css({
  2334. position: wrapper.css("position"),
  2335. width: wrapper.outerWidth(),
  2336. height: wrapper.outerHeight(),
  2337. top: wrapper.css("top"),
  2338. left: wrapper.css("left")
  2339. }).insertAfter( wrapper );
  2340. wrapper.remove();
  2341. }
  2342. this.originalElement.css("resize", this.originalResizeStyle);
  2343. _destroy(this.originalElement);
  2344. return this;
  2345. },
  2346. _mouseCapture: function(event) {
  2347. var i, handle,
  2348. capture = false;
  2349. for (i in this.handles) {
  2350. handle = $(this.handles[i])[0];
  2351. if (handle === event.target || $.contains(handle, event.target)) {
  2352. capture = true;
  2353. }
  2354. }
  2355. return !this.options.disabled && capture;
  2356. },
  2357. _mouseStart: function(event) {
  2358. var curleft, curtop, cursor,
  2359. o = this.options,
  2360. iniPos = this.element.position(),
  2361. el = this.element;
  2362. this.resizing = true;
  2363. // bugfix for http://dev.jquery.com/ticket/1749
  2364. if ( (/absolute/).test( el.css("position") ) ) {
  2365. el.css({ position: "absolute", top: el.css("top"), left: el.css("left") });
  2366. } else if (el.is(".ui-draggable")) {
  2367. el.css({ position: "absolute", top: iniPos.top, left: iniPos.left });
  2368. }
  2369. this._renderProxy();
  2370. curleft = num(this.helper.css("left"));
  2371. curtop = num(this.helper.css("top"));
  2372. if (o.containment) {
  2373. curleft += $(o.containment).scrollLeft() || 0;
  2374. curtop += $(o.containment).scrollTop() || 0;
  2375. }
  2376. //Store needed variables
  2377. this.offset = this.helper.offset();
  2378. this.position = { left: curleft, top: curtop };
  2379. this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
  2380. this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
  2381. this.originalPosition = { left: curleft, top: curtop };
  2382. this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
  2383. this.originalMousePosition = { left: event.pageX, top: event.pageY };
  2384. //Aspect Ratio
  2385. this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
  2386. cursor = $(".ui-resizable-" + this.axis).css("cursor");
  2387. $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);
  2388. el.addClass("ui-resizable-resizing");
  2389. this._propagate("start", event);
  2390. return true;
  2391. },
  2392. _mouseDrag: function(event) {
  2393. //Increase performance, avoid regex
  2394. var data,
  2395. el = this.helper, props = {},
  2396. smp = this.originalMousePosition,
  2397. a = this.axis,
  2398. prevTop = this.position.top,
  2399. prevLeft = this.position.left,
  2400. prevWidth = this.size.width,
  2401. prevHeight = this.size.height,
  2402. dx = (event.pageX-smp.left)||0,
  2403. dy = (event.pageY-smp.top)||0,
  2404. trigger = this._change[a];
  2405. if (!trigger) {
  2406. return false;
  2407. }
  2408. // Calculate the attrs that will be change
  2409. data = trigger.apply(this, [event, dx, dy]);
  2410. // Put this in the mouseDrag handler since the user can start pressing shift while resizing
  2411. this._updateVirtualBoundaries(event.shiftKey);
  2412. if (this._aspectRatio || event.shiftKey) {
  2413. data = this._updateRatio(data, event);
  2414. }
  2415. data = this._respectSize(data, event);
  2416. this._updateCache(data);
  2417. // plugins callbacks need to be called first
  2418. this._propagate("resize", event);
  2419. if (this.position.top !== prevTop) {
  2420. props.top = this.position.top + "px";
  2421. }
  2422. if (this.position.left !== prevLeft) {
  2423. props.left = this.position.left + "px";
  2424. }
  2425. if (this.size.width !== prevWidth) {
  2426. props.width = this.size.width + "px";
  2427. }
  2428. if (this.size.height !== prevHeight) {
  2429. props.height = this.size.height + "px";
  2430. }
  2431. el.css(props);
  2432. if (!this._helper && this._proportionallyResizeElements.length) {
  2433. this._proportionallyResize();
  2434. }
  2435. // Call the user callback if the element was resized
  2436. if ( ! $.isEmptyObject(props) ) {
  2437. this._trigger("resize", event, this.ui());
  2438. }
  2439. return false;
  2440. },
  2441. _mouseStop: function(event) {
  2442. this.resizing = false;
  2443. var pr, ista, soffseth, soffsetw, s, left, top,
  2444. o = this.options, that = this;
  2445. if(this._helper) {
  2446. pr = this._proportionallyResizeElements;
  2447. ista = pr.length && (/textarea/i).test(pr[0].nodeName);
  2448. soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height;
  2449. soffsetw = ista ? 0 : that.sizeDiff.width;
  2450. s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) };
  2451. left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null;
  2452. top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
  2453. if (!o.animate) {
  2454. this.element.css($.extend(s, { top: top, left: left }));
  2455. }
  2456. that.helper.height(that.size.height);
  2457. that.helper.width(that.size.width);
  2458. if (this._helper && !o.animate) {
  2459. this._proportionallyResize();
  2460. }
  2461. }
  2462. $("body").css("cursor", "auto");
  2463. this.element.removeClass("ui-resizable-resizing");
  2464. this._propagate("stop", event);
  2465. if (this._helper) {
  2466. this.helper.remove();
  2467. }
  2468. return false;
  2469. },
  2470. _updateVirtualBoundaries: function(forceAspectRatio) {
  2471. var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
  2472. o = this.options;
  2473. b = {
  2474. minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
  2475. maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
  2476. minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
  2477. maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
  2478. };
  2479. if(this._aspectRatio || forceAspectRatio) {
  2480. // We want to create an enclosing box whose aspect ration is the requested one
  2481. // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
  2482. pMinWidth = b.minHeight * this.aspectRatio;
  2483. pMinHeight = b.minWidth / this.aspectRatio;
  2484. pMaxWidth = b.maxHeight * this.aspectRatio;
  2485. pMaxHeight = b.maxWidth / this.aspectRatio;
  2486. if(pMinWidth > b.minWidth) {
  2487. b.minWidth = pMinWidth;
  2488. }
  2489. if(pMinHeight > b.minHeight) {
  2490. b.minHeight = pMinHeight;
  2491. }
  2492. if(pMaxWidth < b.maxWidth) {
  2493. b.maxWidth = pMaxWidth;
  2494. }
  2495. if(pMaxHeight < b.maxHeight) {
  2496. b.maxHeight = pMaxHeight;
  2497. }
  2498. }
  2499. this._vBoundaries = b;
  2500. },
  2501. _updateCache: function(data) {
  2502. this.offset = this.helper.offset();
  2503. if (isNumber(data.left)) {
  2504. this.position.left = data.left;
  2505. }
  2506. if (isNumber(data.top)) {
  2507. this.position.top = data.top;
  2508. }
  2509. if (isNumber(data.height)) {
  2510. this.size.height = data.height;
  2511. }
  2512. if (isNumber(data.width)) {
  2513. this.size.width = data.width;
  2514. }
  2515. },
  2516. _updateRatio: function( data ) {
  2517. var cpos = this.position,
  2518. csize = this.size,
  2519. a = this.axis;
  2520. if (isNumber(data.height)) {
  2521. data.width = (data.height * this.aspectRatio);
  2522. } else if (isNumber(data.width)) {
  2523. data.height = (data.width / this.aspectRatio);
  2524. }
  2525. if (a === "sw") {
  2526. data.left = cpos.left + (csize.width - data.width);
  2527. data.top = null;
  2528. }
  2529. if (a === "nw") {
  2530. data.top = cpos.top + (csize.height - data.height);
  2531. data.left = cpos.left + (csize.width - data.width);
  2532. }
  2533. return data;
  2534. },
  2535. _respectSize: function( data ) {
  2536. var o = this._vBoundaries,
  2537. a = this.axis,
  2538. ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
  2539. isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height),
  2540. dw = this.originalPosition.left + this.originalSize.width,
  2541. dh = this.position.top + this.size.height,
  2542. cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
  2543. if (isminw) {
  2544. data.width = o.minWidth;
  2545. }
  2546. if (isminh) {
  2547. data.height = o.minHeight;
  2548. }
  2549. if (ismaxw) {
  2550. data.width = o.maxWidth;
  2551. }
  2552. if (ismaxh) {
  2553. data.height = o.maxHeight;
  2554. }
  2555. if (isminw && cw) {
  2556. data.left = dw - o.minWidth;
  2557. }
  2558. if (ismaxw && cw) {
  2559. data.left = dw - o.maxWidth;
  2560. }
  2561. if (isminh && ch) {
  2562. data.top = dh - o.minHeight;
  2563. }
  2564. if (ismaxh && ch) {
  2565. data.top = dh - o.maxHeight;
  2566. }
  2567. // fixing jump error on top/left - bug #2330
  2568. if (!data.width && !data.height && !data.left && data.top) {
  2569. data.top = null;
  2570. } else if (!data.width && !data.height && !data.top && data.left) {
  2571. data.left = null;
  2572. }
  2573. return data;
  2574. },
  2575. _proportionallyResize: function() {
  2576. if (!this._proportionallyResizeElements.length) {
  2577. return;
  2578. }
  2579. var i, j, borders, paddings, prel,
  2580. element = this.helper || this.element;
  2581. for ( i=0; i < this._proportionallyResizeElements.length; i++) {
  2582. prel = this._proportionallyResizeElements[i];
  2583. if (!this.borderDif) {
  2584. this.borderDif = [];
  2585. borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")];
  2586. paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")];
  2587. for ( j = 0; j < borders.length; j++ ) {
  2588. this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 );
  2589. }
  2590. }
  2591. prel.css({
  2592. height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
  2593. width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
  2594. });
  2595. }
  2596. },
  2597. _renderProxy: function() {
  2598. var el = this.element, o = this.options;
  2599. this.elementOffset = el.offset();
  2600. if(this._helper) {
  2601. this.helper = this.helper || $("<div style='overflow:hidden;'></div>");
  2602. this.helper.addClass(this._helper).css({
  2603. width: this.element.outerWidth() - 1,
  2604. height: this.element.outerHeight() - 1,
  2605. position: "absolute",
  2606. left: this.elementOffset.left +"px",
  2607. top: this.elementOffset.top +"px",
  2608. zIndex: ++o.zIndex //TODO: Don't modify option
  2609. });
  2610. this.helper
  2611. .appendTo("body")
  2612. .disableSelection();
  2613. } else {
  2614. this.helper = this.element;
  2615. }
  2616. },
  2617. _change: {
  2618. e: function(event, dx) {
  2619. return { width: this.originalSize.width + dx };
  2620. },
  2621. w: function(event, dx) {
  2622. var cs = this.originalSize, sp = this.originalPosition;
  2623. return { left: sp.left + dx, width: cs.width - dx };
  2624. },
  2625. n: function(event, dx, dy) {
  2626. var cs = this.originalSize, sp = this.originalPosition;
  2627. return { top: sp.top + dy, height: cs.height - dy };
  2628. },
  2629. s: function(event, dx, dy) {
  2630. return { height: this.originalSize.height + dy };
  2631. },
  2632. se: function(event, dx, dy) {
  2633. return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
  2634. },
  2635. sw: function(event, dx, dy) {
  2636. return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
  2637. },
  2638. ne: function(event, dx, dy) {
  2639. return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
  2640. },
  2641. nw: function(event, dx, dy) {
  2642. return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
  2643. }
  2644. },
  2645. _propagate: function(n, event) {
  2646. $.ui.plugin.call(this, n, [event, this.ui()]);
  2647. (n !== "resize" && this._trigger(n, event, this.ui()));
  2648. },
  2649. plugins: {},
  2650. ui: function() {
  2651. return {
  2652. originalElement: this.originalElement,
  2653. element: this.element,
  2654. helper: this.helper,
  2655. position: this.position,
  2656. size: this.size,
  2657. originalSize: this.originalSize,
  2658. originalPosition: this.originalPosition
  2659. };
  2660. }
  2661. });
  2662. /*
  2663. * Resizable Extensions
  2664. */
  2665. $.ui.plugin.add("resizable", "animate", {
  2666. stop: function( event ) {
  2667. var that = $(this).data("ui-resizable"),
  2668. o = that.options,
  2669. pr = that._proportionallyResizeElements,
  2670. ista = pr.length && (/textarea/i).test(pr[0].nodeName),
  2671. soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height,
  2672. soffsetw = ista ? 0 : that.sizeDiff.width,
  2673. style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
  2674. left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null,
  2675. top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
  2676. that.element.animate(
  2677. $.extend(style, top && left ? { top: top, left: left } : {}), {
  2678. duration: o.animateDuration,
  2679. easing: o.animateEasing,
  2680. step: function() {
  2681. var data = {
  2682. width: parseInt(that.element.css("width"), 10),
  2683. height: parseInt(that.element.css("height"), 10),
  2684. top: parseInt(that.element.css("top"), 10),
  2685. left: parseInt(that.element.css("left"), 10)
  2686. };
  2687. if (pr && pr.length) {
  2688. $(pr[0]).css({ width: data.width, height: data.height });
  2689. }
  2690. // propagating resize, and updating values for each animation step
  2691. that._updateCache(data);
  2692. that._propagate("resize", event);
  2693. }
  2694. }
  2695. );
  2696. }
  2697. });
  2698. $.ui.plugin.add("resizable", "containment", {
  2699. start: function() {
  2700. var element, p, co, ch, cw, width, height,
  2701. that = $(this).data("ui-resizable"),
  2702. o = that.options,
  2703. el = that.element,
  2704. oc = o.containment,
  2705. ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
  2706. if (!ce) {
  2707. return;
  2708. }
  2709. that.containerElement = $(ce);
  2710. if (/document/.test(oc) || oc === document) {
  2711. that.containerOffset = { left: 0, top: 0 };
  2712. that.containerPosition = { left: 0, top: 0 };
  2713. that.parentData = {
  2714. element: $(document), left: 0, top: 0,
  2715. width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
  2716. };
  2717. }
  2718. // i'm a node, so compute top, left, right, bottom
  2719. else {
  2720. element = $(ce);
  2721. p = [];
  2722. $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
  2723. that.containerOffset = element.offset();
  2724. that.containerPosition = element.position();
  2725. that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
  2726. co = that.containerOffset;
  2727. ch = that.containerSize.height;
  2728. cw = that.containerSize.width;
  2729. width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw );
  2730. height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
  2731. that.parentData = {
  2732. element: ce, left: co.left, top: co.top, width: width, height: height
  2733. };
  2734. }
  2735. },
  2736. resize: function( event ) {
  2737. var woset, hoset, isParent, isOffsetRelative,
  2738. that = $(this).data("ui-resizable"),
  2739. o = that.options,
  2740. co = that.containerOffset, cp = that.position,
  2741. pRatio = that._aspectRatio || event.shiftKey,
  2742. cop = { top:0, left:0 }, ce = that.containerElement;
  2743. if (ce[0] !== document && (/static/).test(ce.css("position"))) {
  2744. cop = co;
  2745. }
  2746. if (cp.left < (that._helper ? co.left : 0)) {
  2747. that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left));
  2748. if (pRatio) {
  2749. that.size.height = that.size.width / that.aspectRatio;
  2750. }
  2751. that.position.left = o.helper ? co.left : 0;
  2752. }
  2753. if (cp.top < (that._helper ? co.top : 0)) {
  2754. that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top);
  2755. if (pRatio) {
  2756. that.size.width = that.size.height * that.aspectRatio;
  2757. }
  2758. that.position.top = that._helper ? co.top : 0;
  2759. }
  2760. that.offset.left = that.parentData.left+that.position.left;
  2761. that.offset.top = that.parentData.top+that.position.top;
  2762. woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width );
  2763. hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height );
  2764. isParent = that.containerElement.get(0) === that.element.parent().get(0);
  2765. isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position"));
  2766. if(isParent && isOffsetRelative) {
  2767. woset -= that.parentData.left;
  2768. }
  2769. if (woset + that.size.width >= that.parentData.width) {
  2770. that.size.width = that.parentData.width - woset;
  2771. if (pRatio) {
  2772. that.size.height = that.size.width / that.aspectRatio;
  2773. }
  2774. }
  2775. if (hoset + that.size.height >= that.parentData.height) {
  2776. that.size.height = that.parentData.height - hoset;
  2777. if (pRatio) {
  2778. that.size.width = that.size.height * that.aspectRatio;
  2779. }
  2780. }
  2781. },
  2782. stop: function(){
  2783. var that = $(this).data("ui-resizable"),
  2784. o = that.options,
  2785. co = that.containerOffset,
  2786. cop = that.containerPosition,
  2787. ce = that.containerElement,
  2788. helper = $(that.helper),
  2789. ho = helper.offset(),
  2790. w = helper.outerWidth() - that.sizeDiff.width,
  2791. h = helper.outerHeight() - that.sizeDiff.height;
  2792. if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) {
  2793. $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
  2794. }
  2795. if (that._helper && !o.animate && (/static/).test(ce.css("position"))) {
  2796. $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
  2797. }
  2798. }
  2799. });
  2800. $.ui.plugin.add("resizable", "alsoResize", {
  2801. start: function () {
  2802. var that = $(this).data("ui-resizable"),
  2803. o = that.options,
  2804. _store = function (exp) {
  2805. $(exp).each(function() {
  2806. var el = $(this);
  2807. el.data("ui-resizable-alsoresize", {
  2808. width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
  2809. left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
  2810. });
  2811. });
  2812. };
  2813. if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) {
  2814. if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
  2815. else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
  2816. }else{
  2817. _store(o.alsoResize);
  2818. }
  2819. },
  2820. resize: function (event, ui) {
  2821. var that = $(this).data("ui-resizable"),
  2822. o = that.options,
  2823. os = that.originalSize,
  2824. op = that.originalPosition,
  2825. delta = {
  2826. height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0,
  2827. top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0
  2828. },
  2829. _alsoResize = function (exp, c) {
  2830. $(exp).each(function() {
  2831. var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
  2832. css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"];
  2833. $.each(css, function (i, prop) {
  2834. var sum = (start[prop]||0) + (delta[prop]||0);
  2835. if (sum && sum >= 0) {
  2836. style[prop] = sum || null;
  2837. }
  2838. });
  2839. el.css(style);
  2840. });
  2841. };
  2842. if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) {
  2843. $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
  2844. }else{
  2845. _alsoResize(o.alsoResize);
  2846. }
  2847. },
  2848. stop: function () {
  2849. $(this).removeData("resizable-alsoresize");
  2850. }
  2851. });
  2852. $.ui.plugin.add("resizable", "ghost", {
  2853. start: function() {
  2854. var that = $(this).data("ui-resizable"), o = that.options, cs = that.size;
  2855. that.ghost = that.originalElement.clone();
  2856. that.ghost
  2857. .css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
  2858. .addClass("ui-resizable-ghost")
  2859. .addClass(typeof o.ghost === "string" ? o.ghost : "");
  2860. that.ghost.appendTo(that.helper);
  2861. },
  2862. resize: function(){
  2863. var that = $(this).data("ui-resizable");
  2864. if (that.ghost) {
  2865. that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width });
  2866. }
  2867. },
  2868. stop: function() {
  2869. var that = $(this).data("ui-resizable");
  2870. if (that.ghost && that.helper) {
  2871. that.helper.get(0).removeChild(that.ghost.get(0));
  2872. }
  2873. }
  2874. });
  2875. $.ui.plugin.add("resizable", "grid", {
  2876. resize: function() {
  2877. var that = $(this).data("ui-resizable"),
  2878. o = that.options,
  2879. cs = that.size,
  2880. os = that.originalSize,
  2881. op = that.originalPosition,
  2882. a = that.axis,
  2883. grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid,
  2884. gridX = (grid[0]||1),
  2885. gridY = (grid[1]||1),
  2886. ox = Math.round((cs.width - os.width) / gridX) * gridX,
  2887. oy = Math.round((cs.height - os.height) / gridY) * gridY,
  2888. newWidth = os.width + ox,
  2889. newHeight = os.height + oy,
  2890. isMaxWidth = o.maxWidth && (o.maxWidth < newWidth),
  2891. isMaxHeight = o.maxHeight && (o.maxHeight < newHeight),
  2892. isMinWidth = o.minWidth && (o.minWidth > newWidth),
  2893. isMinHeight = o.minHeight && (o.minHeight > newHeight);
  2894. o.grid = grid;
  2895. if (isMinWidth) {
  2896. newWidth = newWidth + gridX;
  2897. }
  2898. if (isMinHeight) {
  2899. newHeight = newHeight + gridY;
  2900. }
  2901. if (isMaxWidth) {
  2902. newWidth = newWidth - gridX;
  2903. }
  2904. if (isMaxHeight) {
  2905. newHeight = newHeight - gridY;
  2906. }
  2907. if (/^(se|s|e)$/.test(a)) {
  2908. that.size.width = newWidth;
  2909. that.size.height = newHeight;
  2910. } else if (/^(ne)$/.test(a)) {
  2911. that.size.width = newWidth;
  2912. that.size.height = newHeight;
  2913. that.position.top = op.top - oy;
  2914. } else if (/^(sw)$/.test(a)) {
  2915. that.size.width = newWidth;
  2916. that.size.height = newHeight;
  2917. that.position.left = op.left - ox;
  2918. } else {
  2919. that.size.width = newWidth;
  2920. that.size.height = newHeight;
  2921. that.position.top = op.top - oy;
  2922. that.position.left = op.left - ox;
  2923. }
  2924. }
  2925. });
  2926. })(jQuery);
  2927. /**
  2928. * @preserve
  2929. * jquery.layout 1.3.0 - Release Candidate 30.79
  2930. * $Date: 2013-01-12 08:00:00 (Sat, 12 Jan 2013) $
  2931. * $Rev: 303007 $
  2932. *
  2933. * Copyright (c) 2013 Kevin Dalman (http://allpro.net)
  2934. * Based on work by Fabrizio Balliano (http://www.fabrizioballiano.net)
  2935. *
  2936. * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
  2937. * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
  2938. *
  2939. * SEE: http://layout.jquery-dev.net/LICENSE.txt
  2940. *
  2941. * Changelog: http://layout.jquery-dev.net/changelog.cfm#1.3.0.rc30.79
  2942. *
  2943. * Docs: http://layout.jquery-dev.net/documentation.html
  2944. * Tips: http://layout.jquery-dev.net/tips.html
  2945. * Help: http://groups.google.com/group/jquery-ui-layout
  2946. */
  2947. /* JavaDoc Info: http://code.google.com/closure/compiler/docs/js-for-compiler.html
  2948. * {!Object} non-nullable type (never NULL)
  2949. * {?string} nullable type (sometimes NULL) - default for {Object}
  2950. * {number=} optional parameter
  2951. * {*} ALL types
  2952. */
  2953. /* TODO for jQ 2.0
  2954. * change .andSelf() to .addBack()
  2955. * $.fn.disableSelection won't work
  2956. */
  2957. // NOTE: For best readability, view with a fixed-width font and tabs equal to 4-chars
  2958. ;(function ($) {
  2959. // alias Math methods - used a lot!
  2960. var min = Math.min
  2961. , max = Math.max
  2962. , round = Math.floor
  2963. , isStr = function (v) { return $.type(v) === "string"; }
  2964. /**
  2965. * @param {!Object} Instance
  2966. * @param {Array.<string>} a_fn
  2967. */
  2968. , runPluginCallbacks = function (Instance, a_fn) {
  2969. if ($.isArray(a_fn))
  2970. for (var i=0, c=a_fn.length; i<c; i++) {
  2971. var fn = a_fn[i];
  2972. try {
  2973. if (isStr(fn)) // 'name' of a function
  2974. fn = eval(fn);
  2975. if ($.isFunction(fn))
  2976. g(fn)( Instance );
  2977. } catch (ex) {}
  2978. }
  2979. function g (f) { return f; }; // compiler hack
  2980. }
  2981. ;
  2982. /*
  2983. * GENERIC $.layout METHODS - used by all layouts
  2984. */
  2985. $.layout = {
  2986. version: "1.3.rc30.79"
  2987. , revision: 0.033007 // 1.3.0 final = 1.0300 - major(n+).minor(nn)+patch(nn+)
  2988. // $.layout.browser REPLACES $.browser
  2989. , browser: {} // set below
  2990. // *PREDEFINED* EFFECTS & DEFAULTS
  2991. // MUST list effect here - OR MUST set an fxSettings option (can be an empty hash: {})
  2992. , effects: {
  2993. // Pane Open/Close Animations
  2994. slide: {
  2995. all: { duration: "fast" } // eg: duration: 1000, easing: "easeOutBounce"
  2996. , north: { direction: "up" }
  2997. , south: { direction: "down" }
  2998. , east: { direction: "right"}
  2999. , west: { direction: "left" }
  3000. }
  3001. , drop: {
  3002. all: { duration: "slow" }
  3003. , north: { direction: "up" }
  3004. , south: { direction: "down" }
  3005. , east: { direction: "right"}
  3006. , west: { direction: "left" }
  3007. }
  3008. , scale: {
  3009. all: { duration: "fast" }
  3010. }
  3011. // these are not recommended, but can be used
  3012. , blind: {}
  3013. , clip: {}
  3014. , explode: {}
  3015. , fade: {}
  3016. , fold: {}
  3017. , puff: {}
  3018. // Pane Resize Animations
  3019. , size: {
  3020. all: { easing: "swing" }
  3021. }
  3022. }
  3023. // INTERNAL CONFIG DATA - DO NOT CHANGE THIS!
  3024. , config: {
  3025. optionRootKeys: "effects,panes,north,south,west,east,center".split(",")
  3026. , allPanes: "north,south,west,east,center".split(",")
  3027. , borderPanes: "north,south,west,east".split(",")
  3028. , oppositeEdge: {
  3029. north: "south"
  3030. , south: "north"
  3031. , east: "west"
  3032. , west: "east"
  3033. }
  3034. // offscreen data
  3035. , offscreenCSS: { left: "-99999px", right: "auto" } // used by hide/close if useOffscreenClose=true
  3036. , offscreenReset: "offscreenReset" // key used for data
  3037. // CSS used in multiple places
  3038. , hidden: { visibility: "hidden" }
  3039. , visible: { visibility: "visible" }
  3040. // layout element settings
  3041. , resizers: {
  3042. cssReq: {
  3043. position: "absolute"
  3044. , padding: 0
  3045. , margin: 0
  3046. , fontSize: "1px"
  3047. , textAlign: "left" // to counter-act "center" alignment!
  3048. , overflow: "hidden" // prevent toggler-button from overflowing
  3049. // SEE $.layout.defaults.zIndexes.resizer_normal
  3050. }
  3051. , cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
  3052. background: "#DDD"
  3053. , border: "none"
  3054. }
  3055. }
  3056. , togglers: {
  3057. cssReq: {
  3058. position: "absolute"
  3059. , display: "block"
  3060. , padding: 0
  3061. , margin: 0
  3062. , overflow: "hidden"
  3063. , textAlign: "center"
  3064. , fontSize: "1px"
  3065. , cursor: "pointer"
  3066. , zIndex: 1
  3067. }
  3068. , cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
  3069. background: "#AAA"
  3070. }
  3071. }
  3072. , content: {
  3073. cssReq: {
  3074. position: "relative" /* contain floated or positioned elements */
  3075. }
  3076. , cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
  3077. overflow: "auto"
  3078. , padding: "10px"
  3079. }
  3080. , cssDemoPane: { // DEMO CSS - REMOVE scrolling from 'pane' when it has a content-div
  3081. overflow: "hidden"
  3082. , padding: 0
  3083. }
  3084. }
  3085. , panes: { // defaults for ALL panes - overridden by 'per-pane settings' below
  3086. cssReq: {
  3087. position: "absolute"
  3088. , margin: 0
  3089. // $.layout.defaults.zIndexes.pane_normal
  3090. }
  3091. , cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
  3092. padding: "10px"
  3093. , background: "#FFF"
  3094. , border: "1px solid #BBB"
  3095. , overflow: "auto"
  3096. }
  3097. }
  3098. , north: {
  3099. side: "top"
  3100. , sizeType: "Height"
  3101. , dir: "horz"
  3102. , cssReq: {
  3103. top: 0
  3104. , bottom: "auto"
  3105. , left: 0
  3106. , right: 0
  3107. , width: "auto"
  3108. // height: DYNAMIC
  3109. }
  3110. }
  3111. , south: {
  3112. side: "bottom"
  3113. , sizeType: "Height"
  3114. , dir: "horz"
  3115. , cssReq: {
  3116. top: "auto"
  3117. , bottom: 0
  3118. , left: 0
  3119. , right: 0
  3120. , width: "auto"
  3121. // height: DYNAMIC
  3122. }
  3123. }
  3124. , east: {
  3125. side: "right"
  3126. , sizeType: "Width"
  3127. , dir: "vert"
  3128. , cssReq: {
  3129. left: "auto"
  3130. , right: 0
  3131. , top: "auto" // DYNAMIC
  3132. , bottom: "auto" // DYNAMIC
  3133. , height: "auto"
  3134. // width: DYNAMIC
  3135. }
  3136. }
  3137. , west: {
  3138. side: "left"
  3139. , sizeType: "Width"
  3140. , dir: "vert"
  3141. , cssReq: {
  3142. left: 0
  3143. , right: "auto"
  3144. , top: "auto" // DYNAMIC
  3145. , bottom: "auto" // DYNAMIC
  3146. , height: "auto"
  3147. // width: DYNAMIC
  3148. }
  3149. }
  3150. , center: {
  3151. dir: "center"
  3152. , cssReq: {
  3153. left: "auto" // DYNAMIC
  3154. , right: "auto" // DYNAMIC
  3155. , top: "auto" // DYNAMIC
  3156. , bottom: "auto" // DYNAMIC
  3157. , height: "auto"
  3158. , width: "auto"
  3159. }
  3160. }
  3161. }
  3162. // CALLBACK FUNCTION NAMESPACE - used to store reusable callback functions
  3163. , callbacks: {}
  3164. , getParentPaneElem: function (el) {
  3165. // must pass either a container or pane element
  3166. var $el = $(el)
  3167. , layout = $el.data("layout") || $el.data("parentLayout");
  3168. if (layout) {
  3169. var $cont = layout.container;
  3170. // see if this container is directly-nested inside an outer-pane
  3171. if ($cont.data("layoutPane")) return $cont;
  3172. var $pane = $cont.closest("."+ $.layout.defaults.panes.paneClass);
  3173. // if a pane was found, return it
  3174. if ($pane.data("layoutPane")) return $pane;
  3175. }
  3176. return null;
  3177. }
  3178. , getParentPaneInstance: function (el) {
  3179. // must pass either a container or pane element
  3180. var $pane = $.layout.getParentPaneElem(el);
  3181. return $pane ? $pane.data("layoutPane") : null;
  3182. }
  3183. , getParentLayoutInstance: function (el) {
  3184. // must pass either a container or pane element
  3185. var $pane = $.layout.getParentPaneElem(el);
  3186. return $pane ? $pane.data("parentLayout") : null;
  3187. }
  3188. , getEventObject: function (evt) {
  3189. return typeof evt === "object" && evt.stopPropagation ? evt : null;
  3190. }
  3191. , parsePaneName: function (evt_or_pane) {
  3192. var evt = $.layout.getEventObject( evt_or_pane )
  3193. , pane = evt_or_pane;
  3194. if (evt) {
  3195. // ALWAYS stop propagation of events triggered in Layout!
  3196. evt.stopPropagation();
  3197. pane = $(this).data("layoutEdge");
  3198. }
  3199. if (pane && !/^(west|east|north|south|center)$/.test(pane)) {
  3200. $.layout.msg('LAYOUT ERROR - Invalid pane-name: "'+ pane +'"');
  3201. pane = "error";
  3202. }
  3203. return pane;
  3204. }
  3205. // LAYOUT-PLUGIN REGISTRATION
  3206. // more plugins can added beyond this default list
  3207. , plugins: {
  3208. draggable: !!$.fn.draggable // resizing
  3209. , effects: {
  3210. core: !!$.effects // animimations (specific effects tested by initOptions)
  3211. , slide: $.effects && ($.effects.slide || ($.effects.effect && $.effects.effect.slide)) // default effect
  3212. }
  3213. }
  3214. // arrays of plugin or other methods to be triggered for events in *each layout* - will be passed 'Instance'
  3215. , onCreate: [] // runs when layout is just starting to be created - right after options are set
  3216. , onLoad: [] // runs after layout container and global events init, but before initPanes is called
  3217. , onReady: [] // runs after initialization *completes* - ie, after initPanes completes successfully
  3218. , onDestroy: [] // runs after layout is destroyed
  3219. , onUnload: [] // runs after layout is destroyed OR when page unloads
  3220. , afterOpen: [] // runs after setAsOpen() completes
  3221. , afterClose: [] // runs after setAsClosed() completes
  3222. /*
  3223. * GENERIC UTILITY METHODS
  3224. */
  3225. // calculate and return the scrollbar width, as an integer
  3226. , scrollbarWidth: function () { return window.scrollbarWidth || $.layout.getScrollbarSize('width'); }
  3227. , scrollbarHeight: function () { return window.scrollbarHeight || $.layout.getScrollbarSize('height'); }
  3228. , getScrollbarSize: function (dim) {
  3229. var $c = $('<div style="position: absolute; top: -10000px; left: -10000px; width: 100px; height: 100px; overflow: scroll;"></div>').appendTo("body");
  3230. var d = { width: $c.css("width") - $c[0].clientWidth, height: $c.height() - $c[0].clientHeight };
  3231. $c.remove();
  3232. window.scrollbarWidth = d.width;
  3233. window.scrollbarHeight = d.height;
  3234. return dim.match(/^(width|height)$/) ? d[dim] : d;
  3235. }
  3236. /**
  3237. * Returns hash container 'display' and 'visibility'
  3238. *
  3239. * @see $.swap() - swaps CSS, runs callback, resets CSS
  3240. * @param {!Object} $E jQuery element
  3241. * @param {boolean=} [force=false] Run even if display != none
  3242. * @return {!Object} Returns current style props, if applicable
  3243. */
  3244. , showInvisibly: function ($E, force) {
  3245. if ($E && $E.length && (force || $E.css("display") === "none")) { // only if not *already hidden*
  3246. var s = $E[0].style
  3247. // save ONLY the 'style' props because that is what we must restore
  3248. , CSS = { display: s.display || '', visibility: s.visibility || '' };
  3249. // show element 'invisibly' so can be measured
  3250. $E.css({ display: "block", visibility: "hidden" });
  3251. return CSS;
  3252. }
  3253. return {};
  3254. }
  3255. /**
  3256. * Returns data for setting size of an element (container or a pane).
  3257. *
  3258. * @see _create(), onWindowResize() for container, plus others for pane
  3259. * @return JSON Returns a hash of all dimensions: top, bottom, left, right, outerWidth, innerHeight, etc
  3260. */
  3261. , getElementDimensions: function ($E, inset) {
  3262. var
  3263. // dimensions hash - start with current data IF passed
  3264. d = { css: {}, inset: {} }
  3265. , x = d.css // CSS hash
  3266. , i = { bottom: 0 } // TEMP insets (bottom = complier hack)
  3267. , N = $.layout.cssNum
  3268. , off = $E.offset()
  3269. , b, p, ei // TEMP border, padding
  3270. ;
  3271. d.offsetLeft = off.left;
  3272. d.offsetTop = off.top;
  3273. if (!inset) inset = {}; // simplify logic below
  3274. $.each("Left,Right,Top,Bottom".split(","), function (idx, e) { // e = edge
  3275. b = x["border" + e] = $.layout.borderWidth($E, e);
  3276. p = x["padding"+ e] = $.layout.cssNum($E, "padding"+e);
  3277. ei = e.toLowerCase();
  3278. d.inset[ei] = inset[ei] >= 0 ? inset[ei] : p; // any missing insetX value = paddingX
  3279. i[ei] = d.inset[ei] + b; // total offset of content from outer side
  3280. });
  3281. x.width = $E.width();
  3282. x.height = $E.height();
  3283. x.top = N($E,"top",true);
  3284. x.bottom = N($E,"bottom",true);
  3285. x.left = N($E,"left",true);
  3286. x.right = N($E,"right",true);
  3287. d.outerWidth = $E.outerWidth();
  3288. d.outerHeight = $E.outerHeight();
  3289. // calc the TRUE inner-dimensions, even in quirks-mode!
  3290. d.innerWidth = max(0, d.outerWidth - i.left - i.right);
  3291. d.innerHeight = max(0, d.outerHeight - i.top - i.bottom);
  3292. // layoutWidth/Height is used in calcs for manual resizing
  3293. // layoutW/H only differs from innerW/H when in quirks-mode - then is like outerW/H
  3294. d.layoutWidth = $E.innerWidth();
  3295. d.layoutHeight = $E.innerHeight();
  3296. //if ($E.prop('tagName') === 'BODY') { debugData( d, $E.prop('tagName') ); } // DEBUG
  3297. //d.visible = $E.is(":visible");// && x.width > 0 && x.height > 0;
  3298. return d;
  3299. }
  3300. , getElementStyles: function ($E, list) {
  3301. var
  3302. CSS = {}
  3303. , style = $E[0].style
  3304. , props = list.split(",")
  3305. , sides = "Top,Bottom,Left,Right".split(",")
  3306. , attrs = "Color,Style,Width".split(",")
  3307. , p, s, a, i, j, k
  3308. ;
  3309. for (i=0; i < props.length; i++) {
  3310. p = props[i];
  3311. if (p.match(/(border|padding|margin)$/))
  3312. for (j=0; j < 4; j++) {
  3313. s = sides[j];
  3314. if (p === "border")
  3315. for (k=0; k < 3; k++) {
  3316. a = attrs[k];
  3317. CSS[p+s+a] = style[p+s+a];
  3318. }
  3319. else
  3320. CSS[p+s] = style[p+s];
  3321. }
  3322. else
  3323. CSS[p] = style[p];
  3324. };
  3325. return CSS
  3326. }
  3327. /**
  3328. * Return the innerWidth for the current browser/doctype
  3329. *
  3330. * @see initPanes(), sizeMidPanes(), initHandles(), sizeHandles()
  3331. * @param {Array.<Object>} $E Must pass a jQuery object - first element is processed
  3332. * @param {number=} outerWidth (optional) Can pass a width, allowing calculations BEFORE element is resized
  3333. * @return {number} Returns the innerWidth of the elem by subtracting padding and borders
  3334. */
  3335. , cssWidth: function ($E, outerWidth) {
  3336. // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
  3337. if (outerWidth <= 0) return 0;
  3338. var bs = !$.layout.browser.boxModel ? "border-box" : $.support.boxSizing ? $E.css("boxSizing") : "content-box"
  3339. , b = $.layout.borderWidth
  3340. , n = $.layout.cssNum
  3341. , W = outerWidth
  3342. ;
  3343. // strip border and/or padding from outerWidth to get CSS Width
  3344. if (bs !== "border-box")
  3345. W -= (b($E, "Left") + b($E, "Right"));
  3346. if (bs === "content-box")
  3347. W -= (n($E, "paddingLeft") + n($E, "paddingRight"));
  3348. return max(0,W);
  3349. }
  3350. /**
  3351. * Return the innerHeight for the current browser/doctype
  3352. *
  3353. * @see initPanes(), sizeMidPanes(), initHandles(), sizeHandles()
  3354. * @param {Array.<Object>} $E Must pass a jQuery object - first element is processed
  3355. * @param {number=} outerHeight (optional) Can pass a width, allowing calculations BEFORE element is resized
  3356. * @return {number} Returns the innerHeight of the elem by subtracting padding and borders
  3357. */
  3358. , cssHeight: function ($E, outerHeight) {
  3359. // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
  3360. if (outerHeight <= 0) return 0;
  3361. var bs = !$.layout.browser.boxModel ? "border-box" : $.support.boxSizing ? $E.css("boxSizing") : "content-box"
  3362. , b = $.layout.borderWidth
  3363. , n = $.layout.cssNum
  3364. , H = outerHeight
  3365. ;
  3366. // strip border and/or padding from outerHeight to get CSS Height
  3367. if (bs !== "border-box")
  3368. H -= (b($E, "Top") + b($E, "Bottom"));
  3369. if (bs === "content-box")
  3370. H -= (n($E, "paddingTop") + n($E, "paddingBottom"));
  3371. return max(0,H);
  3372. }
  3373. /**
  3374. * Returns the 'current CSS numeric value' for a CSS property - 0 if property does not exist
  3375. *
  3376. * @see Called by many methods
  3377. * @param {Array.<Object>} $E Must pass a jQuery object - first element is processed
  3378. * @param {string} prop The name of the CSS property, eg: top, width, etc.
  3379. * @param {boolean=} [allowAuto=false] true = return 'auto' if that is value; false = return 0
  3380. * @return {(string|number)} Usually used to get an integer value for position (top, left) or size (height, width)
  3381. */
  3382. , cssNum: function ($E, prop, allowAuto) {
  3383. if (!$E.jquery) $E = $($E);
  3384. var CSS = $.layout.showInvisibly($E)
  3385. , p = $.css($E[0], prop, true)
  3386. , v = allowAuto && p=="auto" ? p : Math.round(parseFloat(p) || 0);
  3387. $E.css( CSS ); // RESET
  3388. return v;
  3389. }
  3390. , borderWidth: function (el, side) {
  3391. if (el.jquery) el = el[0];
  3392. var b = "border"+ side.substr(0,1).toUpperCase() + side.substr(1); // left => Left
  3393. return $.css(el, b+"Style", true) === "none" ? 0 : Math.round(parseFloat($.css(el, b+"Width", true)) || 0);
  3394. }
  3395. /**
  3396. * Mouse-tracking utility - FUTURE REFERENCE
  3397. *
  3398. * init: if (!window.mouse) {
  3399. * window.mouse = { x: 0, y: 0 };
  3400. * $(document).mousemove( $.layout.trackMouse );
  3401. * }
  3402. *
  3403. * @param {Object} evt
  3404. *
  3405. , trackMouse: function (evt) {
  3406. window.mouse = { x: evt.clientX, y: evt.clientY };
  3407. }
  3408. */
  3409. /**
  3410. * SUBROUTINE for preventPrematureSlideClose option
  3411. *
  3412. * @param {Object} evt
  3413. * @param {Object=} el
  3414. */
  3415. , isMouseOverElem: function (evt, el) {
  3416. var
  3417. $E = $(el || this)
  3418. , d = $E.offset()
  3419. , T = d.top
  3420. , L = d.left
  3421. , R = L + $E.outerWidth()
  3422. , B = T + $E.outerHeight()
  3423. , x = evt.pageX // evt.clientX ?
  3424. , y = evt.pageY // evt.clientY ?
  3425. ;
  3426. // if X & Y are < 0, probably means is over an open SELECT
  3427. return ($.layout.browser.msie && x < 0 && y < 0) || ((x >= L && x <= R) && (y >= T && y <= B));
  3428. }
  3429. /**
  3430. * Message/Logging Utility
  3431. *
  3432. * @example $.layout.msg("My message"); // log text
  3433. * @example $.layout.msg("My message", true); // alert text
  3434. * @example $.layout.msg({ foo: "bar" }, "Title"); // log hash-data, with custom title
  3435. * @example $.layout.msg({ foo: "bar" }, true, "Title", { sort: false }); -OR-
  3436. * @example $.layout.msg({ foo: "bar" }, "Title", { sort: false, display: true }); // alert hash-data
  3437. *
  3438. * @param {(Object|string)} info String message OR Hash/Array
  3439. * @param {(Boolean|string|Object)=} [popup=false] True means alert-box - can be skipped
  3440. * @param {(Object|string)=} [debugTitle=""] Title for Hash data - can be skipped
  3441. * @param {Object=} [debugOpts] Extra options for debug output
  3442. */
  3443. , msg: function (info, popup, debugTitle, debugOpts) {
  3444. if ($.isPlainObject(info) && window.debugData) {
  3445. if (typeof popup === "string") {
  3446. debugOpts = debugTitle;
  3447. debugTitle = popup;
  3448. }
  3449. else if (typeof debugTitle === "object") {
  3450. debugOpts = debugTitle;
  3451. debugTitle = null;
  3452. }
  3453. var t = debugTitle || "log( <object> )"
  3454. , o = $.extend({ sort: false, returnHTML: false, display: false }, debugOpts);
  3455. if (popup === true || o.display)
  3456. debugData( info, t, o );
  3457. else if (window.console)
  3458. console.log(debugData( info, t, o ));
  3459. }
  3460. else if (popup)
  3461. alert(info);
  3462. else if (window.console)
  3463. console.log(info);
  3464. else {
  3465. var id = "#layoutLogger"
  3466. , $l = $(id);
  3467. if (!$l.length)
  3468. $l = createLog();
  3469. $l.children("ul").append('<li style="padding: 4px 10px; margin: 0; border-top: 1px solid #CCC;">'+ info.replace(/\</g,"&lt;").replace(/\>/g,"&gt;") +'</li>');
  3470. }
  3471. function createLog () {
  3472. var pos = $.support.fixedPosition ? 'fixed' : 'absolute'
  3473. , $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);">'
  3474. + '<div style="font-size: 13px; font-weight: bold; padding: 5px 10px; background: #F6F6F6; border-radius: 5px 5px 0 0; cursor: move;">'
  3475. + '<span style="float: right; padding-left: 7px; cursor: pointer;" title="Remove Console" onclick="$(this).closest(\'#layoutLogger\').remove()">X</span>Layout console.log</div>'
  3476. + '<ul style="font-size: 13px; font-weight: none; list-style: none; margin: 0; padding: 0 0 2px;"></ul>'
  3477. + '</div>'
  3478. ).appendTo("body");
  3479. $e.css('left', $(window).width() - $e.outerWidth() - 5)
  3480. if ($.ui.draggable) $e.draggable({ handle: ':first-child' });
  3481. return $e;
  3482. };
  3483. }
  3484. };
  3485. /*
  3486. * $.layout.browser REPLACES removed $.browser, with extra data
  3487. * Parsing code here adapted from jQuery 1.8 $.browse
  3488. */
  3489. var u = navigator.userAgent.toLowerCase()
  3490. , m = /(chrome)[ \/]([\w.]+)/.exec( u )
  3491. || /(webkit)[ \/]([\w.]+)/.exec( u )
  3492. || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( u )
  3493. || /(msie) ([\w.]+)/.exec( u )
  3494. || u.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( u )
  3495. || []
  3496. , b = m[1] || ""
  3497. , v = m[2] || 0
  3498. , ie = b === "msie"
  3499. ;
  3500. $.layout.browser = {
  3501. version: v
  3502. , safari: b === "webkit" // webkit (NOT chrome) = safari
  3503. , webkit: b === "chrome" // chrome = webkit
  3504. , msie: ie
  3505. , isIE6: ie && v == 6
  3506. // ONLY IE reverts to old box-model - update for older jQ onReady
  3507. , boxModel: !ie || $.support.boxModel !== false
  3508. };
  3509. if (b) $.layout.browser[b] = true; // set CURRENT browser
  3510. /* OLD versions of jQuery only set $.support.boxModel after page is loaded
  3511. * so if this is IE, use support.boxModel to test for quirks-mode (ONLY IE changes boxModel) */
  3512. if (ie) $(function(){ $.layout.browser.boxModel = $.support.boxModel; });
  3513. // DEFAULT OPTIONS
  3514. $.layout.defaults = {
  3515. /*
  3516. * LAYOUT & LAYOUT-CONTAINER OPTIONS
  3517. * - none of these options are applicable to individual panes
  3518. */
  3519. name: "" // Not required, but useful for buttons and used for the state-cookie
  3520. , containerClass: "ui-layout-container" // layout-container element
  3521. , inset: null // custom container-inset values (override padding)
  3522. , scrollToBookmarkOnLoad: true // after creating a layout, scroll to bookmark in URL (.../page.htm#myBookmark)
  3523. , resizeWithWindow: true // bind thisLayout.resizeAll() to the window.resize event
  3524. , resizeWithWindowDelay: 200 // delay calling resizeAll because makes window resizing very jerky
  3525. , resizeWithWindowMaxDelay: 0 // 0 = none - force resize every XX ms while window is being resized
  3526. , maskPanesEarly: false // true = create pane-masks on resizer.mouseDown instead of waiting for resizer.dragstart
  3527. , onresizeall_start: null // CALLBACK when resizeAll() STARTS - NOT pane-specific
  3528. , onresizeall_end: null // CALLBACK when resizeAll() ENDS - NOT pane-specific
  3529. , onload_start: null // CALLBACK when Layout inits - after options initialized, but before elements
  3530. , onload_end: null // CALLBACK when Layout inits - after EVERYTHING has been initialized
  3531. , onunload_start: null // CALLBACK when Layout is destroyed OR onWindowUnload
  3532. , onunload_end: null // CALLBACK when Layout is destroyed OR onWindowUnload
  3533. , initPanes: true // false = DO NOT initialize the panes onLoad - will init later
  3534. , showErrorMessages: true // enables fatal error messages to warn developers of common errors
  3535. , showDebugMessages: false // display console-and-alert debug msgs - IF this Layout version _has_ debugging code!
  3536. // Changing this zIndex value will cause other zIndex values to automatically change
  3537. , zIndex: null // the PANE zIndex - resizers and masks will be +1
  3538. // DO NOT CHANGE the zIndex values below unless you clearly understand their relationships
  3539. , zIndexes: { // set _default_ z-index values here...
  3540. pane_normal: 0 // normal z-index for panes
  3541. , content_mask: 1 // applied to overlays used to mask content INSIDE panes during resizing
  3542. , resizer_normal: 2 // normal z-index for resizer-bars
  3543. , pane_sliding: 100 // applied to *BOTH* the pane and its resizer when a pane is 'slid open'
  3544. , pane_animate: 1000 // applied to the pane when being animated - not applied to the resizer
  3545. , resizer_drag: 10000 // applied to the CLONED resizer-bar when being 'dragged'
  3546. }
  3547. , errors: {
  3548. pane: "pane" // description of "layout pane element" - used only in error messages
  3549. , selector: "selector" // description of "jQuery-selector" - used only in error messages
  3550. , addButtonError: "Error Adding Button\nInvalid "
  3551. , containerMissing: "UI Layout Initialization Error\nThe specified layout-container does not exist."
  3552. , centerPaneMissing: "UI Layout Initialization Error\nThe center-pane element does not exist.\nThe center-pane is a required element."
  3553. , noContainerHeight: "UI Layout Initialization Warning\nThe layout-container \"CONTAINER\" has no height.\nTherefore the layout is 0-height and hence 'invisible'!"
  3554. , callbackError: "UI Layout Callback Error\nThe EVENT callback is not a valid function."
  3555. }
  3556. /*
  3557. * PANE DEFAULT SETTINGS
  3558. * - settings under the 'panes' key become the default settings for *all panes*
  3559. * - ALL pane-options can also be set specifically for each panes, which will override these 'default values'
  3560. */
  3561. , panes: { // default options for 'all panes' - will be overridden by 'per-pane settings'
  3562. applyDemoStyles: false // NOTE: renamed from applyDefaultStyles for clarity
  3563. , closable: true // pane can open & close
  3564. , resizable: true // when open, pane can be resized
  3565. , slidable: true // when closed, pane can 'slide open' over other panes - closes on mouse-out
  3566. , initClosed: false // true = init pane as 'closed'
  3567. , initHidden: false // true = init pane as 'hidden' - no resizer-bar/spacing
  3568. // SELECTORS
  3569. //, paneSelector: "" // MUST be pane-specific - jQuery selector for pane
  3570. , contentSelector: ".ui-layout-content" // INNER div/element to auto-size so only it scrolls, not the entire pane!
  3571. , contentIgnoreSelector: ".ui-layout-ignore" // element(s) to 'ignore' when measuring 'content'
  3572. , findNestedContent: false // true = $P.find(contentSelector), false = $P.children(contentSelector)
  3573. // GENERIC ROOT-CLASSES - for auto-generated classNames
  3574. , paneClass: "ui-layout-pane" // Layout Pane
  3575. , resizerClass: "ui-layout-resizer" // Resizer Bar
  3576. , togglerClass: "ui-layout-toggler" // Toggler Button
  3577. , buttonClass: "ui-layout-button" // CUSTOM Buttons - eg: '[ui-layout-button]-toggle/-open/-close/-pin'
  3578. // ELEMENT SIZE & SPACING
  3579. //, size: 100 // MUST be pane-specific -initial size of pane
  3580. , minSize: 0 // when manually resizing a pane
  3581. , maxSize: 0 // ditto, 0 = no limit
  3582. , spacing_open: 6 // space between pane and adjacent panes - when pane is 'open'
  3583. , spacing_closed: 6 // ditto - when pane is 'closed'
  3584. , togglerLength_open: 50 // Length = WIDTH of toggler button on north/south sides - HEIGHT on east/west sides
  3585. , togglerLength_closed: 50 // 100% OR -1 means 'full height/width of resizer bar' - 0 means 'hidden'
  3586. , togglerAlign_open: "center" // top/left, bottom/right, center, OR...
  3587. , togglerAlign_closed: "center" // 1 => nn = offset from top/left, -1 => -nn == offset from bottom/right
  3588. , togglerContent_open: "" // text or HTML to put INSIDE the toggler
  3589. , togglerContent_closed: "" // ditto
  3590. // RESIZING OPTIONS
  3591. , resizerDblClickToggle: true //
  3592. , autoResize: true // IF size is 'auto' or a percentage, then recalc 'pixel size' whenever the layout resizes
  3593. , autoReopen: true // IF a pane was auto-closed due to noRoom, reopen it when there is room? False = leave it closed
  3594. , resizerDragOpacity: 1 // option for ui.draggable
  3595. //, resizerCursor: "" // MUST be pane-specific - cursor when over resizer-bar
  3596. , maskContents: false // true = add DIV-mask over-or-inside this pane so can 'drag' over IFRAMES
  3597. , maskObjects: false // true = add IFRAME-mask over-or-inside this pane to cover objects/applets - content-mask will overlay this mask
  3598. , maskZindex: null // will override zIndexes.content_mask if specified - not applicable to iframe-panes
  3599. , resizingGrid: false // grid size that the resizers will snap-to during resizing, eg: [20,20]
  3600. , livePaneResizing: false // true = LIVE Resizing as resizer is dragged
  3601. , liveContentResizing: false // true = re-measure header/footer heights as resizer is dragged
  3602. , liveResizingTolerance: 1 // how many px change before pane resizes, to control performance
  3603. // SLIDING OPTIONS
  3604. , sliderCursor: "pointer" // cursor when resizer-bar will trigger 'sliding'
  3605. , slideTrigger_open: "click" // click, dblclick, mouseenter
  3606. , slideTrigger_close: "mouseleave"// click, mouseleave
  3607. , slideDelay_open: 300 // applies only for mouseenter event - 0 = instant open
  3608. , slideDelay_close: 300 // applies only for mouseleave event (300ms is the minimum!)
  3609. , hideTogglerOnSlide: false // when pane is slid-open, should the toggler show?
  3610. , preventQuickSlideClose: $.layout.browser.webkit // Chrome triggers slideClosed as it is opening
  3611. , preventPrematureSlideClose: false // handle incorrect mouseleave trigger, like when over a SELECT-list in IE
  3612. // PANE-SPECIFIC TIPS & MESSAGES
  3613. , tips: {
  3614. Open: "Open" // eg: "Open Pane"
  3615. , Close: "Close"
  3616. , Resize: "Resize"
  3617. , Slide: "Slide Open"
  3618. , Pin: "Pin"
  3619. , Unpin: "Un-Pin"
  3620. , noRoomToOpen: "Not enough room to show this panel." // alert if user tries to open a pane that cannot
  3621. , minSizeWarning: "Panel has reached its minimum size" // displays in browser statusbar
  3622. , maxSizeWarning: "Panel has reached its maximum size" // ditto
  3623. }
  3624. // HOT-KEYS & MISC
  3625. , showOverflowOnHover: false // will bind allowOverflow() utility to pane.onMouseOver
  3626. , enableCursorHotkey: true // enabled 'cursor' hotkeys
  3627. //, customHotkey: "" // MUST be pane-specific - EITHER a charCode OR a character
  3628. , customHotkeyModifier: "SHIFT" // either 'SHIFT', 'CTRL' or 'CTRL+SHIFT' - NOT 'ALT'
  3629. // PANE ANIMATION
  3630. // NOTE: fxSss_open, fxSss_close & fxSss_size options (eg: fxName_open) are auto-generated if not passed
  3631. , fxName: "slide" // ('none' or blank), slide, drop, scale -- only relevant to 'open' & 'close', NOT 'size'
  3632. , fxSpeed: null // slow, normal, fast, 200, nnn - if passed, will OVERRIDE fxSettings.duration
  3633. , fxSettings: {} // can be passed, eg: { easing: "easeOutBounce", duration: 1500 }
  3634. , fxOpacityFix: true // tries to fix opacity in IE to restore anti-aliasing after animation
  3635. , animatePaneSizing: false // true = animate resizing after dragging resizer-bar OR sizePane() is called
  3636. /* NOTE: Action-specific FX options are auto-generated from the options above if not specifically set:
  3637. fxName_open: "slide" // 'Open' pane animation
  3638. fnName_close: "slide" // 'Close' pane animation
  3639. fxName_size: "slide" // 'Size' pane animation - when animatePaneSizing = true
  3640. fxSpeed_open: null
  3641. fxSpeed_close: null
  3642. fxSpeed_size: null
  3643. fxSettings_open: {}
  3644. fxSettings_close: {}
  3645. fxSettings_size: {}
  3646. */
  3647. // CHILD/NESTED LAYOUTS
  3648. , children: null // Layout-options for nested/child layout - even {} is valid as options
  3649. , containerSelector: '' // if child is NOT 'directly nested', a selector to find it/them (can have more than one child layout!)
  3650. , initChildren: true // true = child layout will be created as soon as _this_ layout completes initialization
  3651. , destroyChildren: true // true = destroy child-layout if this pane is destroyed
  3652. , resizeChildren: true // true = trigger child-layout.resizeAll() when this pane is resized
  3653. // EVENT TRIGGERING
  3654. , triggerEventsOnLoad: false // true = trigger onopen OR onclose callbacks when layout initializes
  3655. , triggerEventsDuringLiveResize: true // true = trigger onresize callback REPEATEDLY if livePaneResizing==true
  3656. // PANE CALLBACKS
  3657. , onshow_start: null // CALLBACK when pane STARTS to Show - BEFORE onopen/onhide_start
  3658. , onshow_end: null // CALLBACK when pane ENDS being Shown - AFTER onopen/onhide_end
  3659. , onhide_start: null // CALLBACK when pane STARTS to Close - BEFORE onclose_start
  3660. , onhide_end: null // CALLBACK when pane ENDS being Closed - AFTER onclose_end
  3661. , onopen_start: null // CALLBACK when pane STARTS to Open
  3662. , onopen_end: null // CALLBACK when pane ENDS being Opened
  3663. , onclose_start: null // CALLBACK when pane STARTS to Close
  3664. , onclose_end: null // CALLBACK when pane ENDS being Closed
  3665. , onresize_start: null // CALLBACK when pane STARTS being Resized ***FOR ANY REASON***
  3666. , onresize_end: null // CALLBACK when pane ENDS being Resized ***FOR ANY REASON***
  3667. , onsizecontent_start: null // CALLBACK when sizing of content-element STARTS
  3668. , onsizecontent_end: null // CALLBACK when sizing of content-element ENDS
  3669. , onswap_start: null // CALLBACK when pane STARTS to Swap
  3670. , onswap_end: null // CALLBACK when pane ENDS being Swapped
  3671. , ondrag_start: null // CALLBACK when pane STARTS being ***MANUALLY*** Resized
  3672. , ondrag_end: null // CALLBACK when pane ENDS being ***MANUALLY*** Resized
  3673. }
  3674. /*
  3675. * PANE-SPECIFIC SETTINGS
  3676. * - options listed below MUST be specified per-pane - they CANNOT be set under 'panes'
  3677. * - all options under the 'panes' key can also be set specifically for any pane
  3678. * - most options under the 'panes' key apply only to 'border-panes' - NOT the the center-pane
  3679. */
  3680. , north: {
  3681. paneSelector: ".ui-layout-north"
  3682. , size: "auto" // eg: "auto", "30%", .30, 200
  3683. , resizerCursor: "n-resize" // custom = url(myCursor.cur)
  3684. , customHotkey: "" // EITHER a charCode (43) OR a character ("o")
  3685. }
  3686. , south: {
  3687. paneSelector: ".ui-layout-south"
  3688. , size: "auto"
  3689. , resizerCursor: "s-resize"
  3690. , customHotkey: ""
  3691. }
  3692. , east: {
  3693. paneSelector: ".ui-layout-east"
  3694. , size: 200
  3695. , resizerCursor: "e-resize"
  3696. , customHotkey: ""
  3697. }
  3698. , west: {
  3699. paneSelector: ".ui-layout-west"
  3700. , size: 200
  3701. , resizerCursor: "w-resize"
  3702. , customHotkey: ""
  3703. }
  3704. , center: {
  3705. paneSelector: ".ui-layout-center"
  3706. , minWidth: 0
  3707. , minHeight: 0
  3708. }
  3709. };
  3710. $.layout.optionsMap = {
  3711. // layout/global options - NOT pane-options
  3712. layout: ("name,instanceKey,stateManagement,effects,inset,zIndexes,errors,"
  3713. + "zIndex,scrollToBookmarkOnLoad,showErrorMessages,maskPanesEarly,"
  3714. + "outset,resizeWithWindow,resizeWithWindowDelay,resizeWithWindowMaxDelay,"
  3715. + "onresizeall,onresizeall_start,onresizeall_end,onload,onload_start,onload_end,onunload,onunload_start,onunload_end").split(",")
  3716. // borderPanes: [ ALL options that are NOT specified as 'layout' ]
  3717. // default.panes options that apply to the center-pane (most options apply _only_ to border-panes)
  3718. , center: ("paneClass,contentSelector,contentIgnoreSelector,findNestedContent,applyDemoStyles,triggerEventsOnLoad,"
  3719. + "showOverflowOnHover,maskContents,maskObjects,liveContentResizing,"
  3720. + "containerSelector,children,initChildren,resizeChildren,destroyChildren,"
  3721. + "onresize,onresize_start,onresize_end,onsizecontent,onsizecontent_start,onsizecontent_end").split(",")
  3722. // options that MUST be specifically set 'per-pane' - CANNOT set in the panes (defaults) key
  3723. , noDefault: ("paneSelector,resizerCursor,customHotkey").split(",")
  3724. };
  3725. /**
  3726. * Processes options passed in converts flat-format data into subkey (JSON) format
  3727. * In flat-format, subkeys are _currently_ separated with 2 underscores, like north__optName
  3728. * Plugins may also call this method so they can transform their own data
  3729. *
  3730. * @param {!Object} hash Data/options passed by user - may be a single level or nested levels
  3731. * @param {boolean=} [addKeys=false] Should the primary layout.options keys be added if they do not exist?
  3732. * @return {Object} Returns hash of minWidth & minHeight
  3733. */
  3734. $.layout.transformData = function (hash, addKeys) {
  3735. var json = addKeys ? { panes: {}, center: {} } : {} // init return object
  3736. , branch, optKey, keys, key, val, i, c;
  3737. if (typeof hash !== "object") return json; // no options passed
  3738. // convert all 'flat-keys' to 'sub-key' format
  3739. for (optKey in hash) {
  3740. branch = json;
  3741. val = hash[ optKey ];
  3742. keys = optKey.split("__"); // eg: west__size or north__fxSettings__duration
  3743. c = keys.length - 1;
  3744. // convert underscore-delimited to subkeys
  3745. for (i=0; i <= c; i++) {
  3746. key = keys[i];
  3747. if (i === c) { // last key = value
  3748. if ($.isPlainObject( val ))
  3749. branch[key] = $.layout.transformData( val ); // RECURSE
  3750. else
  3751. branch[key] = val;
  3752. }
  3753. else {
  3754. if (!branch[key])
  3755. branch[key] = {}; // create the subkey
  3756. // recurse to sub-key for next loop - if not done
  3757. branch = branch[key];
  3758. }
  3759. }
  3760. }
  3761. return json;
  3762. };
  3763. // INTERNAL CONFIG DATA - DO NOT CHANGE THIS!
  3764. $.layout.backwardCompatibility = {
  3765. // data used by renameOldOptions()
  3766. map: {
  3767. // OLD Option Name: NEW Option Name
  3768. applyDefaultStyles: "applyDemoStyles"
  3769. // CHILD/NESTED LAYOUTS
  3770. , childOptions: "children"
  3771. , initChildLayout: "initChildren"
  3772. , destroyChildLayout: "destroyChildren"
  3773. , resizeChildLayout: "resizeChildren"
  3774. , resizeNestedLayout: "resizeChildren"
  3775. // MISC Options
  3776. , resizeWhileDragging: "livePaneResizing"
  3777. , resizeContentWhileDragging: "liveContentResizing"
  3778. , triggerEventsWhileDragging: "triggerEventsDuringLiveResize"
  3779. , maskIframesOnResize: "maskContents"
  3780. // STATE MANAGEMENT
  3781. , useStateCookie: "stateManagement.enabled"
  3782. , "cookie.autoLoad": "stateManagement.autoLoad"
  3783. , "cookie.autoSave": "stateManagement.autoSave"
  3784. , "cookie.keys": "stateManagement.stateKeys"
  3785. , "cookie.name": "stateManagement.cookie.name"
  3786. , "cookie.domain": "stateManagement.cookie.domain"
  3787. , "cookie.path": "stateManagement.cookie.path"
  3788. , "cookie.expires": "stateManagement.cookie.expires"
  3789. , "cookie.secure": "stateManagement.cookie.secure"
  3790. // OLD Language options
  3791. , noRoomToOpenTip: "tips.noRoomToOpen"
  3792. , togglerTip_open: "tips.Close" // open = Close
  3793. , togglerTip_closed: "tips.Open" // closed = Open
  3794. , resizerTip: "tips.Resize"
  3795. , sliderTip: "tips.Slide"
  3796. }
  3797. /**
  3798. * @param {Object} opts
  3799. */
  3800. , renameOptions: function (opts) {
  3801. var map = $.layout.backwardCompatibility.map
  3802. , oldData, newData, value
  3803. ;
  3804. for (var itemPath in map) {
  3805. oldData = getBranch( itemPath );
  3806. value = oldData.branch[ oldData.key ];
  3807. if (value !== undefined) {
  3808. newData = getBranch( map[itemPath], true );
  3809. newData.branch[ newData.key ] = value;
  3810. delete oldData.branch[ oldData.key ];
  3811. }
  3812. }
  3813. /**
  3814. * @param {string} path
  3815. * @param {boolean=} [create=false] Create path if does not exist
  3816. */
  3817. function getBranch (path, create) {
  3818. var a = path.split(".") // split keys into array
  3819. , c = a.length - 1
  3820. , D = { branch: opts, key: a[c] } // init branch at top & set key (last item)
  3821. , i = 0, k, undef;
  3822. for (; i<c; i++) { // skip the last key (data)
  3823. k = a[i];
  3824. if (D.branch[ k ] == undefined) { // child-key does not exist
  3825. if (create) {
  3826. D.branch = D.branch[ k ] = {}; // create child-branch
  3827. }
  3828. else // can't go any farther
  3829. D.branch = {}; // branch is undefined
  3830. }
  3831. else
  3832. D.branch = D.branch[ k ]; // get child-branch
  3833. }
  3834. return D;
  3835. };
  3836. }
  3837. /**
  3838. * @param {Object} opts
  3839. */
  3840. , renameAllOptions: function (opts) {
  3841. var ren = $.layout.backwardCompatibility.renameOptions;
  3842. // rename root (layout) options
  3843. ren( opts );
  3844. // rename 'defaults' to 'panes'
  3845. if (opts.defaults) {
  3846. if (typeof opts.panes !== "object")
  3847. opts.panes = {};
  3848. $.extend(true, opts.panes, opts.defaults);
  3849. delete opts.defaults;
  3850. }
  3851. // rename options in the the options.panes key
  3852. if (opts.panes) ren( opts.panes );
  3853. // rename options inside *each pane key*, eg: options.west
  3854. $.each($.layout.config.allPanes, function (i, pane) {
  3855. if (opts[pane]) ren( opts[pane] );
  3856. });
  3857. return opts;
  3858. }
  3859. };
  3860. /* ============================================================
  3861. * BEGIN WIDGET: $( selector ).layout( {options} );
  3862. * ============================================================
  3863. */
  3864. $.fn.layout = function (opts) {
  3865. var
  3866. // local aliases to global data
  3867. browser = $.layout.browser
  3868. , _c = $.layout.config
  3869. // local aliases to utlity methods
  3870. , cssW = $.layout.cssWidth
  3871. , cssH = $.layout.cssHeight
  3872. , elDims = $.layout.getElementDimensions
  3873. , styles = $.layout.getElementStyles
  3874. , evtObj = $.layout.getEventObject
  3875. , evtPane = $.layout.parsePaneName
  3876. /**
  3877. * options - populated by initOptions()
  3878. */
  3879. , options = $.extend(true, {}, $.layout.defaults)
  3880. , effects = options.effects = $.extend(true, {}, $.layout.effects)
  3881. /**
  3882. * layout-state object
  3883. */
  3884. , state = {
  3885. // generate unique ID to use for event.namespace so can unbind only events added by 'this layout'
  3886. id: "layout"+ $.now() // code uses alias: sID
  3887. , initialized: false
  3888. , paneResizing: false
  3889. , panesSliding: {}
  3890. , container: { // list all keys referenced in code to avoid compiler error msgs
  3891. innerWidth: 0
  3892. , innerHeight: 0
  3893. , outerWidth: 0
  3894. , outerHeight: 0
  3895. , layoutWidth: 0
  3896. , layoutHeight: 0
  3897. }
  3898. , north: { childIdx: 0 }
  3899. , south: { childIdx: 0 }
  3900. , east: { childIdx: 0 }
  3901. , west: { childIdx: 0 }
  3902. , center: { childIdx: 0 }
  3903. }
  3904. /**
  3905. * parent/child-layout pointers
  3906. */
  3907. //, hasParentLayout = false - exists ONLY inside Instance so can be set externally
  3908. , children = {
  3909. north: null
  3910. , south: null
  3911. , east: null
  3912. , west: null
  3913. , center: null
  3914. }
  3915. /*
  3916. * ###########################
  3917. * INTERNAL HELPER FUNCTIONS
  3918. * ###########################
  3919. */
  3920. /**
  3921. * Manages all internal timers
  3922. */
  3923. , timer = {
  3924. data: {}
  3925. , set: function (s, fn, ms) { timer.clear(s); timer.data[s] = setTimeout(fn, ms); }
  3926. , clear: function (s) { var t=timer.data; if (t[s]) {clearTimeout(t[s]); delete t[s];} }
  3927. }
  3928. /**
  3929. * Alert or console.log a message - IF option is enabled.
  3930. *
  3931. * @param {(string|!Object)} msg Message (or debug-data) to display
  3932. * @param {boolean=} [popup=false] True by default, means 'alert', false means use console.log
  3933. * @param {boolean=} [debug=false] True means is a widget debugging message
  3934. */
  3935. , _log = function (msg, popup, debug) {
  3936. var o = options;
  3937. if ((o.showErrorMessages && !debug) || (debug && o.showDebugMessages))
  3938. $.layout.msg( o.name +' / '+ msg, (popup !== false) );
  3939. return false;
  3940. }
  3941. /**
  3942. * Executes a Callback function after a trigger event, like resize, open or close
  3943. *
  3944. * @param {string} evtName Name of the layout callback, eg "onresize_start"
  3945. * @param {(string|boolean)=} [pane=""] This is passed only so we can pass the 'pane object' to the callback
  3946. * @param {(string|boolean)=} [skipBoundEvents=false] True = do not run events bound to the elements - only the callbacks set in options
  3947. */
  3948. , _runCallbacks = function (evtName, pane, skipBoundEvents) {
  3949. var hasPane = pane && isStr(pane)
  3950. , s = hasPane ? state[pane] : state
  3951. , o = hasPane ? options[pane] : options
  3952. , lName = options.name
  3953. // names like onopen and onopen_end separate are interchangeable in options...
  3954. , lng = evtName + (evtName.match(/_/) ? "" : "_end")
  3955. , shrt = lng.match(/_end$/) ? lng.substr(0, lng.length - 4) : ""
  3956. , fn = o[lng] || o[shrt]
  3957. , retVal = "NC" // NC = No Callback
  3958. , args = []
  3959. , $P
  3960. ;
  3961. if ( !hasPane && $.type(pane) === 'boolean' ) {
  3962. skipBoundEvents = pane; // allow pane param to be skipped for Layout callback
  3963. pane = "";
  3964. }
  3965. // first trigger the callback set in the options
  3966. if (fn) {
  3967. // convert function name (string) to function object
  3968. if (isStr( fn )) {
  3969. if (fn.match(/,/)) {
  3970. // function name cannot contain a comma,
  3971. // so must be a function name AND a parameter to pass
  3972. args = fn.split(",")
  3973. , fn = eval(args[0]);
  3974. }
  3975. else // just the name of an external function?
  3976. fn = eval(fn);
  3977. }
  3978. // execute the callback, if exists
  3979. if ($.isFunction( fn )) {
  3980. if (args.length)
  3981. retVal = g(fn)(args[1]); // pass the argument parsed from 'list'
  3982. else if ( hasPane )
  3983. // pass data: pane-name, pane-element, pane-state, pane-options, and layout-name
  3984. retVal = g(fn)( pane, $Ps[pane], s, o, lName );
  3985. else // must be a layout/container callback - pass suitable info
  3986. retVal = g(fn)( Instance, s, o, lName );
  3987. }
  3988. // _log( options.errors.callbackError.replace(/EVENT/, $.trim((pane || "") +" "+ lng)), false );
  3989. // if ($.type(ex) === 'string' && string.length)
  3990. // _log('Exception: '+ ex, false );
  3991. }
  3992. // trigger additional events bound directly to the pane
  3993. if (!skipBoundEvents && retVal !== false) {
  3994. if ( hasPane ) { // PANE events can be bound to each pane-elements
  3995. $P = $Ps[pane];
  3996. o = options[pane];
  3997. s = state[pane];
  3998. $P.triggerHandler('layoutpane'+ lng, [ pane, $P, s, o, lName ]);
  3999. if (shrt)
  4000. $P.triggerHandler('layoutpane'+ shrt, [ pane, $P, s, o, lName ]);
  4001. }
  4002. else { // LAYOUT events can be bound to the container-element
  4003. $N.triggerHandler('layout'+ lng, [ Instance, s, o, lName ]);
  4004. if (shrt)
  4005. $N.triggerHandler('layout'+ shrt, [ Instance, s, o, lName ]);
  4006. }
  4007. }
  4008. // ALWAYS resizeChildren after an onresize_end event - even during initialization
  4009. // IGNORE onsizecontent_end event because causes child-layouts to resize TWICE
  4010. if (hasPane && evtName === "onresize_end") // BAD: || evtName === "onsizecontent_end"
  4011. resizeChildren(pane+"", true); // compiler hack -force string
  4012. return retVal;
  4013. function g (f) { return f; }; // compiler hack
  4014. }
  4015. /**
  4016. * cure iframe display issues in IE & other browsers
  4017. */
  4018. , _fixIframe = function (pane) {
  4019. if (browser.mozilla) return; // skip FireFox - it auto-refreshes iframes onShow
  4020. var $P = $Ps[pane];
  4021. // if the 'pane' is an iframe, do it
  4022. if (state[pane].tagName === "IFRAME")
  4023. $P.css(_c.hidden).css(_c.visible);
  4024. else // ditto for any iframes INSIDE the pane
  4025. $P.find('IFRAME').css(_c.hidden).css(_c.visible);
  4026. }
  4027. /**
  4028. * @param {string} pane Can accept ONLY a 'pane' (east, west, etc)
  4029. * @param {number=} outerSize (optional) Can pass a width, allowing calculations BEFORE element is resized
  4030. * @return {number} Returns the innerHeight/Width of el by subtracting padding and borders
  4031. */
  4032. , cssSize = function (pane, outerSize) {
  4033. var fn = _c[pane].dir=="horz" ? cssH : cssW;
  4034. return fn($Ps[pane], outerSize);
  4035. }
  4036. /**
  4037. * @param {string} pane Can accept ONLY a 'pane' (east, west, etc)
  4038. * @return {Object} Returns hash of minWidth & minHeight
  4039. */
  4040. , cssMinDims = function (pane) {
  4041. // minWidth/Height means CSS width/height = 1px
  4042. var $P = $Ps[pane]
  4043. , dir = _c[pane].dir
  4044. , d = {
  4045. minWidth: 1001 - cssW($P, 1000)
  4046. , minHeight: 1001 - cssH($P, 1000)
  4047. }
  4048. ;
  4049. if (dir === "horz") d.minSize = d.minHeight;
  4050. if (dir === "vert") d.minSize = d.minWidth;
  4051. return d;
  4052. }
  4053. // TODO: see if these methods can be made more useful...
  4054. // TODO: *maybe* return cssW/H from these so caller can use this info
  4055. /**
  4056. * @param {(string|!Object)} el
  4057. * @param {number=} outerWidth
  4058. * @param {boolean=} [autoHide=false]
  4059. */
  4060. , setOuterWidth = function (el, outerWidth, autoHide) {
  4061. var $E = el, w;
  4062. if (isStr(el)) $E = $Ps[el]; // west
  4063. else if (!el.jquery) $E = $(el);
  4064. w = cssW($E, outerWidth);
  4065. $E.css({ width: w });
  4066. if (w > 0) {
  4067. if (autoHide && $E.data('autoHidden') && $E.innerHeight() > 0) {
  4068. $E.show().data('autoHidden', false);
  4069. if (!browser.mozilla) // FireFox refreshes iframes - IE does not
  4070. // make hidden, then visible to 'refresh' display after animation
  4071. $E.css(_c.hidden).css(_c.visible);
  4072. }
  4073. }
  4074. else if (autoHide && !$E.data('autoHidden'))
  4075. $E.hide().data('autoHidden', true);
  4076. }
  4077. /**
  4078. * @param {(string|!Object)} el
  4079. * @param {number=} outerHeight
  4080. * @param {boolean=} [autoHide=false]
  4081. */
  4082. , setOuterHeight = function (el, outerHeight, autoHide) {
  4083. var $E = el, h;
  4084. if (isStr(el)) $E = $Ps[el]; // west
  4085. else if (!el.jquery) $E = $(el);
  4086. h = cssH($E, outerHeight);
  4087. $E.css({ height: h, visibility: "visible" }); // may have been 'hidden' by sizeContent
  4088. if (h > 0 && $E.innerWidth() > 0) {
  4089. if (autoHide && $E.data('autoHidden')) {
  4090. $E.show().data('autoHidden', false);
  4091. if (!browser.mozilla) // FireFox refreshes iframes - IE does not
  4092. $E.css(_c.hidden).css(_c.visible);
  4093. }
  4094. }
  4095. else if (autoHide && !$E.data('autoHidden'))
  4096. $E.hide().data('autoHidden', true);
  4097. }
  4098. /**
  4099. * Converts any 'size' params to a pixel/integer size, if not already
  4100. * If 'auto' or a decimal/percentage is passed as 'size', a pixel-size is calculated
  4101. *
  4102. /**
  4103. * @param {string} pane
  4104. * @param {(string|number)=} size
  4105. * @param {string=} [dir]
  4106. * @return {number}
  4107. */
  4108. , _parseSize = function (pane, size, dir) {
  4109. if (!dir) dir = _c[pane].dir;
  4110. if (isStr(size) && size.match(/%/))
  4111. size = (size === '100%') ? -1 : parseInt(size, 10) / 100; // convert % to decimal
  4112. if (size === 0)
  4113. return 0;
  4114. else if (size >= 1)
  4115. return parseInt(size, 10);
  4116. var o = options, avail = 0;
  4117. if (dir=="horz") // north or south or center.minHeight
  4118. avail = sC.innerHeight - ($Ps.north ? o.north.spacing_open : 0) - ($Ps.south ? o.south.spacing_open : 0);
  4119. else if (dir=="vert") // east or west or center.minWidth
  4120. avail = sC.innerWidth - ($Ps.west ? o.west.spacing_open : 0) - ($Ps.east ? o.east.spacing_open : 0);
  4121. if (size === -1) // -1 == 100%
  4122. return avail;
  4123. else if (size > 0) // percentage, eg: .25
  4124. return round(avail * size);
  4125. else if (pane=="center")
  4126. return 0;
  4127. else { // size < 0 || size=='auto' || size==Missing || size==Invalid
  4128. // auto-size the pane
  4129. var dim = (dir === "horz" ? "height" : "width")
  4130. , $P = $Ps[pane]
  4131. , $C = dim === 'height' ? $Cs[pane] : false
  4132. , vis = $.layout.showInvisibly($P) // show pane invisibly if hidden
  4133. , szP = $P.css(dim) // SAVE current pane size
  4134. , szC = $C ? $C.css(dim) : 0 // SAVE current content size
  4135. ;
  4136. $P.css(dim, "auto");
  4137. if ($C) $C.css(dim, "auto");
  4138. size = (dim === "height") ? $P.outerHeight() : $P.outerWidth(); // MEASURE
  4139. $P.css(dim, szP).css(vis); // RESET size & visibility
  4140. if ($C) $C.css(dim, szC);
  4141. return size;
  4142. }
  4143. }
  4144. /**
  4145. * Calculates current 'size' (outer-width or outer-height) of a border-pane - optionally with 'pane-spacing' added
  4146. *
  4147. * @param {(string|!Object)} pane
  4148. * @param {boolean=} [inclSpace=false]
  4149. * @return {number} Returns EITHER Width for east/west panes OR Height for north/south panes
  4150. */
  4151. , getPaneSize = function (pane, inclSpace) {
  4152. var
  4153. $P = $Ps[pane]
  4154. , o = options[pane]
  4155. , s = state[pane]
  4156. , oSp = (inclSpace ? o.spacing_open : 0)
  4157. , cSp = (inclSpace ? o.spacing_closed : 0)
  4158. ;
  4159. if (!$P || s.isHidden)
  4160. return 0;
  4161. else if (s.isClosed || (s.isSliding && inclSpace))
  4162. return cSp;
  4163. else if (_c[pane].dir === "horz")
  4164. return $P.outerHeight() + oSp;
  4165. else // dir === "vert"
  4166. return $P.outerWidth() + oSp;
  4167. }
  4168. /**
  4169. * Calculate min/max pane dimensions and limits for resizing
  4170. *
  4171. * @param {string} pane
  4172. * @param {boolean=} [slide=false]
  4173. */
  4174. , setSizeLimits = function (pane, slide) {
  4175. if (!isInitialized()) return;
  4176. var
  4177. o = options[pane]
  4178. , s = state[pane]
  4179. , c = _c[pane]
  4180. , dir = c.dir
  4181. , type = c.sizeType.toLowerCase()
  4182. , isSliding = (slide != undefined ? slide : s.isSliding) // only open() passes 'slide' param
  4183. , $P = $Ps[pane]
  4184. , paneSpacing = o.spacing_open
  4185. // measure the pane on the *opposite side* from this pane
  4186. , altPane = _c.oppositeEdge[pane]
  4187. , altS = state[altPane]
  4188. , $altP = $Ps[altPane]
  4189. , altPaneSize = (!$altP || altS.isVisible===false || altS.isSliding ? 0 : (dir=="horz" ? $altP.outerHeight() : $altP.outerWidth()))
  4190. , altPaneSpacing = ((!$altP || altS.isHidden ? 0 : options[altPane][ altS.isClosed !== false ? "spacing_closed" : "spacing_open" ]) || 0)
  4191. // limitSize prevents this pane from 'overlapping' opposite pane
  4192. , containerSize = (dir=="horz" ? sC.innerHeight : sC.innerWidth)
  4193. , minCenterDims = cssMinDims("center")
  4194. , minCenterSize = dir=="horz" ? max(options.center.minHeight, minCenterDims.minHeight) : max(options.center.minWidth, minCenterDims.minWidth)
  4195. // if pane is 'sliding', then ignore center and alt-pane sizes - because 'overlays' them
  4196. , limitSize = (containerSize - paneSpacing - (isSliding ? 0 : (_parseSize("center", minCenterSize, dir) + altPaneSize + altPaneSpacing)))
  4197. , minSize = s.minSize = max( _parseSize(pane, o.minSize), cssMinDims(pane).minSize )
  4198. , maxSize = s.maxSize = min( (o.maxSize ? _parseSize(pane, o.maxSize) : 100000), limitSize )
  4199. , r = s.resizerPosition = {} // used to set resizing limits
  4200. , top = sC.inset.top
  4201. , left = sC.inset.left
  4202. , W = sC.innerWidth
  4203. , H = sC.innerHeight
  4204. , rW = o.spacing_open // subtract resizer-width to get top/left position for south/east
  4205. ;
  4206. switch (pane) {
  4207. case "north": r.min = top + minSize;
  4208. r.max = top + maxSize;
  4209. break;
  4210. case "west": r.min = left + minSize;
  4211. r.max = left + maxSize;
  4212. break;
  4213. case "south": r.min = top + H - maxSize - rW;
  4214. r.max = top + H - minSize - rW;
  4215. break;
  4216. case "east": r.min = left + W - maxSize - rW;
  4217. r.max = left + W - minSize - rW;
  4218. break;
  4219. };
  4220. }
  4221. /**
  4222. * Returns data for setting the size/position of center pane. Also used to set Height for east/west panes
  4223. *
  4224. * @return JSON Returns a hash of all dimensions: top, bottom, left, right, (outer) width and (outer) height
  4225. */
  4226. , calcNewCenterPaneDims = function () {
  4227. var d = {
  4228. top: getPaneSize("north", true) // true = include 'spacing' value for pane
  4229. , bottom: getPaneSize("south", true)
  4230. , left: getPaneSize("west", true)
  4231. , right: getPaneSize("east", true)
  4232. , width: 0
  4233. , height: 0
  4234. };
  4235. // NOTE: sC = state.container
  4236. // calc center-pane outer dimensions
  4237. d.width = sC.innerWidth - d.left - d.right; // outerWidth
  4238. d.height = sC.innerHeight - d.bottom - d.top; // outerHeight
  4239. // add the 'container border/padding' to get final positions relative to the container
  4240. d.top += sC.inset.top;
  4241. d.bottom += sC.inset.bottom;
  4242. d.left += sC.inset.left;
  4243. d.right += sC.inset.right;
  4244. return d;
  4245. }
  4246. /**
  4247. * @param {!Object} el
  4248. * @param {boolean=} [allStates=false]
  4249. */
  4250. , getHoverClasses = function (el, allStates) {
  4251. var
  4252. $El = $(el)
  4253. , type = $El.data("layoutRole")
  4254. , pane = $El.data("layoutEdge")
  4255. , o = options[pane]
  4256. , root = o[type +"Class"]
  4257. , _pane = "-"+ pane // eg: "-west"
  4258. , _open = "-open"
  4259. , _closed = "-closed"
  4260. , _slide = "-sliding"
  4261. , _hover = "-hover " // NOTE the trailing space
  4262. , _state = $El.hasClass(root+_closed) ? _closed : _open
  4263. , _alt = _state === _closed ? _open : _closed
  4264. , classes = (root+_hover) + (root+_pane+_hover) + (root+_state+_hover) + (root+_pane+_state+_hover)
  4265. ;
  4266. if (allStates) // when 'removing' classes, also remove alternate-state classes
  4267. classes += (root+_alt+_hover) + (root+_pane+_alt+_hover);
  4268. if (type=="resizer" && $El.hasClass(root+_slide))
  4269. classes += (root+_slide+_hover) + (root+_pane+_slide+_hover);
  4270. return $.trim(classes);
  4271. }
  4272. , addHover = function (evt, el) {
  4273. var $E = $(el || this);
  4274. if (evt && $E.data("layoutRole") === "toggler")
  4275. evt.stopPropagation(); // prevent triggering 'slide' on Resizer-bar
  4276. $E.addClass( getHoverClasses($E) );
  4277. }
  4278. , removeHover = function (evt, el) {
  4279. var $E = $(el || this);
  4280. $E.removeClass( getHoverClasses($E, true) );
  4281. }
  4282. , onResizerEnter = function (evt) { // ALSO called by toggler.mouseenter
  4283. var pane = $(this).data("layoutEdge")
  4284. , s = state[pane]
  4285. ;
  4286. // ignore closed-panes and mouse moving back & forth over resizer!
  4287. // also ignore if ANY pane is currently resizing
  4288. if ( s.isClosed || s.isResizing || state.paneResizing ) return;
  4289. if ($.fn.disableSelection)
  4290. $("body").disableSelection();
  4291. if (options.maskPanesEarly)
  4292. showMasks( pane, { resizing: true });
  4293. }
  4294. , onResizerLeave = function (evt, el) {
  4295. var e = el || this // el is only passed when called by the timer
  4296. , pane = $(e).data("layoutEdge")
  4297. , name = pane +"ResizerLeave"
  4298. ;
  4299. timer.clear(pane+"_openSlider"); // cancel slideOpen timer, if set
  4300. timer.clear(name); // cancel enableSelection timer - may re/set below
  4301. // this method calls itself on a timer because it needs to allow
  4302. // enough time for dragging to kick-in and set the isResizing flag
  4303. // dragging has a 100ms delay set, so this delay must be >100
  4304. if (!el) // 1st call - mouseleave event
  4305. timer.set(name, function(){ onResizerLeave(evt, e); }, 200);
  4306. // if user is resizing, then dragStop will enableSelection(), so can skip it here
  4307. else if ( !state.paneResizing ) { // 2nd call - by timer
  4308. if ($.fn.enableSelection)
  4309. $("body").enableSelection();
  4310. if (options.maskPanesEarly)
  4311. hideMasks();
  4312. }
  4313. }
  4314. /*
  4315. * ###########################
  4316. * INITIALIZATION METHODS
  4317. * ###########################
  4318. */
  4319. /**
  4320. * Initialize the layout - called automatically whenever an instance of layout is created
  4321. *
  4322. * @see none - triggered onInit
  4323. * @return mixed true = fully initialized | false = panes not initialized (yet) | 'cancel' = abort
  4324. */
  4325. , _create = function () {
  4326. // initialize config/options
  4327. initOptions();
  4328. var o = options
  4329. , s = state;
  4330. // TEMP state so isInitialized returns true during init process
  4331. s.creatingLayout = true;
  4332. // init plugins for this layout, if there are any (eg: stateManagement)
  4333. runPluginCallbacks( Instance, $.layout.onCreate );
  4334. // options & state have been initialized, so now run beforeLoad callback
  4335. // onload will CANCEL layout creation if it returns false
  4336. if (false === _runCallbacks("onload_start"))
  4337. return 'cancel';
  4338. // initialize the container element
  4339. _initContainer();
  4340. // bind hotkey function - keyDown - if required
  4341. initHotkeys();
  4342. // bind window.onunload
  4343. $(window).bind("unload."+ sID, unload);
  4344. // init plugins for this layout, if there are any (eg: customButtons)
  4345. runPluginCallbacks( Instance, $.layout.onLoad );
  4346. // if layout elements are hidden, then layout WILL NOT complete initialization!
  4347. // initLayoutElements will set initialized=true and run the onload callback IF successful
  4348. if (o.initPanes) _initLayoutElements();
  4349. delete s.creatingLayout;
  4350. return state.initialized;
  4351. }
  4352. /**
  4353. * Initialize the layout IF not already
  4354. *
  4355. * @see All methods in Instance run this test
  4356. * @return boolean true = layoutElements have been initialized | false = panes are not initialized (yet)
  4357. */
  4358. , isInitialized = function () {
  4359. if (state.initialized || state.creatingLayout) return true; // already initialized
  4360. else return _initLayoutElements(); // try to init panes NOW
  4361. }
  4362. /**
  4363. * Initialize the layout - called automatically whenever an instance of layout is created
  4364. *
  4365. * @see _create() & isInitialized
  4366. * @param {boolean=} [retry=false] // indicates this is a 2nd try
  4367. * @return An object pointer to the instance created
  4368. */
  4369. , _initLayoutElements = function (retry) {
  4370. // initialize config/options
  4371. var o = options;
  4372. // CANNOT init panes inside a hidden container!
  4373. if (!$N.is(":visible")) {
  4374. // handle Chrome bug where popup window 'has no height'
  4375. // if layout is BODY element, try again in 50ms
  4376. // SEE: http://layout.jquery-dev.net/samples/test_popup_window.html
  4377. if ( !retry && browser.webkit && $N[0].tagName === "BODY" )
  4378. setTimeout(function(){ _initLayoutElements(true); }, 50);
  4379. return false;
  4380. }
  4381. // a center pane is required, so make sure it exists
  4382. if (!getPane("center").length) {
  4383. return _log( o.errors.centerPaneMissing );
  4384. }
  4385. // TEMP state so isInitialized returns true during init process
  4386. state.creatingLayout = true;
  4387. // update Container dims
  4388. $.extend(sC, elDims( $N, o.inset )); // passing inset means DO NOT include insetX values
  4389. // initialize all layout elements
  4390. initPanes(); // size & position panes - calls initHandles() - which calls initResizable()
  4391. if (o.scrollToBookmarkOnLoad) {
  4392. var l = self.location;
  4393. if (l.hash) l.replace( l.hash ); // scrollTo Bookmark
  4394. }
  4395. // check to see if this layout 'nested' inside a pane
  4396. if (Instance.hasParentLayout)
  4397. o.resizeWithWindow = false;
  4398. // bind resizeAll() for 'this layout instance' to window.resize event
  4399. else if (o.resizeWithWindow)
  4400. $(window).bind("resize."+ sID, windowResize);
  4401. delete state.creatingLayout;
  4402. state.initialized = true;
  4403. // init plugins for this layout, if there are any
  4404. runPluginCallbacks( Instance, $.layout.onReady );
  4405. // now run the onload callback, if exists
  4406. _runCallbacks("onload_end");
  4407. return true; // elements initialized successfully
  4408. }
  4409. /**
  4410. * Initialize nested layouts for a specific pane - can optionally pass layout-options
  4411. *
  4412. * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west
  4413. * @param {Object=} [opts] Layout-options - if passed, will OVERRRIDE options[pane].children
  4414. * @return An object pointer to the layout instance created - or null
  4415. */
  4416. , createChildren = function (evt_or_pane, opts) {
  4417. var pane = evtPane.call(this, evt_or_pane)
  4418. , $P = $Ps[pane]
  4419. ;
  4420. if (!$P) return;
  4421. var $C = $Cs[pane]
  4422. , s = state[pane]
  4423. , o = options[pane]
  4424. , sm = options.stateManagement || {}
  4425. , cos = opts ? (o.children = opts) : o.children
  4426. ;
  4427. if ( $.isPlainObject( cos ) )
  4428. cos = [ cos ]; // convert a hash to a 1-elem array
  4429. else if (!cos || !$.isArray( cos ))
  4430. return;
  4431. $.each( cos, function (idx, co) {
  4432. if ( !$.isPlainObject( co ) ) return;
  4433. // determine which element is supposed to be the 'child container'
  4434. // if pane has a 'containerSelector' OR a 'content-div', use those instead of the pane
  4435. var $containers = co.containerSelector ? $P.find( co.containerSelector ) : ($C || $P);
  4436. $containers.each(function(){
  4437. var $cont = $(this)
  4438. , child = $cont.data("layout") // see if a child-layout ALREADY exists on this element
  4439. ;
  4440. // if no layout exists, but children are set, try to create the layout now
  4441. if (!child) {
  4442. // TODO: see about moving this to the stateManagement plugin, as a method
  4443. // set a unique child-instance key for this layout, if not already set
  4444. setInstanceKey({ container: $cont, options: co }, s );
  4445. // If THIS layout has a hash in stateManagement.autoLoad,
  4446. // then see if it also contains state-data for this child-layout
  4447. // If so, copy the stateData to child.options.stateManagement.autoLoad
  4448. if ( sm.includeChildren && state.stateData[pane] ) {
  4449. // THIS layout's state was cached when its state was loaded
  4450. var paneChildren = state.stateData[pane].children || {}
  4451. , childState = paneChildren[ co.instanceKey ]
  4452. , co_sm = co.stateManagement || (co.stateManagement = { autoLoad: true })
  4453. ;
  4454. // COPY the stateData into the autoLoad key
  4455. if ( co_sm.autoLoad === true && childState ) {
  4456. co_sm.autoSave = false; // disable autoSave because saving handled by parent-layout
  4457. co_sm.includeChildren = true; // cascade option - FOR NOW
  4458. co_sm.autoLoad = $.extend(true, {}, childState); // COPY the state-hash
  4459. }
  4460. }
  4461. // create the layout
  4462. child = $cont.layout( co );
  4463. // if successful, update data
  4464. if (child) {
  4465. // add the child and update all layout-pointers
  4466. // MAY have already been done by child-layout calling parent.refreshChildren()
  4467. refreshChildren( pane, child );
  4468. }
  4469. }
  4470. });
  4471. });
  4472. }
  4473. , setInstanceKey = function (child, parentPaneState) {
  4474. // create a named key for use in state and instance branches
  4475. var $c = child.container
  4476. , o = child.options
  4477. , sm = o.stateManagement
  4478. , key = o.instanceKey || $c.data("layoutInstanceKey")
  4479. ;
  4480. if (!key) key = (sm && sm.cookie ? sm.cookie.name : '') || o.name; // look for a name/key
  4481. if (!key) key = "layout"+ (++parentPaneState.childIdx); // if no name/key found, generate one
  4482. else key = key.replace(/[^\w-]/gi, '_').replace(/_{2,}/g, '_'); // ensure is valid as a hash key
  4483. o.instanceKey = key;
  4484. $c.data("layoutInstanceKey", key); // useful if layout is destroyed and then recreated
  4485. return key;
  4486. }
  4487. /**
  4488. * @param {string} pane The pane being opened, ie: north, south, east, or west
  4489. * @param {Object=} newChild New child-layout Instance to add to this pane
  4490. */
  4491. , refreshChildren = function (pane, newChild) {
  4492. var $P = $Ps[pane]
  4493. , pC = children[pane]
  4494. , s = state[pane]
  4495. , o
  4496. ;
  4497. // check for destroy()ed layouts and update the child pointers & arrays
  4498. if ($.isPlainObject( pC )) {
  4499. $.each( pC, function (key, child) {
  4500. if (child.destroyed) delete pC[key]
  4501. });
  4502. // if no more children, remove the children hash
  4503. if ($.isEmptyObject( pC ))
  4504. pC = children[pane] = null; // clear children hash
  4505. }
  4506. // see if there is a directly-nested layout inside this pane
  4507. // if there is, then there can be only ONE child-layout, so check that...
  4508. if (!newChild && !pC) {
  4509. newChild = $P.data("layout");
  4510. }
  4511. // if a newChild instance was passed, add it to children[pane]
  4512. if (newChild) {
  4513. // update child.state
  4514. newChild.hasParentLayout = true; // set parent-flag in child
  4515. // instanceKey is a key-name used in both state and children
  4516. o = newChild.options;
  4517. // set a unique child-instance key for this layout, if not already set
  4518. setInstanceKey( newChild, s );
  4519. // add pointer to pane.children hash
  4520. if (!pC) pC = children[pane] = {}; // create an empty children hash
  4521. pC[ o.instanceKey ] = newChild.container.data("layout"); // add childLayout instance
  4522. }
  4523. // ALWAYS refresh the pane.children alias, even if null
  4524. Instance[pane].children = children[pane];
  4525. // if newChild was NOT passed - see if there is a child layout NOW
  4526. if (!newChild) {
  4527. createChildren(pane); // MAY create a child and re-call this method
  4528. }
  4529. }
  4530. , windowResize = function () {
  4531. var o = options
  4532. , delay = Number(o.resizeWithWindowDelay);
  4533. if (delay < 10) delay = 100; // MUST have a delay!
  4534. // resizing uses a delay-loop because the resize event fires repeatly - except in FF, but delay anyway
  4535. timer.clear("winResize"); // if already running
  4536. timer.set("winResize", function(){
  4537. timer.clear("winResize");
  4538. timer.clear("winResizeRepeater");
  4539. var dims = elDims( $N, o.inset );
  4540. // only trigger resizeAll() if container has changed size
  4541. if (dims.innerWidth !== sC.innerWidth || dims.innerHeight !== sC.innerHeight)
  4542. resizeAll();
  4543. }, delay);
  4544. // ALSO set fixed-delay timer, if not already running
  4545. if (!timer.data["winResizeRepeater"]) setWindowResizeRepeater();
  4546. }
  4547. , setWindowResizeRepeater = function () {
  4548. var delay = Number(options.resizeWithWindowMaxDelay);
  4549. if (delay > 0)
  4550. timer.set("winResizeRepeater", function(){ setWindowResizeRepeater(); resizeAll(); }, delay);
  4551. }
  4552. , unload = function () {
  4553. var o = options;
  4554. _runCallbacks("onunload_start");
  4555. // trigger plugin callabacks for this layout (eg: stateManagement)
  4556. runPluginCallbacks( Instance, $.layout.onUnload );
  4557. _runCallbacks("onunload_end");
  4558. }
  4559. /**
  4560. * Validate and initialize container CSS and events
  4561. *
  4562. * @see _create()
  4563. */
  4564. , _initContainer = function () {
  4565. var
  4566. N = $N[0]
  4567. , $H = $("html")
  4568. , tag = sC.tagName = N.tagName
  4569. , id = sC.id = N.id
  4570. , cls = sC.className = N.className
  4571. , o = options
  4572. , name = o.name
  4573. , props = "position,margin,padding,border"
  4574. , css = "layoutCSS"
  4575. , CSS = {}
  4576. , hid = "hidden" // used A LOT!
  4577. // see if this container is a 'pane' inside an outer-layout
  4578. , parent = $N.data("parentLayout") // parent-layout Instance
  4579. , pane = $N.data("layoutEdge") // pane-name in parent-layout
  4580. , isChild = parent && pane
  4581. , num = $.layout.cssNum
  4582. , $parent, n
  4583. ;
  4584. // sC = state.container
  4585. sC.selector = $N.selector.split(".slice")[0];
  4586. sC.ref = (o.name ? o.name +' layout / ' : '') + tag + (id ? "#"+id : cls ? '.['+cls+']' : ''); // used in messages
  4587. sC.isBody = (tag === "BODY");
  4588. // try to find a parent-layout
  4589. if (!isChild && !sC.isBody) {
  4590. $parent = $N.closest("."+ $.layout.defaults.panes.paneClass);
  4591. parent = $parent.data("parentLayout");
  4592. pane = $parent.data("layoutEdge");
  4593. isChild = parent && pane;
  4594. }
  4595. $N .data({
  4596. layout: Instance
  4597. , layoutContainer: sID // FLAG to indicate this is a layout-container - contains unique internal ID
  4598. })
  4599. .addClass(o.containerClass)
  4600. ;
  4601. var layoutMethods = {
  4602. destroy: ''
  4603. , initPanes: ''
  4604. , resizeAll: 'resizeAll'
  4605. , resize: 'resizeAll'
  4606. };
  4607. // loop hash and bind all methods - include layoutID namespacing
  4608. for (name in layoutMethods) {
  4609. $N.bind("layout"+ name.toLowerCase() +"."+ sID, Instance[ layoutMethods[name] || name ]);
  4610. }
  4611. // if this container is another layout's 'pane', then set child/parent pointers
  4612. if (isChild) {
  4613. // update parent flag
  4614. Instance.hasParentLayout = true;
  4615. // set pointers to THIS child-layout (Instance) in parent-layout
  4616. parent.refreshChildren( pane, Instance );
  4617. }
  4618. // SAVE original container CSS for use in destroy()
  4619. if (!$N.data(css)) {
  4620. // handle props like overflow different for BODY & HTML - has 'system default' values
  4621. if (sC.isBody) {
  4622. // SAVE <BODY> CSS
  4623. $N.data(css, $.extend( styles($N, props), {
  4624. height: $N.css("height")
  4625. , overflow: $N.css("overflow")
  4626. , overflowX: $N.css("overflowX")
  4627. , overflowY: $N.css("overflowY")
  4628. }));
  4629. // ALSO SAVE <HTML> CSS
  4630. $H.data(css, $.extend( styles($H, 'padding'), {
  4631. height: "auto" // FF would return a fixed px-size!
  4632. , overflow: $H.css("overflow")
  4633. , overflowX: $H.css("overflowX")
  4634. , overflowY: $H.css("overflowY")
  4635. }));
  4636. }
  4637. else // handle props normally for non-body elements
  4638. $N.data(css, styles($N, props+",top,bottom,left,right,width,height,overflow,overflowX,overflowY") );
  4639. }
  4640. try {
  4641. // common container CSS
  4642. CSS = {
  4643. overflow: hid
  4644. , overflowX: hid
  4645. , overflowY: hid
  4646. };
  4647. $N.css( CSS );
  4648. if (o.inset && !$.isPlainObject(o.inset)) {
  4649. // can specify a single number for equal outset all-around
  4650. n = parseInt(o.inset, 10) || 0
  4651. o.inset = {
  4652. top: n
  4653. , bottom: n
  4654. , left: n
  4655. , right: n
  4656. };
  4657. }
  4658. // format html & body if this is a full page layout
  4659. if (sC.isBody) {
  4660. // if HTML has padding, use this as an outer-spacing around BODY
  4661. if (!o.outset) {
  4662. // use padding from parent-elem (HTML) as outset
  4663. o.outset = {
  4664. top: num($H, "paddingTop")
  4665. , bottom: num($H, "paddingBottom")
  4666. , left: num($H, "paddingLeft")
  4667. , right: num($H, "paddingRight")
  4668. };
  4669. }
  4670. else if (!$.isPlainObject(o.outset)) {
  4671. // can specify a single number for equal outset all-around
  4672. n = parseInt(o.outset, 10) || 0
  4673. o.outset = {
  4674. top: n
  4675. , bottom: n
  4676. , left: n
  4677. , right: n
  4678. };
  4679. }
  4680. // HTML
  4681. $H.css( CSS ).css({
  4682. height: "100%"
  4683. , border: "none" // no border or padding allowed when using height = 100%
  4684. , padding: 0 // ditto
  4685. , margin: 0
  4686. });
  4687. // BODY
  4688. if (browser.isIE6) {
  4689. // IE6 CANNOT use the trick of setting absolute positioning on all 4 sides - must have 'height'
  4690. $N.css({
  4691. width: "100%"
  4692. , height: "100%"
  4693. , border: "none" // no border or padding allowed when using height = 100%
  4694. , padding: 0 // ditto
  4695. , margin: 0
  4696. , position: "relative"
  4697. });
  4698. // convert body padding to an inset option - the border cannot be measured in IE6!
  4699. if (!o.inset) o.inset = elDims( $N ).inset;
  4700. }
  4701. else { // use absolute positioning for BODY to allow borders & padding without overflow
  4702. $N.css({
  4703. width: "auto"
  4704. , height: "auto"
  4705. , margin: 0
  4706. , position: "absolute" // allows for border and padding on BODY
  4707. });
  4708. // apply edge-positioning created above
  4709. $N.css( o.outset );
  4710. }
  4711. // set current layout-container dimensions
  4712. $.extend(sC, elDims( $N, o.inset )); // passing inset means DO NOT include insetX values
  4713. }
  4714. else {
  4715. // container MUST have 'position'
  4716. var p = $N.css("position");
  4717. if (!p || !p.match(/(fixed|absolute|relative)/))
  4718. $N.css("position","relative");
  4719. // set current layout-container dimensions
  4720. if ( $N.is(":visible") ) {
  4721. $.extend(sC, elDims( $N, o.inset )); // passing inset means DO NOT change insetX (padding) values
  4722. if (sC.innerHeight < 1) // container has no 'height' - warn developer
  4723. _log( o.errors.noContainerHeight.replace(/CONTAINER/, sC.ref) );
  4724. }
  4725. }
  4726. // if container has min-width/height, then enable scrollbar(s)
  4727. if ( num($N, "minWidth") ) $N.parent().css("overflowX","auto");
  4728. if ( num($N, "minHeight") ) $N.parent().css("overflowY","auto");
  4729. } catch (ex) {}
  4730. }
  4731. /**
  4732. * Bind layout hotkeys - if options enabled
  4733. *
  4734. * @see _create() and addPane()
  4735. * @param {string=} [panes=""] The edge(s) to process
  4736. */
  4737. , initHotkeys = function (panes) {
  4738. panes = panes ? panes.split(",") : _c.borderPanes;
  4739. // bind keyDown to capture hotkeys, if option enabled for ANY pane
  4740. $.each(panes, function (i, pane) {
  4741. var o = options[pane];
  4742. if (o.enableCursorHotkey || o.customHotkey) {
  4743. $(document).bind("keydown."+ sID, keyDown); // only need to bind this ONCE
  4744. return false; // BREAK - binding was done
  4745. }
  4746. });
  4747. }
  4748. /**
  4749. * Build final OPTIONS data
  4750. *
  4751. * @see _create()
  4752. */
  4753. , initOptions = function () {
  4754. var data, d, pane, key, val, i, c, o;
  4755. // reprocess user's layout-options to have correct options sub-key structure
  4756. opts = $.layout.transformData( opts, true ); // panes = default subkey
  4757. // auto-rename old options for backward compatibility
  4758. opts = $.layout.backwardCompatibility.renameAllOptions( opts );
  4759. // if user-options has 'panes' key (pane-defaults), clean it...
  4760. if (!$.isEmptyObject(opts.panes)) {
  4761. // REMOVE any pane-defaults that MUST be set per-pane
  4762. data = $.layout.optionsMap.noDefault;
  4763. for (i=0, c=data.length; i<c; i++) {
  4764. key = data[i];
  4765. delete opts.panes[key]; // OK if does not exist
  4766. }
  4767. // REMOVE any layout-options specified under opts.panes
  4768. data = $.layout.optionsMap.layout;
  4769. for (i=0, c=data.length; i<c; i++) {
  4770. key = data[i];
  4771. delete opts.panes[key]; // OK if does not exist
  4772. }
  4773. }
  4774. // MOVE any NON-layout-options from opts-root to opts.panes
  4775. data = $.layout.optionsMap.layout;
  4776. var rootKeys = $.layout.config.optionRootKeys;
  4777. for (key in opts) {
  4778. val = opts[key];
  4779. if ($.inArray(key, rootKeys) < 0 && $.inArray(key, data) < 0) {
  4780. if (!opts.panes[key])
  4781. opts.panes[key] = $.isPlainObject(val) ? $.extend(true, {}, val) : val;
  4782. delete opts[key]
  4783. }
  4784. }
  4785. // START by updating ALL options from opts
  4786. $.extend(true, options, opts);
  4787. // CREATE final options (and config) for EACH pane
  4788. $.each(_c.allPanes, function (i, pane) {
  4789. // apply 'pane-defaults' to CONFIG.[PANE]
  4790. _c[pane] = $.extend(true, {}, _c.panes, _c[pane]);
  4791. d = options.panes;
  4792. o = options[pane];
  4793. // center-pane uses SOME keys in defaults.panes branch
  4794. if (pane === 'center') {
  4795. // ONLY copy keys from opts.panes listed in: $.layout.optionsMap.center
  4796. data = $.layout.optionsMap.center; // list of 'center-pane keys'
  4797. for (i=0, c=data.length; i<c; i++) { // loop the list...
  4798. key = data[i];
  4799. // only need to use pane-default if pane-specific value not set
  4800. if (!opts.center[key] && (opts.panes[key] || !o[key]))
  4801. o[key] = d[key]; // pane-default
  4802. }
  4803. }
  4804. else {
  4805. // border-panes use ALL keys in defaults.panes branch
  4806. o = options[pane] = $.extend(true, {}, d, o); // re-apply pane-specific opts AFTER pane-defaults
  4807. createFxOptions( pane );
  4808. // ensure all border-pane-specific base-classes exist
  4809. if (!o.resizerClass) o.resizerClass = "ui-layout-resizer";
  4810. if (!o.togglerClass) o.togglerClass = "ui-layout-toggler";
  4811. }
  4812. // ensure we have base pane-class (ALL panes)
  4813. if (!o.paneClass) o.paneClass = "ui-layout-pane";
  4814. });
  4815. // update options.zIndexes if a zIndex-option specified
  4816. var zo = opts.zIndex
  4817. , z = options.zIndexes;
  4818. if (zo > 0) {
  4819. z.pane_normal = zo;
  4820. z.content_mask = max(zo+1, z.content_mask); // MIN = +1
  4821. z.resizer_normal = max(zo+2, z.resizer_normal); // MIN = +2
  4822. }
  4823. // DELETE 'panes' key now that we are done - values were copied to EACH pane
  4824. delete options.panes;
  4825. function createFxOptions ( pane ) {
  4826. var o = options[pane]
  4827. , d = options.panes;
  4828. // ensure fxSettings key to avoid errors
  4829. if (!o.fxSettings) o.fxSettings = {};
  4830. if (!d.fxSettings) d.fxSettings = {};
  4831. $.each(["_open","_close","_size"], function (i,n) {
  4832. var
  4833. sName = "fxName"+ n
  4834. , sSpeed = "fxSpeed"+ n
  4835. , sSettings = "fxSettings"+ n
  4836. // recalculate fxName according to specificity rules
  4837. , fxName = o[sName] =
  4838. o[sName] // options.west.fxName_open
  4839. || d[sName] // options.panes.fxName_open
  4840. || o.fxName // options.west.fxName
  4841. || d.fxName // options.panes.fxName
  4842. || "none" // MEANS $.layout.defaults.panes.fxName == "" || false || null || 0
  4843. , fxExists = $.effects && ($.effects[fxName] || ($.effects.effect && $.effects.effect[fxName]))
  4844. ;
  4845. // validate fxName to ensure is valid effect - MUST have effect-config data in options.effects
  4846. if (fxName === "none" || !options.effects[fxName] || !fxExists)
  4847. fxName = o[sName] = "none"; // effect not loaded OR unrecognized fxName
  4848. // set vars for effects subkeys to simplify logic
  4849. var fx = options.effects[fxName] || {} // effects.slide
  4850. , fx_all = fx.all || null // effects.slide.all
  4851. , fx_pane = fx[pane] || null // effects.slide.west
  4852. ;
  4853. // create fxSpeed[_open|_close|_size]
  4854. o[sSpeed] =
  4855. o[sSpeed] // options.west.fxSpeed_open
  4856. || d[sSpeed] // options.west.fxSpeed_open
  4857. || o.fxSpeed // options.west.fxSpeed
  4858. || d.fxSpeed // options.panes.fxSpeed
  4859. || null // DEFAULT - let fxSetting.duration control speed
  4860. ;
  4861. // create fxSettings[_open|_close|_size]
  4862. o[sSettings] = $.extend(
  4863. true
  4864. , {}
  4865. , fx_all // effects.slide.all
  4866. , fx_pane // effects.slide.west
  4867. , d.fxSettings // options.panes.fxSettings
  4868. , o.fxSettings // options.west.fxSettings
  4869. , d[sSettings] // options.panes.fxSettings_open
  4870. , o[sSettings] // options.west.fxSettings_open
  4871. );
  4872. });
  4873. // DONE creating action-specific-settings for this pane,
  4874. // so DELETE generic options - are no longer meaningful
  4875. delete o.fxName;
  4876. delete o.fxSpeed;
  4877. delete o.fxSettings;
  4878. }
  4879. }
  4880. /**
  4881. * Initialize module objects, styling, size and position for all panes
  4882. *
  4883. * @see _initElements()
  4884. * @param {string} pane The pane to process
  4885. */
  4886. , getPane = function (pane) {
  4887. var sel = options[pane].paneSelector
  4888. if (sel.substr(0,1)==="#") // ID selector
  4889. // NOTE: elements selected 'by ID' DO NOT have to be 'children'
  4890. return $N.find(sel).eq(0);
  4891. else { // class or other selector
  4892. var $P = $N.children(sel).eq(0);
  4893. // look for the pane nested inside a 'form' element
  4894. return $P.length ? $P : $N.children("form:first").children(sel).eq(0);
  4895. }
  4896. }
  4897. /**
  4898. * @param {Object=} evt
  4899. */
  4900. , initPanes = function (evt) {
  4901. // stopPropagation if called by trigger("layoutinitpanes") - use evtPane utility
  4902. evtPane(evt);
  4903. // NOTE: do north & south FIRST so we can measure their height - do center LAST
  4904. $.each(_c.allPanes, function (idx, pane) {
  4905. addPane( pane, true );
  4906. });
  4907. // init the pane-handles NOW in case we have to hide or close the pane below
  4908. initHandles();
  4909. // now that all panes have been initialized and initially-sized,
  4910. // make sure there is really enough space available for each pane
  4911. $.each(_c.borderPanes, function (i, pane) {
  4912. if ($Ps[pane] && state[pane].isVisible) { // pane is OPEN
  4913. setSizeLimits(pane);
  4914. makePaneFit(pane); // pane may be Closed, Hidden or Resized by makePaneFit()
  4915. }
  4916. });
  4917. // size center-pane AGAIN in case we 'closed' a border-pane in loop above
  4918. sizeMidPanes("center");
  4919. // Chrome/Webkit sometimes fires callbacks BEFORE it completes resizing!
  4920. // Before RC30.3, there was a 10ms delay here, but that caused layout
  4921. // to load asynchrously, which is BAD, so try skipping delay for now
  4922. // process pane contents and callbacks, and init/resize child-layout if exists
  4923. $.each(_c.allPanes, function (idx, pane) {
  4924. afterInitPane(pane);
  4925. });
  4926. }
  4927. /**
  4928. * Add a pane to the layout - subroutine of initPanes()
  4929. *
  4930. * @see initPanes()
  4931. * @param {string} pane The pane to process
  4932. * @param {boolean=} [force=false] Size content after init
  4933. */
  4934. , addPane = function (pane, force) {
  4935. if (!force && !isInitialized()) return;
  4936. var
  4937. o = options[pane]
  4938. , s = state[pane]
  4939. , c = _c[pane]
  4940. , dir = c.dir
  4941. , fx = s.fx
  4942. , spacing = o.spacing_open || 0
  4943. , isCenter = (pane === "center")
  4944. , CSS = {}
  4945. , $P = $Ps[pane]
  4946. , size, minSize, maxSize, child
  4947. ;
  4948. // if pane-pointer already exists, remove the old one first
  4949. if ($P)
  4950. removePane( pane, false, true, false );
  4951. else
  4952. $Cs[pane] = false; // init
  4953. $P = $Ps[pane] = getPane(pane);
  4954. if (!$P.length) {
  4955. $Ps[pane] = false; // logic
  4956. return;
  4957. }
  4958. // SAVE original Pane CSS
  4959. if (!$P.data("layoutCSS")) {
  4960. var props = "position,top,left,bottom,right,width,height,overflow,zIndex,display,backgroundColor,padding,margin,border";
  4961. $P.data("layoutCSS", styles($P, props));
  4962. }
  4963. // create alias for pane data in Instance - initHandles will add more
  4964. Instance[pane] = {
  4965. name: pane
  4966. , pane: $Ps[pane]
  4967. , content: $Cs[pane]
  4968. , options: options[pane]
  4969. , state: state[pane]
  4970. , children: children[pane]
  4971. };
  4972. // add classes, attributes & events
  4973. $P .data({
  4974. parentLayout: Instance // pointer to Layout Instance
  4975. , layoutPane: Instance[pane] // NEW pointer to pane-alias-object
  4976. , layoutEdge: pane
  4977. , layoutRole: "pane"
  4978. })
  4979. .css(c.cssReq).css("zIndex", options.zIndexes.pane_normal)
  4980. .css(o.applyDemoStyles ? c.cssDemo : {}) // demo styles
  4981. .addClass( o.paneClass +" "+ o.paneClass+"-"+pane ) // default = "ui-layout-pane ui-layout-pane-west" - may be a dupe of 'paneSelector'
  4982. .bind("mouseenter."+ sID, addHover )
  4983. .bind("mouseleave."+ sID, removeHover )
  4984. ;
  4985. var paneMethods = {
  4986. hide: ''
  4987. , show: ''
  4988. , toggle: ''
  4989. , close: ''
  4990. , open: ''
  4991. , slideOpen: ''
  4992. , slideClose: ''
  4993. , slideToggle: ''
  4994. , size: 'sizePane'
  4995. , sizePane: 'sizePane'
  4996. , sizeContent: ''
  4997. , sizeHandles: ''
  4998. , enableClosable: ''
  4999. , disableClosable: ''
  5000. , enableSlideable: ''
  5001. , disableSlideable: ''
  5002. , enableResizable: ''
  5003. , disableResizable: ''
  5004. , swapPanes: 'swapPanes'
  5005. , swap: 'swapPanes'
  5006. , move: 'swapPanes'
  5007. , removePane: 'removePane'
  5008. , remove: 'removePane'
  5009. , createChildren: ''
  5010. , resizeChildren: ''
  5011. , resizeAll: 'resizeAll'
  5012. , resizeLayout: 'resizeAll'
  5013. }
  5014. , name;
  5015. // loop hash and bind all methods - include layoutID namespacing
  5016. for (name in paneMethods) {
  5017. $P.bind("layoutpane"+ name.toLowerCase() +"."+ sID, Instance[ paneMethods[name] || name ]);
  5018. }
  5019. // see if this pane has a 'scrolling-content element'
  5020. initContent(pane, false); // false = do NOT sizeContent() - called later
  5021. if (!isCenter) {
  5022. // call _parseSize AFTER applying pane classes & styles - but before making visible (if hidden)
  5023. // if o.size is auto or not valid, then MEASURE the pane and use that as its 'size'
  5024. size = s.size = _parseSize(pane, o.size);
  5025. minSize = _parseSize(pane,o.minSize) || 1;
  5026. maxSize = _parseSize(pane,o.maxSize) || 100000;
  5027. if (size > 0) size = max(min(size, maxSize), minSize);
  5028. s.autoResize = o.autoResize; // used with percentage sizes
  5029. // state for border-panes
  5030. s.isClosed = false; // true = pane is closed
  5031. s.isSliding = false; // true = pane is currently open by 'sliding' over adjacent panes
  5032. s.isResizing= false; // true = pane is in process of being resized
  5033. s.isHidden = false; // true = pane is hidden - no spacing, resizer or toggler is visible!
  5034. // array for 'pin buttons' whose classNames are auto-updated on pane-open/-close
  5035. if (!s.pins) s.pins = [];
  5036. }
  5037. // states common to ALL panes
  5038. s.tagName = $P[0].tagName;
  5039. s.edge = pane; // useful if pane is (or about to be) 'swapped' - easy find out where it is (or is going)
  5040. s.noRoom = false; // true = pane 'automatically' hidden due to insufficient room - will unhide automatically
  5041. s.isVisible = true; // false = pane is invisible - closed OR hidden - simplify logic
  5042. // init pane positioning
  5043. setPanePosition( pane );
  5044. // if pane is not visible,
  5045. if (dir === "horz") // north or south pane
  5046. CSS.height = cssH($P, size);
  5047. else if (dir === "vert") // east or west pane
  5048. CSS.width = cssW($P, size);
  5049. //else if (isCenter) {}
  5050. $P.css(CSS); // apply size -- top, bottom & height will be set by sizeMidPanes
  5051. if (dir != "horz") sizeMidPanes(pane, true); // true = skipCallback
  5052. // if manually adding a pane AFTER layout initialization, then...
  5053. if (state.initialized) {
  5054. initHandles( pane );
  5055. initHotkeys( pane );
  5056. }
  5057. // close or hide the pane if specified in settings
  5058. if (o.initClosed && o.closable && !o.initHidden)
  5059. close(pane, true, true); // true, true = force, noAnimation
  5060. else if (o.initHidden || o.initClosed)
  5061. hide(pane); // will be completely invisible - no resizer or spacing
  5062. else if (!s.noRoom)
  5063. // make the pane visible - in case was initially hidden
  5064. $P.css("display","block");
  5065. // ELSE setAsOpen() - called later by initHandles()
  5066. // RESET visibility now - pane will appear IF display:block
  5067. $P.css("visibility","visible");
  5068. // check option for auto-handling of pop-ups & drop-downs
  5069. if (o.showOverflowOnHover)
  5070. $P.hover( allowOverflow, resetOverflow );
  5071. // if manually adding a pane AFTER layout initialization, then...
  5072. if (state.initialized) {
  5073. afterInitPane( pane );
  5074. }
  5075. }
  5076. , afterInitPane = function (pane) {
  5077. var $P = $Ps[pane]
  5078. , s = state[pane]
  5079. , o = options[pane]
  5080. ;
  5081. if (!$P) return;
  5082. // see if there is a directly-nested layout inside this pane
  5083. if ($P.data("layout"))
  5084. refreshChildren( pane, $P.data("layout") );
  5085. // process pane contents and callbacks, and init/resize child-layout if exists
  5086. if (s.isVisible) { // pane is OPEN
  5087. if (state.initialized) // this pane was added AFTER layout was created
  5088. resizeAll(); // will also sizeContent
  5089. else
  5090. sizeContent(pane);
  5091. if (o.triggerEventsOnLoad)
  5092. _runCallbacks("onresize_end", pane);
  5093. else // automatic if onresize called, otherwise call it specifically
  5094. // resize child - IF inner-layout already exists (created before this layout)
  5095. resizeChildren(pane, true); // a previously existing childLayout
  5096. }
  5097. // init childLayouts - even if pane is not visible
  5098. if (o.initChildren && o.children)
  5099. createChildren(pane);
  5100. }
  5101. /**
  5102. * @param {string=} panes The pane(s) to process
  5103. */
  5104. , setPanePosition = function (panes) {
  5105. panes = panes ? panes.split(",") : _c.borderPanes;
  5106. // create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV
  5107. $.each(panes, function (i, pane) {
  5108. var $P = $Ps[pane]
  5109. , $R = $Rs[pane]
  5110. , o = options[pane]
  5111. , s = state[pane]
  5112. , side = _c[pane].side
  5113. , CSS = {}
  5114. ;
  5115. if (!$P) return; // pane does not exist - skip
  5116. // set css-position to account for container borders & padding
  5117. switch (pane) {
  5118. case "north": CSS.top = sC.inset.top;
  5119. CSS.left = sC.inset.left;
  5120. CSS.right = sC.inset.right;
  5121. break;
  5122. case "south": CSS.bottom = sC.inset.bottom;
  5123. CSS.left = sC.inset.left;
  5124. CSS.right = sC.inset.right;
  5125. break;
  5126. case "west": CSS.left = sC.inset.left; // top, bottom & height set by sizeMidPanes()
  5127. break;
  5128. case "east": CSS.right = sC.inset.right; // ditto
  5129. break;
  5130. case "center": // top, left, width & height set by sizeMidPanes()
  5131. }
  5132. // apply position
  5133. $P.css(CSS);
  5134. // update resizer position
  5135. if ($R && s.isClosed)
  5136. $R.css(side, sC.inset[side]);
  5137. else if ($R && !s.isHidden)
  5138. $R.css(side, sC.inset[side] + getPaneSize(pane));
  5139. });
  5140. }
  5141. /**
  5142. * Initialize module objects, styling, size and position for all resize bars and toggler buttons
  5143. *
  5144. * @see _create()
  5145. * @param {string=} [panes=""] The edge(s) to process
  5146. */
  5147. , initHandles = function (panes) {
  5148. panes = panes ? panes.split(",") : _c.borderPanes;
  5149. // create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV
  5150. $.each(panes, function (i, pane) {
  5151. var $P = $Ps[pane];
  5152. $Rs[pane] = false; // INIT
  5153. $Ts[pane] = false;
  5154. if (!$P) return; // pane does not exist - skip
  5155. var o = options[pane]
  5156. , s = state[pane]
  5157. , c = _c[pane]
  5158. , paneId = o.paneSelector.substr(0,1) === "#" ? o.paneSelector.substr(1) : ""
  5159. , rClass = o.resizerClass
  5160. , tClass = o.togglerClass
  5161. , spacing = (s.isVisible ? o.spacing_open : o.spacing_closed)
  5162. , _pane = "-"+ pane // used for classNames
  5163. , _state = (s.isVisible ? "-open" : "-closed") // used for classNames
  5164. , I = Instance[pane]
  5165. // INIT RESIZER BAR
  5166. , $R = I.resizer = $Rs[pane] = $("<div></div>")
  5167. // INIT TOGGLER BUTTON
  5168. , $T = I.toggler = (o.closable ? $Ts[pane] = $("<div></div>") : false)
  5169. ;
  5170. //if (s.isVisible && o.resizable) ... handled by initResizable
  5171. if (!s.isVisible && o.slidable)
  5172. $R.attr("title", o.tips.Slide).css("cursor", o.sliderCursor);
  5173. $R // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-resizer"
  5174. .attr("id", paneId ? paneId +"-resizer" : "" )
  5175. .data({
  5176. parentLayout: Instance
  5177. , layoutPane: Instance[pane] // NEW pointer to pane-alias-object
  5178. , layoutEdge: pane
  5179. , layoutRole: "resizer"
  5180. })
  5181. .css(_c.resizers.cssReq).css("zIndex", options.zIndexes.resizer_normal)
  5182. .css(o.applyDemoStyles ? _c.resizers.cssDemo : {}) // add demo styles
  5183. .addClass(rClass +" "+ rClass+_pane)
  5184. .hover(addHover, removeHover) // ALWAYS add hover-classes, even if resizing is not enabled - handle with CSS instead
  5185. .hover(onResizerEnter, onResizerLeave) // ALWAYS NEED resizer.mouseleave to balance toggler.mouseenter
  5186. .appendTo($N) // append DIV to container
  5187. ;
  5188. if (o.resizerDblClickToggle)
  5189. $R.bind("dblclick."+ sID, toggle );
  5190. if ($T) {
  5191. $T // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "#paneLeft-toggler"
  5192. .attr("id", paneId ? paneId +"-toggler" : "" )
  5193. .data({
  5194. parentLayout: Instance
  5195. , layoutPane: Instance[pane] // NEW pointer to pane-alias-object
  5196. , layoutEdge: pane
  5197. , layoutRole: "toggler"
  5198. })
  5199. .css(_c.togglers.cssReq) // add base/required styles
  5200. .css(o.applyDemoStyles ? _c.togglers.cssDemo : {}) // add demo styles
  5201. .addClass(tClass +" "+ tClass+_pane)
  5202. .hover(addHover, removeHover) // ALWAYS add hover-classes, even if toggling is not enabled - handle with CSS instead
  5203. .bind("mouseenter", onResizerEnter) // NEED toggler.mouseenter because mouseenter MAY NOT fire on resizer
  5204. .appendTo($R) // append SPAN to resizer DIV
  5205. ;
  5206. // ADD INNER-SPANS TO TOGGLER
  5207. if (o.togglerContent_open) // ui-layout-open
  5208. $("<span>"+ o.togglerContent_open +"</span>")
  5209. .data({
  5210. layoutEdge: pane
  5211. , layoutRole: "togglerContent"
  5212. })
  5213. .data("layoutRole", "togglerContent")
  5214. .data("layoutEdge", pane)
  5215. .addClass("content content-open")
  5216. .css("display","none")
  5217. .appendTo( $T )
  5218. //.hover( addHover, removeHover ) // use ui-layout-toggler-west-hover .content-open instead!
  5219. ;
  5220. if (o.togglerContent_closed) // ui-layout-closed
  5221. $("<span>"+ o.togglerContent_closed +"</span>")
  5222. .data({
  5223. layoutEdge: pane
  5224. , layoutRole: "togglerContent"
  5225. })
  5226. .addClass("content content-closed")
  5227. .css("display","none")
  5228. .appendTo( $T )
  5229. //.hover( addHover, removeHover ) // use ui-layout-toggler-west-hover .content-closed instead!
  5230. ;
  5231. // ADD TOGGLER.click/.hover
  5232. enableClosable(pane);
  5233. }
  5234. // add Draggable events
  5235. initResizable(pane);
  5236. // ADD CLASSNAMES & SLIDE-BINDINGS - eg: class="resizer resizer-west resizer-open"
  5237. if (s.isVisible)
  5238. setAsOpen(pane); // onOpen will be called, but NOT onResize
  5239. else {
  5240. setAsClosed(pane); // onClose will be called
  5241. bindStartSlidingEvents(pane, true); // will enable events IF option is set
  5242. }
  5243. });
  5244. // SET ALL HANDLE DIMENSIONS
  5245. sizeHandles();
  5246. }
  5247. /**
  5248. * Initialize scrolling ui-layout-content div - if exists
  5249. *
  5250. * @see initPane() - or externally after an Ajax injection
  5251. * @param {string} pane The pane to process
  5252. * @param {boolean=} [resize=true] Size content after init
  5253. */
  5254. , initContent = function (pane, resize) {
  5255. if (!isInitialized()) return;
  5256. var
  5257. o = options[pane]
  5258. , sel = o.contentSelector
  5259. , I = Instance[pane]
  5260. , $P = $Ps[pane]
  5261. , $C
  5262. ;
  5263. if (sel) $C = I.content = $Cs[pane] = (o.findNestedContent)
  5264. ? $P.find(sel).eq(0) // match 1-element only
  5265. : $P.children(sel).eq(0)
  5266. ;
  5267. if ($C && $C.length) {
  5268. $C.data("layoutRole", "content");
  5269. // SAVE original Content CSS
  5270. if (!$C.data("layoutCSS"))
  5271. $C.data("layoutCSS", styles($C, "height"));
  5272. $C.css( _c.content.cssReq );
  5273. if (o.applyDemoStyles) {
  5274. $C.css( _c.content.cssDemo ); // add padding & overflow: auto to content-div
  5275. $P.css( _c.content.cssDemoPane ); // REMOVE padding/scrolling from pane
  5276. }
  5277. // ensure no vertical scrollbar on pane - will mess up measurements
  5278. if ($P.css("overflowX").match(/(scroll|auto)/)) {
  5279. $P.css("overflow", "hidden");
  5280. }
  5281. state[pane].content = {}; // init content state
  5282. if (resize !== false) sizeContent(pane);
  5283. // sizeContent() is called AFTER init of all elements
  5284. }
  5285. else
  5286. I.content = $Cs[pane] = false;
  5287. }
  5288. /**
  5289. * Add resize-bars to all panes that specify it in options
  5290. * -dependancy: $.fn.resizable - will skip if not found
  5291. *
  5292. * @see _create()
  5293. * @param {string=} [panes=""] The edge(s) to process
  5294. */
  5295. , initResizable = function (panes) {
  5296. var draggingAvailable = $.layout.plugins.draggable
  5297. , side // set in start()
  5298. ;
  5299. panes = panes ? panes.split(",") : _c.borderPanes;
  5300. $.each(panes, function (idx, pane) {
  5301. var o = options[pane];
  5302. if (!draggingAvailable || !$Ps[pane] || !o.resizable) {
  5303. o.resizable = false;
  5304. return true; // skip to next
  5305. }
  5306. var s = state[pane]
  5307. , z = options.zIndexes
  5308. , c = _c[pane]
  5309. , side = c.dir=="horz" ? "top" : "left"
  5310. , $P = $Ps[pane]
  5311. , $R = $Rs[pane]
  5312. , base = o.resizerClass
  5313. , lastPos = 0 // used when live-resizing
  5314. , r, live // set in start because may change
  5315. // 'drag' classes are applied to the ORIGINAL resizer-bar while dragging is in process
  5316. , resizerClass = base+"-drag" // resizer-drag
  5317. , resizerPaneClass = base+"-"+pane+"-drag" // resizer-north-drag
  5318. // 'helper' class is applied to the CLONED resizer-bar while it is being dragged
  5319. , helperClass = base+"-dragging" // resizer-dragging
  5320. , helperPaneClass = base+"-"+pane+"-dragging" // resizer-north-dragging
  5321. , helperLimitClass = base+"-dragging-limit" // resizer-drag
  5322. , helperPaneLimitClass = base+"-"+pane+"-dragging-limit" // resizer-north-drag
  5323. , helperClassesSet = false // logic var
  5324. ;
  5325. if (!s.isClosed)
  5326. $R.attr("title", o.tips.Resize)
  5327. .css("cursor", o.resizerCursor); // n-resize, s-resize, etc
  5328. $R.draggable({
  5329. containment: $N[0] // limit resizing to layout container
  5330. , axis: (c.dir=="horz" ? "y" : "x") // limit resizing to horz or vert axis
  5331. , delay: 0
  5332. , distance: 1
  5333. , grid: o.resizingGrid
  5334. // basic format for helper - style it using class: .ui-draggable-dragging
  5335. , helper: "clone"
  5336. , opacity: o.resizerDragOpacity
  5337. , addClasses: false // avoid ui-state-disabled class when disabled
  5338. //, iframeFix: o.draggableIframeFix // TODO: consider using when bug is fixed
  5339. , zIndex: z.resizer_drag
  5340. , start: function (e, ui) {
  5341. // REFRESH options & state pointers in case we used swapPanes
  5342. o = options[pane];
  5343. s = state[pane];
  5344. // re-read options
  5345. live = o.livePaneResizing;
  5346. // ondrag_start callback - will CANCEL hide if returns false
  5347. // TODO: dragging CANNOT be cancelled like this, so see if there is a way?
  5348. if (false === _runCallbacks("ondrag_start", pane)) return false;
  5349. s.isResizing = true; // prevent pane from closing while resizing
  5350. state.paneResizing = pane; // easy to see if ANY pane is resizing
  5351. timer.clear(pane+"_closeSlider"); // just in case already triggered
  5352. // SET RESIZER LIMITS - used in drag()
  5353. setSizeLimits(pane); // update pane/resizer state
  5354. r = s.resizerPosition;
  5355. lastPos = ui.position[ side ]
  5356. $R.addClass( resizerClass +" "+ resizerPaneClass ); // add drag classes
  5357. helperClassesSet = false; // reset logic var - see drag()
  5358. // DISABLE TEXT SELECTION (probably already done by resizer.mouseOver)
  5359. $('body').disableSelection();
  5360. // MASK PANES CONTAINING IFRAMES, APPLETS OR OTHER TROUBLESOME ELEMENTS
  5361. showMasks( pane, { resizing: true });
  5362. }
  5363. , drag: function (e, ui) {
  5364. if (!helperClassesSet) { // can only add classes after clone has been added to the DOM
  5365. //$(".ui-draggable-dragging")
  5366. ui.helper
  5367. .addClass( helperClass +" "+ helperPaneClass ) // add helper classes
  5368. .css({ right: "auto", bottom: "auto" }) // fix dir="rtl" issue
  5369. .children().css("visibility","hidden") // hide toggler inside dragged resizer-bar
  5370. ;
  5371. helperClassesSet = true;
  5372. // draggable bug!? RE-SET zIndex to prevent E/W resize-bar showing through N/S pane!
  5373. if (s.isSliding) $Ps[pane].css("zIndex", z.pane_sliding);
  5374. }
  5375. // CONTAIN RESIZER-BAR TO RESIZING LIMITS
  5376. var limit = 0;
  5377. if (ui.position[side] < r.min) {
  5378. ui.position[side] = r.min;
  5379. limit = -1;
  5380. }
  5381. else if (ui.position[side] > r.max) {
  5382. ui.position[side] = r.max;
  5383. limit = 1;
  5384. }
  5385. // ADD/REMOVE dragging-limit CLASS
  5386. if (limit) {
  5387. ui.helper.addClass( helperLimitClass +" "+ helperPaneLimitClass ); // at dragging-limit
  5388. window.defaultStatus = (limit>0 && pane.match(/(north|west)/)) || (limit<0 && pane.match(/(south|east)/)) ? o.tips.maxSizeWarning : o.tips.minSizeWarning;
  5389. }
  5390. else {
  5391. ui.helper.removeClass( helperLimitClass +" "+ helperPaneLimitClass ); // not at dragging-limit
  5392. window.defaultStatus = "";
  5393. }
  5394. // DYNAMICALLY RESIZE PANES IF OPTION ENABLED
  5395. // won't trigger unless resizer has actually moved!
  5396. if (live && Math.abs(ui.position[side] - lastPos) >= o.liveResizingTolerance) {
  5397. lastPos = ui.position[side];
  5398. resizePanes(e, ui, pane)
  5399. }
  5400. }
  5401. , stop: function (e, ui) {
  5402. $('body').enableSelection(); // RE-ENABLE TEXT SELECTION
  5403. window.defaultStatus = ""; // clear 'resizing limit' message from statusbar
  5404. $R.removeClass( resizerClass +" "+ resizerPaneClass ); // remove drag classes from Resizer
  5405. s.isResizing = false;
  5406. state.paneResizing = false; // easy to see if ANY pane is resizing
  5407. resizePanes(e, ui, pane, true); // true = resizingDone
  5408. }
  5409. });
  5410. });
  5411. /**
  5412. * resizePanes
  5413. *
  5414. * Sub-routine called from stop() - and drag() if livePaneResizing
  5415. *
  5416. * @param {!Object} evt
  5417. * @param {!Object} ui
  5418. * @param {string} pane
  5419. * @param {boolean=} [resizingDone=false]
  5420. */
  5421. var resizePanes = function (evt, ui, pane, resizingDone) {
  5422. var dragPos = ui.position
  5423. , c = _c[pane]
  5424. , o = options[pane]
  5425. , s = state[pane]
  5426. , resizerPos
  5427. ;
  5428. switch (pane) {
  5429. case "north": resizerPos = dragPos.top; break;
  5430. case "west": resizerPos = dragPos.left; break;
  5431. case "south": resizerPos = sC.layoutHeight - dragPos.top - o.spacing_open; break;
  5432. case "east": resizerPos = sC.layoutWidth - dragPos.left - o.spacing_open; break;
  5433. };
  5434. // remove container margin from resizer position to get the pane size
  5435. var newSize = resizerPos - sC.inset[c.side];
  5436. // Disable OR Resize Mask(s) created in drag.start
  5437. if (!resizingDone) {
  5438. // ensure we meet liveResizingTolerance criteria
  5439. if (Math.abs(newSize - s.size) < o.liveResizingTolerance)
  5440. return; // SKIP resize this time
  5441. // resize the pane
  5442. manualSizePane(pane, newSize, false, true); // true = noAnimation
  5443. sizeMasks(); // resize all visible masks
  5444. }
  5445. else { // resizingDone
  5446. // ondrag_end callback
  5447. if (false !== _runCallbacks("ondrag_end", pane))
  5448. manualSizePane(pane, newSize, false, true); // true = noAnimation
  5449. hideMasks(true); // true = force hiding all masks even if one is 'sliding'
  5450. if (s.isSliding) // RE-SHOW 'object-masks' so objects won't show through sliding pane
  5451. showMasks( pane, { resizing: true });
  5452. }
  5453. };
  5454. }
  5455. /**
  5456. * sizeMask
  5457. *
  5458. * Needed to overlay a DIV over an IFRAME-pane because mask CANNOT be *inside* the pane
  5459. * Called when mask created, and during livePaneResizing
  5460. */
  5461. , sizeMask = function () {
  5462. var $M = $(this)
  5463. , pane = $M.data("layoutMask") // eg: "west"
  5464. , s = state[pane]
  5465. ;
  5466. // only masks over an IFRAME-pane need manual resizing
  5467. if (s.tagName == "IFRAME" && s.isVisible) // no need to mask closed/hidden panes
  5468. $M.css({
  5469. top: s.offsetTop
  5470. , left: s.offsetLeft
  5471. , width: s.outerWidth
  5472. , height: s.outerHeight
  5473. });
  5474. /* ALT Method...
  5475. var $P = $Ps[pane];
  5476. $M.css( $P.position() ).css({ width: $P[0].offsetWidth, height: $P[0].offsetHeight });
  5477. */
  5478. }
  5479. , sizeMasks = function () {
  5480. $Ms.each( sizeMask ); // resize all 'visible' masks
  5481. }
  5482. /**
  5483. * @param {string} pane The pane being resized, animated or isSliding
  5484. * @param {Object=} [args] (optional) Options: which masks to apply, and to which panes
  5485. */
  5486. , showMasks = function (pane, args) {
  5487. var c = _c[pane]
  5488. , panes = ["center"]
  5489. , z = options.zIndexes
  5490. , a = $.extend({
  5491. objectsOnly: false
  5492. , animation: false
  5493. , resizing: true
  5494. , sliding: state[pane].isSliding
  5495. }, args )
  5496. , o, s
  5497. ;
  5498. if (a.resizing)
  5499. panes.push( pane );
  5500. if (a.sliding)
  5501. panes.push( _c.oppositeEdge[pane] ); // ADD the oppositeEdge-pane
  5502. if (c.dir === "horz") {
  5503. panes.push("west");
  5504. panes.push("east");
  5505. }
  5506. $.each(panes, function(i,p){
  5507. s = state[p];
  5508. o = options[p];
  5509. if (s.isVisible && ( o.maskObjects || (!a.objectsOnly && o.maskContents) )) {
  5510. getMasks(p).each(function(){
  5511. sizeMask.call(this);
  5512. this.style.zIndex = s.isSliding ? z.pane_sliding+1 : z.pane_normal+1
  5513. this.style.display = "block";
  5514. });
  5515. }
  5516. });
  5517. }
  5518. /**
  5519. * @param {boolean=} force Hide masks even if a pane is sliding
  5520. */
  5521. , hideMasks = function (force) {
  5522. // ensure no pane is resizing - could be a timing issue
  5523. if (force || !state.paneResizing) {
  5524. $Ms.hide(); // hide ALL masks
  5525. }
  5526. // if ANY pane is sliding, then DO NOT remove masks from panes with maskObjects enabled
  5527. else if (!force && !$.isEmptyObject( state.panesSliding )) {
  5528. var i = $Ms.length - 1
  5529. , p, $M;
  5530. for (; i >= 0; i--) {
  5531. $M = $Ms.eq(i);
  5532. p = $M.data("layoutMask");
  5533. if (!options[p].maskObjects) {
  5534. $M.hide();
  5535. }
  5536. }
  5537. }
  5538. }
  5539. /**
  5540. * @param {string} pane
  5541. */
  5542. , getMasks = function (pane) {
  5543. var $Masks = $([])
  5544. , $M, i = 0, c = $Ms.length
  5545. ;
  5546. for (; i<c; i++) {
  5547. $M = $Ms.eq(i);
  5548. if ($M.data("layoutMask") === pane)
  5549. $Masks = $Masks.add( $M );
  5550. }
  5551. if ($Masks.length)
  5552. return $Masks;
  5553. else
  5554. return createMasks(pane);
  5555. }
  5556. /**
  5557. * createMasks
  5558. *
  5559. * Generates both DIV (ALWAYS used) and IFRAME (optional) elements as masks
  5560. * An IFRAME mask is created *under* the DIV when maskObjects=true, because a DIV cannot mask an applet
  5561. *
  5562. * @param {string} pane
  5563. */
  5564. , createMasks = function (pane) {
  5565. var
  5566. $P = $Ps[pane]
  5567. , s = state[pane]
  5568. , o = options[pane]
  5569. , z = options.zIndexes
  5570. //, objMask = o.maskObjects && s.tagName != "IFRAME" // check for option
  5571. , $Masks = $([])
  5572. , isIframe, el, $M, css, i
  5573. ;
  5574. if (!o.maskContents && !o.maskObjects) return $Masks;
  5575. // if o.maskObjects=true, then loop TWICE to create BOTH kinds of mask, else only create a DIV
  5576. for (i=0; i < (o.maskObjects ? 2 : 1); i++) {
  5577. isIframe = o.maskObjects && i==0;
  5578. el = document.createElement( isIframe ? "iframe" : "div" );
  5579. $M = $(el).data("layoutMask", pane); // add data to relate mask to pane
  5580. el.className = "ui-layout-mask ui-layout-mask-"+ pane; // for user styling
  5581. css = el.style;
  5582. // styles common to both DIVs and IFRAMES
  5583. css.display = "block";
  5584. css.position = "absolute";
  5585. css.background = "#FFF";
  5586. if (isIframe) { // IFRAME-only props
  5587. el.frameborder = 0;
  5588. el.src = "about:blank";
  5589. //el.allowTransparency = true; - for IE, but breaks masking ability!
  5590. css.opacity = 0;
  5591. css.filter = "Alpha(Opacity='0')";
  5592. css.border = 0;
  5593. }
  5594. // if pane is an IFRAME, then must mask the pane itself
  5595. if (s.tagName == "IFRAME") {
  5596. // NOTE sizing done by a subroutine so can be called during live-resizing
  5597. css.zIndex = z.pane_normal+1; // 1-higher than pane
  5598. $N.append( el ); // append to LAYOUT CONTAINER
  5599. }
  5600. // otherwise put masks *inside the pane* to mask its contents
  5601. else {
  5602. $M.addClass("ui-layout-mask-inside-pane");
  5603. css.zIndex = o.maskZindex || z.content_mask; // usually 1, but customizable
  5604. css.top = 0;
  5605. css.left = 0;
  5606. css.width = "100%";
  5607. css.height = "100%";
  5608. $P.append( el ); // append INSIDE pane element
  5609. }
  5610. // add to return object
  5611. $Masks = $Masks.add( el );
  5612. // add Mask to cached array so can be resized & reused
  5613. $Ms = $Ms.add( el );
  5614. }
  5615. return $Masks;
  5616. }
  5617. /**
  5618. * Destroy this layout and reset all elements
  5619. *
  5620. * @param {boolean=} [destroyChildren=false] Destory Child-Layouts first?
  5621. */
  5622. , destroy = function (evt_or_destroyChildren, destroyChildren) {
  5623. // UNBIND layout events and remove global object
  5624. $(window).unbind("."+ sID); // resize & unload
  5625. $(document).unbind("."+ sID); // keyDown (hotkeys)
  5626. if (typeof evt_or_destroyChildren === "object")
  5627. // stopPropagation if called by trigger("layoutdestroy") - use evtPane utility
  5628. evtPane(evt_or_destroyChildren);
  5629. else // no event, so transfer 1st param to destroyChildren param
  5630. destroyChildren = evt_or_destroyChildren;
  5631. // need to look for parent layout BEFORE we remove the container data, else skips a level
  5632. //var parentPane = Instance.hasParentLayout ? $.layout.getParentPaneInstance( $N ) : null;
  5633. // reset layout-container
  5634. $N .clearQueue()
  5635. .removeData("layout")
  5636. .removeData("layoutContainer")
  5637. .removeClass(options.containerClass)
  5638. .unbind("."+ sID) // remove ALL Layout events
  5639. ;
  5640. // remove all mask elements that have been created
  5641. $Ms.remove();
  5642. // loop all panes to remove layout classes, attributes and bindings
  5643. $.each(_c.allPanes, function (i, pane) {
  5644. removePane( pane, false, true, destroyChildren ); // true = skipResize
  5645. });
  5646. // do NOT reset container CSS if is a 'pane' (or 'content') in an outer-layout - ie, THIS layout is 'nested'
  5647. var css = "layoutCSS";
  5648. if ($N.data(css) && !$N.data("layoutRole")) // RESET CSS
  5649. $N.css( $N.data(css) ).removeData(css);
  5650. // for full-page layouts, also reset the <HTML> CSS
  5651. if (sC.tagName === "BODY" && ($N = $("html")).data(css)) // RESET <HTML> CSS
  5652. $N.css( $N.data(css) ).removeData(css);
  5653. // trigger plugins for this layout, if there are any
  5654. runPluginCallbacks( Instance, $.layout.onDestroy );
  5655. // trigger state-management and onunload callback
  5656. unload();
  5657. // clear the Instance of everything except for container & options (so could recreate)
  5658. // RE-CREATE: myLayout = myLayout.container.layout( myLayout.options );
  5659. for (var n in Instance)
  5660. if (!n.match(/^(container|options)$/)) delete Instance[ n ];
  5661. // add a 'destroyed' flag to make it easy to check
  5662. Instance.destroyed = true;
  5663. // if this is a child layout, CLEAR the child-pointer in the parent
  5664. /* for now the pointer REMAINS, but with only container, options and destroyed keys
  5665. if (parentPane) {
  5666. var layout = parentPane.pane.data("parentLayout")
  5667. , key = layout.options.instanceKey || 'error';
  5668. // THIS SYNTAX MAY BE WRONG!
  5669. parentPane.children[key] = layout.children[ parentPane.name ].children[key] = null;
  5670. }
  5671. */
  5672. return Instance; // for coding convenience
  5673. }
  5674. /**
  5675. * Remove a pane from the layout - subroutine of destroy()
  5676. *
  5677. * @see destroy()
  5678. * @param {(string|Object)} evt_or_pane The pane to process
  5679. * @param {boolean=} [remove=false] Remove the DOM element?
  5680. * @param {boolean=} [skipResize=false] Skip calling resizeAll()?
  5681. * @param {boolean=} [destroyChild=true] Destroy Child-layouts? If not passed, obeys options setting
  5682. */
  5683. , removePane = function (evt_or_pane, remove, skipResize, destroyChild) {
  5684. if (!isInitialized()) return;
  5685. var pane = evtPane.call(this, evt_or_pane)
  5686. , $P = $Ps[pane]
  5687. , $C = $Cs[pane]
  5688. , $R = $Rs[pane]
  5689. , $T = $Ts[pane]
  5690. ;
  5691. // NOTE: elements can still exist even after remove()
  5692. // so check for missing data(), which is cleared by removed()
  5693. if ($P && $.isEmptyObject( $P.data() )) $P = false;
  5694. if ($C && $.isEmptyObject( $C.data() )) $C = false;
  5695. if ($R && $.isEmptyObject( $R.data() )) $R = false;
  5696. if ($T && $.isEmptyObject( $T.data() )) $T = false;
  5697. if ($P) $P.stop(true, true);
  5698. var o = options[pane]
  5699. , s = state[pane]
  5700. , d = "layout"
  5701. , css = "layoutCSS"
  5702. , pC = children[pane]
  5703. , hasChildren = $.isPlainObject( pC ) && !$.isEmptyObject( pC )
  5704. , destroy = destroyChild !== undefined ? destroyChild : o.destroyChildren
  5705. ;
  5706. // FIRST destroy the child-layout(s)
  5707. if (hasChildren && destroy) {
  5708. $.each( pC, function (key, child) {
  5709. if (!child.destroyed)
  5710. child.destroy(true);// tell child-layout to destroy ALL its child-layouts too
  5711. if (child.destroyed) // destroy was successful
  5712. delete pC[key];
  5713. });
  5714. // if no more children, remove the children hash
  5715. if ($.isEmptyObject( pC )) {
  5716. pC = children[pane] = null; // clear children hash
  5717. hasChildren = false;
  5718. }
  5719. }
  5720. // Note: can't 'remove' a pane element with non-destroyed children
  5721. if ($P && remove && !hasChildren)
  5722. $P.remove(); // remove the pane-element and everything inside it
  5723. else if ($P && $P[0]) {
  5724. // create list of ALL pane-classes that need to be removed
  5725. var root = o.paneClass // default="ui-layout-pane"
  5726. , pRoot = root +"-"+ pane // eg: "ui-layout-pane-west"
  5727. , _open = "-open"
  5728. , _sliding= "-sliding"
  5729. , _closed = "-closed"
  5730. , classes = [ root, root+_open, root+_closed, root+_sliding, // generic classes
  5731. pRoot, pRoot+_open, pRoot+_closed, pRoot+_sliding ] // pane-specific classes
  5732. ;
  5733. $.merge(classes, getHoverClasses($P, true)); // ADD hover-classes
  5734. // remove all Layout classes from pane-element
  5735. $P .removeClass( classes.join(" ") ) // remove ALL pane-classes
  5736. .removeData("parentLayout")
  5737. .removeData("layoutPane")
  5738. .removeData("layoutRole")
  5739. .removeData("layoutEdge")
  5740. .removeData("autoHidden") // in case set
  5741. .unbind("."+ sID) // remove ALL Layout events
  5742. // TODO: remove these extra unbind commands when jQuery is fixed
  5743. //.unbind("mouseenter"+ sID)
  5744. //.unbind("mouseleave"+ sID)
  5745. ;
  5746. // do NOT reset CSS if this pane/content is STILL the container of a nested layout!
  5747. // the nested layout will reset its 'container' CSS when/if it is destroyed
  5748. if (hasChildren && $C) {
  5749. // a content-div may not have a specific width, so give it one to contain the Layout
  5750. $C.width( $C.width() );
  5751. $.each( pC, function (key, child) {
  5752. child.resizeAll(); // resize the Layout
  5753. });
  5754. }
  5755. else if ($C)
  5756. $C.css( $C.data(css) ).removeData(css).removeData("layoutRole");
  5757. // remove pane AFTER content in case there was a nested layout
  5758. if (!$P.data(d))
  5759. $P.css( $P.data(css) ).removeData(css);
  5760. }
  5761. // REMOVE pane resizer and toggler elements
  5762. if ($T) $T.remove();
  5763. if ($R) $R.remove();
  5764. // CLEAR all pointers and state data
  5765. Instance[pane] = $Ps[pane] = $Cs[pane] = $Rs[pane] = $Ts[pane] = false;
  5766. s = { removed: true };
  5767. if (!skipResize)
  5768. resizeAll();
  5769. }
  5770. /*
  5771. * ###########################
  5772. * ACTION METHODS
  5773. * ###########################
  5774. */
  5775. /**
  5776. * @param {string} pane
  5777. */
  5778. , _hidePane = function (pane) {
  5779. var $P = $Ps[pane]
  5780. , o = options[pane]
  5781. , s = $P[0].style
  5782. ;
  5783. if (o.useOffscreenClose) {
  5784. if (!$P.data(_c.offscreenReset))
  5785. $P.data(_c.offscreenReset, { left: s.left, right: s.right });
  5786. $P.css( _c.offscreenCSS );
  5787. }
  5788. else
  5789. $P.hide().removeData(_c.offscreenReset);
  5790. }
  5791. /**
  5792. * @param {string} pane
  5793. */
  5794. , _showPane = function (pane) {
  5795. var $P = $Ps[pane]
  5796. , o = options[pane]
  5797. , off = _c.offscreenCSS
  5798. , old = $P.data(_c.offscreenReset)
  5799. , s = $P[0].style
  5800. ;
  5801. $P .show() // ALWAYS show, just in case
  5802. .removeData(_c.offscreenReset);
  5803. if (o.useOffscreenClose && old) {
  5804. if (s.left == off.left)
  5805. s.left = old.left;
  5806. if (s.right == off.right)
  5807. s.right = old.right;
  5808. }
  5809. }
  5810. /**
  5811. * Completely 'hides' a pane, including its spacing - as if it does not exist
  5812. * The pane is not actually 'removed' from the source, so can use 'show' to un-hide it
  5813. *
  5814. * @param {(string|Object)} evt_or_pane The pane being hidden, ie: north, south, east, or west
  5815. * @param {boolean=} [noAnimation=false]
  5816. */
  5817. , hide = function (evt_or_pane, noAnimation) {
  5818. if (!isInitialized()) return;
  5819. var pane = evtPane.call(this, evt_or_pane)
  5820. , o = options[pane]
  5821. , s = state[pane]
  5822. , $P = $Ps[pane]
  5823. , $R = $Rs[pane]
  5824. ;
  5825. if (!$P || s.isHidden) return; // pane does not exist OR is already hidden
  5826. // onhide_start callback - will CANCEL hide if returns false
  5827. if (state.initialized && false === _runCallbacks("onhide_start", pane)) return;
  5828. s.isSliding = false; // just in case
  5829. delete state.panesSliding[pane];
  5830. // now hide the elements
  5831. if ($R) $R.hide(); // hide resizer-bar
  5832. if (!state.initialized || s.isClosed) {
  5833. s.isClosed = true; // to trigger open-animation on show()
  5834. s.isHidden = true;
  5835. s.isVisible = false;
  5836. if (!state.initialized)
  5837. _hidePane(pane); // no animation when loading page
  5838. sizeMidPanes(_c[pane].dir === "horz" ? "" : "center");
  5839. if (state.initialized || o.triggerEventsOnLoad)
  5840. _runCallbacks("onhide_end", pane);
  5841. }
  5842. else {
  5843. s.isHiding = true; // used by onclose
  5844. close(pane, false, noAnimation); // adjust all panes to fit
  5845. }
  5846. }
  5847. /**
  5848. * Show a hidden pane - show as 'closed' by default unless openPane = true
  5849. *
  5850. * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west
  5851. * @param {boolean=} [openPane=false]
  5852. * @param {boolean=} [noAnimation=false]
  5853. * @param {boolean=} [noAlert=false]
  5854. */
  5855. , show = function (evt_or_pane, openPane, noAnimation, noAlert) {
  5856. if (!isInitialized()) return;
  5857. var pane = evtPane.call(this, evt_or_pane)
  5858. , o = options[pane]
  5859. , s = state[pane]
  5860. , $P = $Ps[pane]
  5861. , $R = $Rs[pane]
  5862. ;
  5863. if (!$P || !s.isHidden) return; // pane does not exist OR is not hidden
  5864. // onshow_start callback - will CANCEL show if returns false
  5865. if (false === _runCallbacks("onshow_start", pane)) return;
  5866. s.isShowing = true; // used by onopen/onclose
  5867. //s.isHidden = false; - will be set by open/close - if not cancelled
  5868. s.isSliding = false; // just in case
  5869. delete state.panesSliding[pane];
  5870. // now show the elements
  5871. //if ($R) $R.show(); - will be shown by open/close
  5872. if (openPane === false)
  5873. close(pane, true); // true = force
  5874. else
  5875. open(pane, false, noAnimation, noAlert); // adjust all panes to fit
  5876. }
  5877. /**
  5878. * Toggles a pane open/closed by calling either open or close
  5879. *
  5880. * @param {(string|Object)} evt_or_pane The pane being toggled, ie: north, south, east, or west
  5881. * @param {boolean=} [slide=false]
  5882. */
  5883. , toggle = function (evt_or_pane, slide) {
  5884. if (!isInitialized()) return;
  5885. var evt = evtObj(evt_or_pane)
  5886. , pane = evtPane.call(this, evt_or_pane)
  5887. , s = state[pane]
  5888. ;
  5889. if (evt) // called from to $R.dblclick OR triggerPaneEvent
  5890. evt.stopImmediatePropagation();
  5891. if (s.isHidden)
  5892. show(pane); // will call 'open' after unhiding it
  5893. else if (s.isClosed)
  5894. open(pane, !!slide);
  5895. else
  5896. close(pane);
  5897. }
  5898. /**
  5899. * Utility method used during init or other auto-processes
  5900. *
  5901. * @param {string} pane The pane being closed
  5902. * @param {boolean=} [setHandles=false]
  5903. */
  5904. , _closePane = function (pane, setHandles) {
  5905. var
  5906. $P = $Ps[pane]
  5907. , s = state[pane]
  5908. ;
  5909. _hidePane(pane);
  5910. s.isClosed = true;
  5911. s.isVisible = false;
  5912. if (setHandles) setAsClosed(pane);
  5913. }
  5914. /**
  5915. * Close the specified pane (animation optional), and resize all other panes as needed
  5916. *
  5917. * @param {(string|Object)} evt_or_pane The pane being closed, ie: north, south, east, or west
  5918. * @param {boolean=} [force=false]
  5919. * @param {boolean=} [noAnimation=false]
  5920. * @param {boolean=} [skipCallback=false]
  5921. */
  5922. , close = function (evt_or_pane, force, noAnimation, skipCallback) {
  5923. var pane = evtPane.call(this, evt_or_pane);
  5924. // if pane has been initialized, but NOT the complete layout, close pane instantly
  5925. if (!state.initialized && $Ps[pane]) {
  5926. _closePane(pane, true); // INIT pane as closed
  5927. return;
  5928. }
  5929. if (!isInitialized()) return;
  5930. var
  5931. $P = $Ps[pane]
  5932. , $R = $Rs[pane]
  5933. , $T = $Ts[pane]
  5934. , o = options[pane]
  5935. , s = state[pane]
  5936. , c = _c[pane]
  5937. , doFX, isShowing, isHiding, wasSliding;
  5938. // QUEUE in case another action/animation is in progress
  5939. $N.queue(function( queueNext ){
  5940. if ( !$P
  5941. || (!o.closable && !s.isShowing && !s.isHiding) // invalid request // (!o.resizable && !o.closable) ???
  5942. || (!force && s.isClosed && !s.isShowing) // already closed
  5943. ) return queueNext();
  5944. // onclose_start callback - will CANCEL hide if returns false
  5945. // SKIP if just 'showing' a hidden pane as 'closed'
  5946. var abort = !s.isShowing && false === _runCallbacks("onclose_start", pane);
  5947. // transfer logic vars to temp vars
  5948. isShowing = s.isShowing;
  5949. isHiding = s.isHiding;
  5950. wasSliding = s.isSliding;
  5951. // now clear the logic vars (REQUIRED before aborting)
  5952. delete s.isShowing;
  5953. delete s.isHiding;
  5954. if (abort) return queueNext();
  5955. doFX = !noAnimation && !s.isClosed && (o.fxName_close != "none");
  5956. s.isMoving = true;
  5957. s.isClosed = true;
  5958. s.isVisible = false;
  5959. // update isHidden BEFORE sizing panes
  5960. if (isHiding) s.isHidden = true;
  5961. else if (isShowing) s.isHidden = false;
  5962. if (s.isSliding) // pane is being closed, so UNBIND trigger events
  5963. bindStopSlidingEvents(pane, false); // will set isSliding=false
  5964. else // resize panes adjacent to this one
  5965. sizeMidPanes(_c[pane].dir === "horz" ? "" : "center", false); // false = NOT skipCallback
  5966. // if this pane has a resizer bar, move it NOW - before animation
  5967. setAsClosed(pane);
  5968. // CLOSE THE PANE
  5969. if (doFX) { // animate the close
  5970. lockPaneForFX(pane, true); // need to set left/top so animation will work
  5971. $P.hide( o.fxName_close, o.fxSettings_close, o.fxSpeed_close, function () {
  5972. lockPaneForFX(pane, false); // undo
  5973. if (s.isClosed) close_2();
  5974. queueNext();
  5975. });
  5976. }
  5977. else { // hide the pane without animation
  5978. _hidePane(pane);
  5979. close_2();
  5980. queueNext();
  5981. };
  5982. });
  5983. // SUBROUTINE
  5984. function close_2 () {
  5985. s.isMoving = false;
  5986. bindStartSlidingEvents(pane, true); // will enable if o.slidable = true
  5987. // if opposite-pane was autoClosed, see if it can be autoOpened now
  5988. var altPane = _c.oppositeEdge[pane];
  5989. if (state[ altPane ].noRoom) {
  5990. setSizeLimits( altPane );
  5991. makePaneFit( altPane );
  5992. }
  5993. if (!skipCallback && (state.initialized || o.triggerEventsOnLoad)) {
  5994. // onclose callback - UNLESS just 'showing' a hidden pane as 'closed'
  5995. if (!isShowing) _runCallbacks("onclose_end", pane);
  5996. // onhide OR onshow callback
  5997. if (isShowing) _runCallbacks("onshow_end", pane);
  5998. if (isHiding) _runCallbacks("onhide_end", pane);
  5999. }
  6000. }
  6001. }
  6002. /**
  6003. * @param {string} pane The pane just closed, ie: north, south, east, or west
  6004. */
  6005. , setAsClosed = function (pane) {
  6006. if (!$Rs[pane]) return; // handles not initialized yet!
  6007. var
  6008. $P = $Ps[pane]
  6009. , $R = $Rs[pane]
  6010. , $T = $Ts[pane]
  6011. , o = options[pane]
  6012. , s = state[pane]
  6013. , side = _c[pane].side
  6014. , rClass = o.resizerClass
  6015. , tClass = o.togglerClass
  6016. , _pane = "-"+ pane // used for classNames
  6017. , _open = "-open"
  6018. , _sliding= "-sliding"
  6019. , _closed = "-closed"
  6020. ;
  6021. $R
  6022. .css(side, sC.inset[side]) // move the resizer
  6023. .removeClass( rClass+_open +" "+ rClass+_pane+_open )
  6024. .removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
  6025. .addClass( rClass+_closed +" "+ rClass+_pane+_closed )
  6026. ;
  6027. // DISABLE 'resizing' when closed - do this BEFORE bindStartSlidingEvents?
  6028. if (o.resizable && $.layout.plugins.draggable)
  6029. $R
  6030. .draggable("disable")
  6031. .removeClass("ui-state-disabled") // do NOT apply disabled styling - not suitable here
  6032. .css("cursor", "default")
  6033. .attr("title","")
  6034. ;
  6035. // if pane has a toggler button, adjust that too
  6036. if ($T) {
  6037. $T
  6038. .removeClass( tClass+_open +" "+ tClass+_pane+_open )
  6039. .addClass( tClass+_closed +" "+ tClass+_pane+_closed )
  6040. .attr("title", o.tips.Open) // may be blank
  6041. ;
  6042. // toggler-content - if exists
  6043. $T.children(".content-open").hide();
  6044. $T.children(".content-closed").css("display","block");
  6045. }
  6046. // sync any 'pin buttons'
  6047. syncPinBtns(pane, false);
  6048. if (state.initialized) {
  6049. // resize 'length' and position togglers for adjacent panes
  6050. sizeHandles();
  6051. }
  6052. }
  6053. /**
  6054. * Open the specified pane (animation optional), and resize all other panes as needed
  6055. *
  6056. * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west
  6057. * @param {boolean=} [slide=false]
  6058. * @param {boolean=} [noAnimation=false]
  6059. * @param {boolean=} [noAlert=false]
  6060. */
  6061. , open = function (evt_or_pane, slide, noAnimation, noAlert) {
  6062. if (!isInitialized()) return;
  6063. var pane = evtPane.call(this, evt_or_pane)
  6064. , $P = $Ps[pane]
  6065. , $R = $Rs[pane]
  6066. , $T = $Ts[pane]
  6067. , o = options[pane]
  6068. , s = state[pane]
  6069. , c = _c[pane]
  6070. , doFX, isShowing
  6071. ;
  6072. // QUEUE in case another action/animation is in progress
  6073. $N.queue(function( queueNext ){
  6074. if ( !$P
  6075. || (!o.resizable && !o.closable && !s.isShowing) // invalid request
  6076. || (s.isVisible && !s.isSliding) // already open
  6077. ) return queueNext();
  6078. // pane can ALSO be unhidden by just calling show(), so handle this scenario
  6079. if (s.isHidden && !s.isShowing) {
  6080. queueNext(); // call before show() because it needs the queue free
  6081. show(pane, true);
  6082. return;
  6083. }
  6084. if (s.autoResize && s.size != o.size) // resize pane to original size set in options
  6085. sizePane(pane, o.size, true, true, true); // true=skipCallback/noAnimation/forceResize
  6086. else
  6087. // make sure there is enough space available to open the pane
  6088. setSizeLimits(pane, slide);
  6089. // onopen_start callback - will CANCEL open if returns false
  6090. var cbReturn = _runCallbacks("onopen_start", pane);
  6091. if (cbReturn === "abort")
  6092. return queueNext();
  6093. // update pane-state again in case options were changed in onopen_start
  6094. if (cbReturn !== "NC") // NC = "No Callback"
  6095. setSizeLimits(pane, slide);
  6096. if (s.minSize > s.maxSize) { // INSUFFICIENT ROOM FOR PANE TO OPEN!
  6097. syncPinBtns(pane, false); // make sure pin-buttons are reset
  6098. if (!noAlert && o.tips.noRoomToOpen)
  6099. alert(o.tips.noRoomToOpen);
  6100. return queueNext(); // ABORT
  6101. }
  6102. if (slide) // START Sliding - will set isSliding=true
  6103. bindStopSlidingEvents(pane, true); // BIND trigger events to close sliding-pane
  6104. else if (s.isSliding) // PIN PANE (stop sliding) - open pane 'normally' instead
  6105. bindStopSlidingEvents(pane, false); // UNBIND trigger events - will set isSliding=false
  6106. else if (o.slidable)
  6107. bindStartSlidingEvents(pane, false); // UNBIND trigger events
  6108. s.noRoom = false; // will be reset by makePaneFit if 'noRoom'
  6109. makePaneFit(pane);
  6110. // transfer logic var to temp var
  6111. isShowing = s.isShowing;
  6112. // now clear the logic var
  6113. delete s.isShowing;
  6114. doFX = !noAnimation && s.isClosed && (o.fxName_open != "none");
  6115. s.isMoving = true;
  6116. s.isVisible = true;
  6117. s.isClosed = false;
  6118. // update isHidden BEFORE sizing panes - WHY??? Old?
  6119. if (isShowing) s.isHidden = false;
  6120. if (doFX) { // ANIMATE
  6121. // mask adjacent panes with objects
  6122. lockPaneForFX(pane, true); // need to set left/top so animation will work
  6123. $P.show( o.fxName_open, o.fxSettings_open, o.fxSpeed_open, function() {
  6124. lockPaneForFX(pane, false); // undo
  6125. if (s.isVisible) open_2(); // continue
  6126. queueNext();
  6127. });
  6128. }
  6129. else { // no animation
  6130. _showPane(pane);// just show pane and...
  6131. open_2(); // continue
  6132. queueNext();
  6133. };
  6134. });
  6135. // SUBROUTINE
  6136. function open_2 () {
  6137. s.isMoving = false;
  6138. // cure iframe display issues
  6139. _fixIframe(pane);
  6140. // NOTE: if isSliding, then other panes are NOT 'resized'
  6141. if (!s.isSliding) { // resize all panes adjacent to this one
  6142. sizeMidPanes(_c[pane].dir=="vert" ? "center" : "", false); // false = NOT skipCallback
  6143. }
  6144. // set classes, position handles and execute callbacks...
  6145. setAsOpen(pane);
  6146. };
  6147. }
  6148. /**
  6149. * @param {string} pane The pane just opened, ie: north, south, east, or west
  6150. * @param {boolean=} [skipCallback=false]
  6151. */
  6152. , setAsOpen = function (pane, skipCallback) {
  6153. var
  6154. $P = $Ps[pane]
  6155. , $R = $Rs[pane]
  6156. , $T = $Ts[pane]
  6157. , o = options[pane]
  6158. , s = state[pane]
  6159. , side = _c[pane].side
  6160. , rClass = o.resizerClass
  6161. , tClass = o.togglerClass
  6162. , _pane = "-"+ pane // used for classNames
  6163. , _open = "-open"
  6164. , _closed = "-closed"
  6165. , _sliding= "-sliding"
  6166. ;
  6167. $R
  6168. .css(side, sC.inset[side] + getPaneSize(pane)) // move the resizer
  6169. .removeClass( rClass+_closed +" "+ rClass+_pane+_closed )
  6170. .addClass( rClass+_open +" "+ rClass+_pane+_open )
  6171. ;
  6172. if (s.isSliding)
  6173. $R.addClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
  6174. else // in case 'was sliding'
  6175. $R.removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
  6176. removeHover( 0, $R ); // remove hover classes
  6177. if (o.resizable && $.layout.plugins.draggable)
  6178. $R .draggable("enable")
  6179. .css("cursor", o.resizerCursor)
  6180. .attr("title", o.tips.Resize);
  6181. else if (!s.isSliding)
  6182. $R.css("cursor", "default"); // n-resize, s-resize, etc
  6183. // if pane also has a toggler button, adjust that too
  6184. if ($T) {
  6185. $T .removeClass( tClass+_closed +" "+ tClass+_pane+_closed )
  6186. .addClass( tClass+_open +" "+ tClass+_pane+_open )
  6187. .attr("title", o.tips.Close); // may be blank
  6188. removeHover( 0, $T ); // remove hover classes
  6189. // toggler-content - if exists
  6190. $T.children(".content-closed").hide();
  6191. $T.children(".content-open").css("display","block");
  6192. }
  6193. // sync any 'pin buttons'
  6194. syncPinBtns(pane, !s.isSliding);
  6195. // update pane-state dimensions - BEFORE resizing content
  6196. $.extend(s, elDims($P));
  6197. if (state.initialized) {
  6198. // resize resizer & toggler sizes for all panes
  6199. sizeHandles();
  6200. // resize content every time pane opens - to be sure
  6201. sizeContent(pane, true); // true = remeasure headers/footers, even if 'pane.isMoving'
  6202. }
  6203. if (!skipCallback && (state.initialized || o.triggerEventsOnLoad) && $P.is(":visible")) {
  6204. // onopen callback
  6205. _runCallbacks("onopen_end", pane);
  6206. // onshow callback - TODO: should this be here?
  6207. if (s.isShowing) _runCallbacks("onshow_end", pane);
  6208. // ALSO call onresize because layout-size *may* have changed while pane was closed
  6209. if (state.initialized)
  6210. _runCallbacks("onresize_end", pane);
  6211. }
  6212. // TODO: Somehow sizePane("north") is being called after this point???
  6213. }
  6214. /**
  6215. * slideOpen / slideClose / slideToggle
  6216. *
  6217. * Pass-though methods for sliding
  6218. */
  6219. , slideOpen = function (evt_or_pane) {
  6220. if (!isInitialized()) return;
  6221. var evt = evtObj(evt_or_pane)
  6222. , pane = evtPane.call(this, evt_or_pane)
  6223. , s = state[pane]
  6224. , delay = options[pane].slideDelay_open
  6225. ;
  6226. // prevent event from triggering on NEW resizer binding created below
  6227. if (evt) evt.stopImmediatePropagation();
  6228. if (s.isClosed && evt && evt.type === "mouseenter" && delay > 0)
  6229. // trigger = mouseenter - use a delay
  6230. timer.set(pane+"_openSlider", open_NOW, delay);
  6231. else
  6232. open_NOW(); // will unbind events if is already open
  6233. /**
  6234. * SUBROUTINE for timed open
  6235. */
  6236. function open_NOW () {
  6237. if (!s.isClosed) // skip if no longer closed!
  6238. bindStopSlidingEvents(pane, true); // BIND trigger events to close sliding-pane
  6239. else if (!s.isMoving)
  6240. open(pane, true); // true = slide - open() will handle binding
  6241. };
  6242. }
  6243. , slideClose = function (evt_or_pane) {
  6244. if (!isInitialized()) return;
  6245. var evt = evtObj(evt_or_pane)
  6246. , pane = evtPane.call(this, evt_or_pane)
  6247. , o = options[pane]
  6248. , s = state[pane]
  6249. , delay = s.isMoving ? 1000 : 300 // MINIMUM delay - option may override
  6250. ;
  6251. if (s.isClosed || s.isResizing)
  6252. return; // skip if already closed OR in process of resizing
  6253. else if (o.slideTrigger_close === "click")
  6254. close_NOW(); // close immediately onClick
  6255. else if (o.preventQuickSlideClose && s.isMoving)
  6256. return; // handle Chrome quick-close on slide-open
  6257. else if (o.preventPrematureSlideClose && evt && $.layout.isMouseOverElem(evt, $Ps[pane]))
  6258. return; // handle incorrect mouseleave trigger, like when over a SELECT-list in IE
  6259. else if (evt) // trigger = mouseleave - use a delay
  6260. // 1 sec delay if 'opening', else .3 sec
  6261. timer.set(pane+"_closeSlider", close_NOW, max(o.slideDelay_close, delay));
  6262. else // called programically
  6263. close_NOW();
  6264. /**
  6265. * SUBROUTINE for timed close
  6266. */
  6267. function close_NOW () {
  6268. if (s.isClosed) // skip 'close' if already closed!
  6269. bindStopSlidingEvents(pane, false); // UNBIND trigger events - TODO: is this needed here?
  6270. else if (!s.isMoving)
  6271. close(pane); // close will handle unbinding
  6272. };
  6273. }
  6274. /**
  6275. * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west
  6276. */
  6277. , slideToggle = function (evt_or_pane) {
  6278. var pane = evtPane.call(this, evt_or_pane);
  6279. toggle(pane, true);
  6280. }
  6281. /**
  6282. * Must set left/top on East/South panes so animation will work properly
  6283. *
  6284. * @param {string} pane The pane to lock, 'east' or 'south' - any other is ignored!
  6285. * @param {boolean} doLock true = set left/top, false = remove
  6286. */
  6287. , lockPaneForFX = function (pane, doLock) {
  6288. var $P = $Ps[pane]
  6289. , s = state[pane]
  6290. , o = options[pane]
  6291. , z = options.zIndexes
  6292. ;
  6293. if (doLock) {
  6294. showMasks( pane, { animation: true, objectsOnly: true });
  6295. $P.css({ zIndex: z.pane_animate }); // overlay all elements during animation
  6296. if (pane=="south")
  6297. $P.css({ top: sC.inset.top + sC.innerHeight - $P.outerHeight() });
  6298. else if (pane=="east")
  6299. $P.css({ left: sC.inset.left + sC.innerWidth - $P.outerWidth() });
  6300. }
  6301. else { // animation DONE - RESET CSS
  6302. hideMasks();
  6303. $P.css({ zIndex: (s.isSliding ? z.pane_sliding : z.pane_normal) });
  6304. if (pane=="south")
  6305. $P.css({ top: "auto" });
  6306. // if pane is positioned 'off-screen', then DO NOT screw with it!
  6307. else if (pane=="east" && !$P.css("left").match(/\-99999/))
  6308. $P.css({ left: "auto" });
  6309. // fix anti-aliasing in IE - only needed for animations that change opacity
  6310. if (browser.msie && o.fxOpacityFix && o.fxName_open != "slide" && $P.css("filter") && $P.css("opacity") == 1)
  6311. $P[0].style.removeAttribute('filter');
  6312. }
  6313. }
  6314. /**
  6315. * Toggle sliding functionality of a specific pane on/off by adding removing 'slide open' trigger
  6316. *
  6317. * @see open(), close()
  6318. * @param {string} pane The pane to enable/disable, 'north', 'south', etc.
  6319. * @param {boolean} enable Enable or Disable sliding?
  6320. */
  6321. , bindStartSlidingEvents = function (pane, enable) {
  6322. var o = options[pane]
  6323. , $P = $Ps[pane]
  6324. , $R = $Rs[pane]
  6325. , evtName = o.slideTrigger_open.toLowerCase()
  6326. ;
  6327. if (!$R || (enable && !o.slidable)) return;
  6328. // make sure we have a valid event
  6329. if (evtName.match(/mouseover/))
  6330. evtName = o.slideTrigger_open = "mouseenter";
  6331. else if (!evtName.match(/(click|dblclick|mouseenter)/))
  6332. evtName = o.slideTrigger_open = "click";
  6333. // must remove double-click-toggle when using dblclick-slide
  6334. if (o.resizerDblClickToggle && evtName.match(/click/)) {
  6335. $R[enable ? "unbind" : "bind"]('dblclick.'+ sID, toggle)
  6336. }
  6337. $R
  6338. // add or remove event
  6339. [enable ? "bind" : "unbind"](evtName +'.'+ sID, slideOpen)
  6340. // set the appropriate cursor & title/tip
  6341. .css("cursor", enable ? o.sliderCursor : "default")
  6342. .attr("title", enable ? o.tips.Slide : "")
  6343. ;
  6344. }
  6345. /**
  6346. * Add or remove 'mouseleave' events to 'slide close' when pane is 'sliding' open or closed
  6347. * Also increases zIndex when pane is sliding open
  6348. * See bindStartSlidingEvents for code to control 'slide open'
  6349. *
  6350. * @see slideOpen(), slideClose()
  6351. * @param {string} pane The pane to process, 'north', 'south', etc.
  6352. * @param {boolean} enable Enable or Disable events?
  6353. */
  6354. , bindStopSlidingEvents = function (pane, enable) {
  6355. var o = options[pane]
  6356. , s = state[pane]
  6357. , c = _c[pane]
  6358. , z = options.zIndexes
  6359. , evtName = o.slideTrigger_close.toLowerCase()
  6360. , action = (enable ? "bind" : "unbind")
  6361. , $P = $Ps[pane]
  6362. , $R = $Rs[pane]
  6363. ;
  6364. timer.clear(pane+"_closeSlider"); // just in case
  6365. if (enable) {
  6366. s.isSliding = true;
  6367. state.panesSliding[pane] = true;
  6368. // remove 'slideOpen' event from resizer
  6369. // ALSO will raise the zIndex of the pane & resizer
  6370. bindStartSlidingEvents(pane, false);
  6371. }
  6372. else {
  6373. s.isSliding = false;
  6374. delete state.panesSliding[pane];
  6375. }
  6376. // RE/SET zIndex - increases when pane is sliding-open, resets to normal when not
  6377. $P.css("zIndex", enable ? z.pane_sliding : z.pane_normal);
  6378. $R.css("zIndex", enable ? z.pane_sliding+2 : z.resizer_normal); // NOTE: mask = pane_sliding+1
  6379. // make sure we have a valid event
  6380. if (!evtName.match(/(click|mouseleave)/))
  6381. evtName = o.slideTrigger_close = "mouseleave"; // also catches 'mouseout'
  6382. // add/remove slide triggers
  6383. $R[action](evtName, slideClose); // base event on resize
  6384. // need extra events for mouseleave
  6385. if (evtName === "mouseleave") {
  6386. // also close on pane.mouseleave
  6387. $P[action]("mouseleave."+ sID, slideClose);
  6388. // cancel timer when mouse moves between 'pane' and 'resizer'
  6389. $R[action]("mouseenter."+ sID, cancelMouseOut);
  6390. $P[action]("mouseenter."+ sID, cancelMouseOut);
  6391. }
  6392. if (!enable)
  6393. timer.clear(pane+"_closeSlider");
  6394. else if (evtName === "click" && !o.resizable) {
  6395. // IF pane is not resizable (which already has a cursor and tip)
  6396. // then set the a cursor & title/tip on resizer when sliding
  6397. $R.css("cursor", enable ? o.sliderCursor : "default");
  6398. $R.attr("title", enable ? o.tips.Close : ""); // use Toggler-tip, eg: "Close Pane"
  6399. }
  6400. // SUBROUTINE for mouseleave timer clearing
  6401. function cancelMouseOut (evt) {
  6402. timer.clear(pane+"_closeSlider");
  6403. evt.stopPropagation();
  6404. }
  6405. }
  6406. /**
  6407. * Hides/closes a pane if there is insufficient room - reverses this when there is room again
  6408. * MUST have already called setSizeLimits() before calling this method
  6409. *
  6410. * @param {string} pane The pane being resized
  6411. * @param {boolean=} [isOpening=false] Called from onOpen?
  6412. * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
  6413. * @param {boolean=} [force=false]
  6414. */
  6415. , makePaneFit = function (pane, isOpening, skipCallback, force) {
  6416. var o = options[pane]
  6417. , s = state[pane]
  6418. , c = _c[pane]
  6419. , $P = $Ps[pane]
  6420. , $R = $Rs[pane]
  6421. , isSidePane = c.dir==="vert"
  6422. , hasRoom = false
  6423. ;
  6424. // special handling for center & east/west panes
  6425. if (pane === "center" || (isSidePane && s.noVerticalRoom)) {
  6426. // see if there is enough room to display the pane
  6427. // ERROR: hasRoom = s.minHeight <= s.maxHeight && (isSidePane || s.minWidth <= s.maxWidth);
  6428. hasRoom = (s.maxHeight >= 0);
  6429. if (hasRoom && s.noRoom) { // previously hidden due to noRoom, so show now
  6430. _showPane(pane);
  6431. if ($R) $R.show();
  6432. s.isVisible = true;
  6433. s.noRoom = false;
  6434. if (isSidePane) s.noVerticalRoom = false;
  6435. _fixIframe(pane);
  6436. }
  6437. else if (!hasRoom && !s.noRoom) { // not currently hidden, so hide now
  6438. _hidePane(pane);
  6439. if ($R) $R.hide();
  6440. s.isVisible = false;
  6441. s.noRoom = true;
  6442. }
  6443. }
  6444. // see if there is enough room to fit the border-pane
  6445. if (pane === "center") {
  6446. // ignore center in this block
  6447. }
  6448. else if (s.minSize <= s.maxSize) { // pane CAN fit
  6449. hasRoom = true;
  6450. if (s.size > s.maxSize) // pane is too big - shrink it
  6451. sizePane(pane, s.maxSize, skipCallback, true, force); // true = noAnimation
  6452. else if (s.size < s.minSize) // pane is too small - enlarge it
  6453. sizePane(pane, s.minSize, skipCallback, true, force); // true = noAnimation
  6454. // need s.isVisible because new pseudoClose method keeps pane visible, but off-screen
  6455. else if ($R && s.isVisible && $P.is(":visible")) {
  6456. // make sure resizer-bar is positioned correctly
  6457. // handles situation where nested layout was 'hidden' when initialized
  6458. var pos = s.size + sC.inset[c.side];
  6459. if ($.layout.cssNum( $R, c.side ) != pos) $R.css( c.side, pos );
  6460. }
  6461. // if was previously hidden due to noRoom, then RESET because NOW there is room
  6462. if (s.noRoom) {
  6463. // s.noRoom state will be set by open or show
  6464. if (s.wasOpen && o.closable) {
  6465. if (o.autoReopen)
  6466. open(pane, false, true, true); // true = noAnimation, true = noAlert
  6467. else // leave the pane closed, so just update state
  6468. s.noRoom = false;
  6469. }
  6470. else
  6471. show(pane, s.wasOpen, true, true); // true = noAnimation, true = noAlert
  6472. }
  6473. }
  6474. else { // !hasRoom - pane CANNOT fit
  6475. if (!s.noRoom) { // pane not set as noRoom yet, so hide or close it now...
  6476. s.noRoom = true; // update state
  6477. s.wasOpen = !s.isClosed && !s.isSliding;
  6478. if (s.isClosed){} // SKIP
  6479. else if (o.closable) // 'close' if possible
  6480. close(pane, true, true); // true = force, true = noAnimation
  6481. else // 'hide' pane if cannot just be closed
  6482. hide(pane, true); // true = noAnimation
  6483. }
  6484. }
  6485. }
  6486. /**
  6487. * manualSizePane is an exposed flow-through method allowing extra code when pane is 'manually resized'
  6488. *
  6489. * @param {(string|Object)} evt_or_pane The pane being resized
  6490. * @param {number} size The *desired* new size for this pane - will be validated
  6491. * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
  6492. * @param {boolean=} [noAnimation=false]
  6493. * @param {boolean=} [force=false] Force resizing even if does not seem necessary
  6494. */
  6495. , manualSizePane = function (evt_or_pane, size, skipCallback, noAnimation, force) {
  6496. if (!isInitialized()) return;
  6497. var pane = evtPane.call(this, evt_or_pane)
  6498. , o = options[pane]
  6499. , s = state[pane]
  6500. // if resizing callbacks have been delayed and resizing is now DONE, force resizing to complete...
  6501. , forceResize = force || (o.livePaneResizing && !s.isResizing)
  6502. ;
  6503. // ANY call to manualSizePane disables autoResize - ie, percentage sizing
  6504. s.autoResize = false;
  6505. // flow-through...
  6506. sizePane(pane, size, skipCallback, noAnimation, forceResize); // will animate resize if option enabled
  6507. }
  6508. /**
  6509. * sizePane is called only by internal methods whenever a pane needs to be resized
  6510. *
  6511. * @param {(string|Object)} evt_or_pane The pane being resized
  6512. * @param {number} size The *desired* new size for this pane - will be validated
  6513. * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
  6514. * @param {boolean=} [noAnimation=false]
  6515. * @param {boolean=} [force=false] Force resizing even if does not seem necessary
  6516. */
  6517. , sizePane = function (evt_or_pane, size, skipCallback, noAnimation, force) {
  6518. if (!isInitialized()) return;
  6519. var pane = evtPane.call(this, evt_or_pane) // probably NEVER called from event?
  6520. , o = options[pane]
  6521. , s = state[pane]
  6522. , $P = $Ps[pane]
  6523. , $R = $Rs[pane]
  6524. , side = _c[pane].side
  6525. , dimName = _c[pane].sizeType.toLowerCase()
  6526. , skipResizeWhileDragging = s.isResizing && !o.triggerEventsDuringLiveResize
  6527. , doFX = noAnimation !== true && o.animatePaneSizing
  6528. , oldSize, newSize
  6529. ;
  6530. // QUEUE in case another action/animation is in progress
  6531. $N.queue(function( queueNext ){
  6532. // calculate 'current' min/max sizes
  6533. setSizeLimits(pane); // update pane-state
  6534. oldSize = s.size;
  6535. size = _parseSize(pane, size); // handle percentages & auto
  6536. size = max(size, _parseSize(pane, o.minSize));
  6537. size = min(size, s.maxSize);
  6538. if (size < s.minSize) { // not enough room for pane!
  6539. queueNext(); // call before makePaneFit() because it needs the queue free
  6540. makePaneFit(pane, false, skipCallback); // will hide or close pane
  6541. return;
  6542. }
  6543. // IF newSize is same as oldSize, then nothing to do - abort
  6544. if (!force && size === oldSize)
  6545. return queueNext();
  6546. s.newSize = size;
  6547. // onresize_start callback CANNOT cancel resizing because this would break the layout!
  6548. if (!skipCallback && state.initialized && s.isVisible)
  6549. _runCallbacks("onresize_start", pane);
  6550. // resize the pane, and make sure its visible
  6551. newSize = cssSize(pane, size);
  6552. if (doFX && $P.is(":visible")) { // ANIMATE
  6553. var fx = $.layout.effects.size[pane] || $.layout.effects.size.all
  6554. , easing = o.fxSettings_size.easing || fx.easing
  6555. , z = options.zIndexes
  6556. , props = {};
  6557. props[ dimName ] = newSize +'px';
  6558. s.isMoving = true;
  6559. // overlay all elements during animation
  6560. $P.css({ zIndex: z.pane_animate })
  6561. .show().animate( props, o.fxSpeed_size, easing, function(){
  6562. // reset zIndex after animation
  6563. $P.css({ zIndex: (s.isSliding ? z.pane_sliding : z.pane_normal) });
  6564. s.isMoving = false;
  6565. delete s.newSize;
  6566. sizePane_2(); // continue
  6567. queueNext();
  6568. });
  6569. }
  6570. else { // no animation
  6571. $P.css( dimName, newSize ); // resize pane
  6572. delete s.newSize;
  6573. // if pane is visible, then
  6574. if ($P.is(":visible"))
  6575. sizePane_2(); // continue
  6576. else {
  6577. // pane is NOT VISIBLE, so just update state data...
  6578. // when pane is *next opened*, it will have the new size
  6579. s.size = size; // update state.size
  6580. $.extend(s, elDims($P)); // update state dimensions
  6581. }
  6582. queueNext();
  6583. };
  6584. });
  6585. // SUBROUTINE
  6586. function sizePane_2 () {
  6587. /* Panes are sometimes not sized precisely in some browsers!?
  6588. * This code will resize the pane up to 3 times to nudge the pane to the correct size
  6589. */
  6590. var actual = dimName==='width' ? $P.outerWidth() : $P.outerHeight()
  6591. , tries = [{
  6592. pane: pane
  6593. , count: 1
  6594. , target: size
  6595. , actual: actual
  6596. , correct: (size === actual)
  6597. , attempt: size
  6598. , cssSize: newSize
  6599. }]
  6600. , lastTry = tries[0]
  6601. , thisTry = {}
  6602. , msg = 'Inaccurate size after resizing the '+ pane +'-pane.'
  6603. ;
  6604. while ( !lastTry.correct ) {
  6605. thisTry = { pane: pane, count: lastTry.count+1, target: size };
  6606. if (lastTry.actual > size)
  6607. thisTry.attempt = max(0, lastTry.attempt - (lastTry.actual - size));
  6608. else // lastTry.actual < size
  6609. thisTry.attempt = max(0, lastTry.attempt + (size - lastTry.actual));
  6610. thisTry.cssSize = cssSize(pane, thisTry.attempt);
  6611. $P.css( dimName, thisTry.cssSize );
  6612. thisTry.actual = dimName=='width' ? $P.outerWidth() : $P.outerHeight();
  6613. thisTry.correct = (size === thisTry.actual);
  6614. // log attempts and alert the user of this *non-fatal error* (if showDebugMessages)
  6615. if ( tries.length === 1) {
  6616. _log(msg, false, true);
  6617. _log(lastTry, false, true);
  6618. }
  6619. _log(thisTry, false, true);
  6620. // after 4 tries, is as close as its gonna get!
  6621. if (tries.length > 3) break;
  6622. tries.push( thisTry );
  6623. lastTry = tries[ tries.length - 1 ];
  6624. }
  6625. // END TESTING CODE
  6626. // update pane-state dimensions
  6627. s.size = size;
  6628. $.extend(s, elDims($P));
  6629. if (s.isVisible && $P.is(":visible")) {
  6630. // reposition the resizer-bar
  6631. if ($R) $R.css( side, size + sC.inset[side] );
  6632. // resize the content-div
  6633. sizeContent(pane);
  6634. }
  6635. if (!skipCallback && !skipResizeWhileDragging && state.initialized && s.isVisible)
  6636. _runCallbacks("onresize_end", pane);
  6637. // resize all the adjacent panes, and adjust their toggler buttons
  6638. // when skipCallback passed, it means the controlling method will handle 'other panes'
  6639. if (!skipCallback) {
  6640. // also no callback if live-resize is in progress and NOT triggerEventsDuringLiveResize
  6641. if (!s.isSliding) sizeMidPanes(_c[pane].dir=="horz" ? "" : "center", skipResizeWhileDragging, force);
  6642. sizeHandles();
  6643. }
  6644. // if opposite-pane was autoClosed, see if it can be autoOpened now
  6645. var altPane = _c.oppositeEdge[pane];
  6646. if (size < oldSize && state[ altPane ].noRoom) {
  6647. setSizeLimits( altPane );
  6648. makePaneFit( altPane, false, skipCallback );
  6649. }
  6650. // DEBUG - ALERT user/developer so they know there was a sizing problem
  6651. if (tries.length > 1)
  6652. _log(msg +'\nSee the Error Console for details.', true, true);
  6653. }
  6654. }
  6655. /**
  6656. * @see initPanes(), sizePane(), resizeAll(), open(), close(), hide()
  6657. * @param {(Array.<string>|string)} panes The pane(s) being resized, comma-delmited string
  6658. * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
  6659. * @param {boolean=} [force=false]
  6660. */
  6661. , sizeMidPanes = function (panes, skipCallback, force) {
  6662. panes = (panes ? panes : "east,west,center").split(",");
  6663. $.each(panes, function (i, pane) {
  6664. if (!$Ps[pane]) return; // NO PANE - skip
  6665. var
  6666. o = options[pane]
  6667. , s = state[pane]
  6668. , $P = $Ps[pane]
  6669. , $R = $Rs[pane]
  6670. , isCenter= (pane=="center")
  6671. , hasRoom = true
  6672. , CSS = {}
  6673. // if pane is not visible, show it invisibly NOW rather than for *each call* in this script
  6674. , visCSS = $.layout.showInvisibly($P)
  6675. , newCenter = calcNewCenterPaneDims()
  6676. ;
  6677. // update pane-state dimensions
  6678. $.extend(s, elDims($P));
  6679. if (pane === "center") {
  6680. if (!force && s.isVisible && newCenter.width === s.outerWidth && newCenter.height === s.outerHeight) {
  6681. $P.css(visCSS);
  6682. return true; // SKIP - pane already the correct size
  6683. }
  6684. // set state for makePaneFit() logic
  6685. $.extend(s, cssMinDims(pane), {
  6686. maxWidth: newCenter.width
  6687. , maxHeight: newCenter.height
  6688. });
  6689. CSS = newCenter;
  6690. s.newWidth = CSS.width;
  6691. s.newHeight = CSS.height;
  6692. // convert OUTER width/height to CSS width/height
  6693. CSS.width = cssW($P, CSS.width);
  6694. // NEW - allow pane to extend 'below' visible area rather than hide it
  6695. CSS.height = cssH($P, CSS.height);
  6696. hasRoom = CSS.width >= 0 && CSS.height >= 0; // height >= 0 = ALWAYS TRUE NOW
  6697. // during layout init, try to shrink east/west panes to make room for center
  6698. if (!state.initialized && o.minWidth > newCenter.width) {
  6699. var
  6700. reqPx = o.minWidth - s.outerWidth
  6701. , minE = options.east.minSize || 0
  6702. , minW = options.west.minSize || 0
  6703. , sizeE = state.east.size
  6704. , sizeW = state.west.size
  6705. , newE = sizeE
  6706. , newW = sizeW
  6707. ;
  6708. if (reqPx > 0 && state.east.isVisible && sizeE > minE) {
  6709. newE = max( sizeE-minE, sizeE-reqPx );
  6710. reqPx -= sizeE-newE;
  6711. }
  6712. if (reqPx > 0 && state.west.isVisible && sizeW > minW) {
  6713. newW = max( sizeW-minW, sizeW-reqPx );
  6714. reqPx -= sizeW-newW;
  6715. }
  6716. // IF we found enough extra space, then resize the border panes as calculated
  6717. if (reqPx === 0) {
  6718. if (sizeE && sizeE != minE)
  6719. sizePane('east', newE, true, true, force); // true = skipCallback/noAnimation - initPanes will handle when done
  6720. if (sizeW && sizeW != minW)
  6721. sizePane('west', newW, true, true, force); // true = skipCallback/noAnimation
  6722. // now start over!
  6723. sizeMidPanes('center', skipCallback, force);
  6724. $P.css(visCSS);
  6725. return; // abort this loop
  6726. }
  6727. }
  6728. }
  6729. else { // for east and west, set only the height, which is same as center height
  6730. // set state.min/maxWidth/Height for makePaneFit() logic
  6731. if (s.isVisible && !s.noVerticalRoom)
  6732. $.extend(s, elDims($P), cssMinDims(pane))
  6733. if (!force && !s.noVerticalRoom && newCenter.height === s.outerHeight) {
  6734. $P.css(visCSS);
  6735. return true; // SKIP - pane already the correct size
  6736. }
  6737. // east/west have same top, bottom & height as center
  6738. CSS.top = newCenter.top;
  6739. CSS.bottom = newCenter.bottom;
  6740. s.newSize = newCenter.height
  6741. // NEW - allow pane to extend 'below' visible area rather than hide it
  6742. CSS.height = cssH($P, newCenter.height);
  6743. s.maxHeight = CSS.height;
  6744. hasRoom = (s.maxHeight >= 0); // ALWAYS TRUE NOW
  6745. if (!hasRoom) s.noVerticalRoom = true; // makePaneFit() logic
  6746. }
  6747. if (hasRoom) {
  6748. // resizeAll passes skipCallback because it triggers callbacks after ALL panes are resized
  6749. if (!skipCallback && state.initialized)
  6750. _runCallbacks("onresize_start", pane);
  6751. $P.css(CSS); // apply the CSS to pane
  6752. if (pane !== "center")
  6753. sizeHandles(pane); // also update resizer length
  6754. if (s.noRoom && !s.isClosed && !s.isHidden)
  6755. makePaneFit(pane); // will re-open/show auto-closed/hidden pane
  6756. if (s.isVisible) {
  6757. $.extend(s, elDims($P)); // update pane dimensions
  6758. if (state.initialized) sizeContent(pane); // also resize the contents, if exists
  6759. }
  6760. }
  6761. else if (!s.noRoom && s.isVisible) // no room for pane
  6762. makePaneFit(pane); // will hide or close pane
  6763. // reset visibility, if necessary
  6764. $P.css(visCSS);
  6765. delete s.newSize;
  6766. delete s.newWidth;
  6767. delete s.newHeight;
  6768. if (!s.isVisible)
  6769. return true; // DONE - next pane
  6770. /*
  6771. * Extra CSS for IE6 or IE7 in Quirks-mode - add 'width' to NORTH/SOUTH panes
  6772. * Normally these panes have only 'left' & 'right' positions so pane auto-sizes
  6773. * ALSO required when pane is an IFRAME because will NOT default to 'full width'
  6774. * TODO: Can I use width:100% for a north/south iframe?
  6775. * TODO: Sounds like a job for $P.outerWidth( sC.innerWidth ) SETTER METHOD
  6776. */
  6777. if (pane === "center") { // finished processing midPanes
  6778. var fix = browser.isIE6 || !browser.boxModel;
  6779. if ($Ps.north && (fix || state.north.tagName=="IFRAME"))
  6780. $Ps.north.css("width", cssW($Ps.north, sC.innerWidth));
  6781. if ($Ps.south && (fix || state.south.tagName=="IFRAME"))
  6782. $Ps.south.css("width", cssW($Ps.south, sC.innerWidth));
  6783. }
  6784. // resizeAll passes skipCallback because it triggers callbacks after ALL panes are resized
  6785. if (!skipCallback && state.initialized)
  6786. _runCallbacks("onresize_end", pane);
  6787. });
  6788. }
  6789. /**
  6790. * @see window.onresize(), callbacks or custom code
  6791. * @param {(Object|boolean)=} evt_or_refresh If 'true', then also reset pane-positioning
  6792. */
  6793. , resizeAll = function (evt_or_refresh) {
  6794. var oldW = sC.innerWidth
  6795. , oldH = sC.innerHeight
  6796. ;
  6797. // stopPropagation if called by trigger("layoutdestroy") - use evtPane utility
  6798. evtPane(evt_or_refresh);
  6799. // cannot size layout when 'container' is hidden or collapsed
  6800. if (!$N.is(":visible")) return;
  6801. if (!state.initialized) {
  6802. _initLayoutElements();
  6803. return; // no need to resize since we just initialized!
  6804. }
  6805. if (evt_or_refresh === true && $.isPlainObject(options.outset)) {
  6806. // update container CSS in case outset option has changed
  6807. $N.css( options.outset );
  6808. }
  6809. // UPDATE container dimensions
  6810. $.extend(sC, elDims( $N, options.inset ));
  6811. if (!sC.outerHeight) return;
  6812. // if 'true' passed, refresh pane & handle positioning too
  6813. if (evt_or_refresh === true) {
  6814. setPanePosition();
  6815. }
  6816. // onresizeall_start will CANCEL resizing if returns false
  6817. // state.container has already been set, so user can access this info for calcuations
  6818. if (false === _runCallbacks("onresizeall_start")) return false;
  6819. var // see if container is now 'smaller' than before
  6820. shrunkH = (sC.innerHeight < oldH)
  6821. , shrunkW = (sC.innerWidth < oldW)
  6822. , $P, o, s
  6823. ;
  6824. // NOTE special order for sizing: S-N-E-W
  6825. $.each(["south","north","east","west"], function (i, pane) {
  6826. if (!$Ps[pane]) return; // no pane - SKIP
  6827. o = options[pane];
  6828. s = state[pane];
  6829. if (s.autoResize && s.size != o.size) // resize pane to original size set in options
  6830. sizePane(pane, o.size, true, true, true); // true=skipCallback/noAnimation/forceResize
  6831. else {
  6832. setSizeLimits(pane);
  6833. makePaneFit(pane, false, true, true); // true=skipCallback/forceResize
  6834. }
  6835. });
  6836. sizeMidPanes("", true, true); // true=skipCallback/forceResize
  6837. sizeHandles(); // reposition the toggler elements
  6838. // trigger all individual pane callbacks AFTER layout has finished resizing
  6839. $.each(_c.allPanes, function (i, pane) {
  6840. $P = $Ps[pane];
  6841. if (!$P) return; // SKIP
  6842. if (state[pane].isVisible) // undefined for non-existent panes
  6843. _runCallbacks("onresize_end", pane); // callback - if exists
  6844. });
  6845. _runCallbacks("onresizeall_end");
  6846. //_triggerLayoutEvent(pane, 'resizeall');
  6847. }
  6848. /**
  6849. * Whenever a pane resizes or opens that has a nested layout, trigger resizeAll
  6850. *
  6851. * @param {(string|Object)} evt_or_pane The pane just resized or opened
  6852. */
  6853. , resizeChildren = function (evt_or_pane, skipRefresh) {
  6854. var pane = evtPane.call(this, evt_or_pane);
  6855. if (!options[pane].resizeChildren) return;
  6856. // ensure the pane-children are up-to-date
  6857. if (!skipRefresh) refreshChildren( pane );
  6858. var pC = children[pane];
  6859. if ($.isPlainObject( pC )) {
  6860. // resize one or more children
  6861. $.each( pC, function (key, child) {
  6862. if (!child.destroyed) child.resizeAll();
  6863. });
  6864. }
  6865. }
  6866. /**
  6867. * IF pane has a content-div, then resize all elements inside pane to fit pane-height
  6868. *
  6869. * @param {(string|Object)} evt_or_panes The pane(s) being resized
  6870. * @param {boolean=} [remeasure=false] Should the content (header/footer) be remeasured?
  6871. */
  6872. , sizeContent = function (evt_or_panes, remeasure) {
  6873. if (!isInitialized()) return;
  6874. var panes = evtPane.call(this, evt_or_panes);
  6875. panes = panes ? panes.split(",") : _c.allPanes;
  6876. $.each(panes, function (idx, pane) {
  6877. var
  6878. $P = $Ps[pane]
  6879. , $C = $Cs[pane]
  6880. , o = options[pane]
  6881. , s = state[pane]
  6882. , m = s.content // m = measurements
  6883. ;
  6884. if (!$P || !$C || !$P.is(":visible")) return true; // NOT VISIBLE - skip
  6885. // if content-element was REMOVED, update OR remove the pointer
  6886. if (!$C.length) {
  6887. initContent(pane, false); // false = do NOT sizeContent() - already there!
  6888. if (!$C) return; // no replacement element found - pointer have been removed
  6889. }
  6890. // onsizecontent_start will CANCEL resizing if returns false
  6891. if (false === _runCallbacks("onsizecontent_start", pane)) return;
  6892. // skip re-measuring offsets if live-resizing
  6893. if ((!s.isMoving && !s.isResizing) || o.liveContentResizing || remeasure || m.top == undefined) {
  6894. _measure();
  6895. // if any footers are below pane-bottom, they may not measure correctly,
  6896. // so allow pane overflow and re-measure
  6897. if (m.hiddenFooters > 0 && $P.css("overflow") === "hidden") {
  6898. $P.css("overflow", "visible");
  6899. _measure(); // remeasure while overflowing
  6900. $P.css("overflow", "hidden");
  6901. }
  6902. }
  6903. // NOTE: spaceAbove/Below *includes* the pane paddingTop/Bottom, but not pane.borders
  6904. var newH = s.innerHeight - (m.spaceAbove - s.css.paddingTop) - (m.spaceBelow - s.css.paddingBottom);
  6905. if (!$C.is(":visible") || m.height != newH) {
  6906. // size the Content element to fit new pane-size - will autoHide if not enough room
  6907. setOuterHeight($C, newH, true); // true=autoHide
  6908. m.height = newH; // save new height
  6909. };
  6910. if (state.initialized)
  6911. _runCallbacks("onsizecontent_end", pane);
  6912. function _below ($E) {
  6913. return max(s.css.paddingBottom, (parseInt($E.css("marginBottom"), 10) || 0));
  6914. };
  6915. function _measure () {
  6916. var
  6917. ignore = options[pane].contentIgnoreSelector
  6918. , $Fs = $C.nextAll().not(".ui-layout-mask").not(ignore || ":lt(0)") // not :lt(0) = ALL
  6919. , $Fs_vis = $Fs.filter(':visible')
  6920. , $F = $Fs_vis.filter(':last')
  6921. ;
  6922. m = {
  6923. top: $C[0].offsetTop
  6924. , height: $C.outerHeight()
  6925. , numFooters: $Fs.length
  6926. , hiddenFooters: $Fs.length - $Fs_vis.length
  6927. , spaceBelow: 0 // correct if no content footer ($E)
  6928. }
  6929. m.spaceAbove = m.top; // just for state - not used in calc
  6930. m.bottom = m.top + m.height;
  6931. if ($F.length)
  6932. //spaceBelow = (LastFooter.top + LastFooter.height) [footerBottom] - Content.bottom + max(LastFooter.marginBottom, pane.paddingBotom)
  6933. m.spaceBelow = ($F[0].offsetTop + $F.outerHeight()) - m.bottom + _below($F);
  6934. else // no footer - check marginBottom on Content element itself
  6935. m.spaceBelow = _below($C);
  6936. };
  6937. });
  6938. }
  6939. /**
  6940. * Called every time a pane is opened, closed, or resized to slide the togglers to 'center' and adjust their length if necessary
  6941. *
  6942. * @see initHandles(), open(), close(), resizeAll()
  6943. * @param {(string|Object)=} evt_or_panes The pane(s) being resized
  6944. */
  6945. , sizeHandles = function (evt_or_panes) {
  6946. var panes = evtPane.call(this, evt_or_panes)
  6947. panes = panes ? panes.split(",") : _c.borderPanes;
  6948. $.each(panes, function (i, pane) {
  6949. var
  6950. o = options[pane]
  6951. , s = state[pane]
  6952. , $P = $Ps[pane]
  6953. , $R = $Rs[pane]
  6954. , $T = $Ts[pane]
  6955. , $TC
  6956. ;
  6957. if (!$P || !$R) return;
  6958. var
  6959. dir = _c[pane].dir
  6960. , _state = (s.isClosed ? "_closed" : "_open")
  6961. , spacing = o["spacing"+ _state]
  6962. , togAlign = o["togglerAlign"+ _state]
  6963. , togLen = o["togglerLength"+ _state]
  6964. , paneLen
  6965. , left
  6966. , offset
  6967. , CSS = {}
  6968. ;
  6969. if (spacing === 0) {
  6970. $R.hide();
  6971. return;
  6972. }
  6973. else if (!s.noRoom && !s.isHidden) // skip if resizer was hidden for any reason
  6974. $R.show(); // in case was previously hidden
  6975. // Resizer Bar is ALWAYS same width/height of pane it is attached to
  6976. if (dir === "horz") { // north/south
  6977. //paneLen = $P.outerWidth(); // s.outerWidth ||
  6978. paneLen = sC.innerWidth; // handle offscreen-panes
  6979. s.resizerLength = paneLen;
  6980. left = $.layout.cssNum($P, "left")
  6981. $R.css({
  6982. width: cssW($R, paneLen) // account for borders & padding
  6983. , height: cssH($R, spacing) // ditto
  6984. , left: left > -9999 ? left : sC.inset.left // handle offscreen-panes
  6985. });
  6986. }
  6987. else { // east/west
  6988. paneLen = $P.outerHeight(); // s.outerHeight ||
  6989. s.resizerLength = paneLen;
  6990. $R.css({
  6991. height: cssH($R, paneLen) // account for borders & padding
  6992. , width: cssW($R, spacing) // ditto
  6993. , top: sC.inset.top + getPaneSize("north", true) // TODO: what if no North pane?
  6994. //, top: $.layout.cssNum($Ps["center"], "top")
  6995. });
  6996. }
  6997. // remove hover classes
  6998. removeHover( o, $R );
  6999. if ($T) {
  7000. if (togLen === 0 || (s.isSliding && o.hideTogglerOnSlide)) {
  7001. $T.hide(); // always HIDE the toggler when 'sliding'
  7002. return;
  7003. }
  7004. else
  7005. $T.show(); // in case was previously hidden
  7006. if (!(togLen > 0) || togLen === "100%" || togLen > paneLen) {
  7007. togLen = paneLen;
  7008. offset = 0;
  7009. }
  7010. else { // calculate 'offset' based on options.PANE.togglerAlign_open/closed
  7011. if (isStr(togAlign)) {
  7012. switch (togAlign) {
  7013. case "top":
  7014. case "left": offset = 0;
  7015. break;
  7016. case "bottom":
  7017. case "right": offset = paneLen - togLen;
  7018. break;
  7019. case "middle":
  7020. case "center":
  7021. default: offset = round((paneLen - togLen) / 2); // 'default' catches typos
  7022. }
  7023. }
  7024. else { // togAlign = number
  7025. var x = parseInt(togAlign, 10); //
  7026. if (togAlign >= 0) offset = x;
  7027. else offset = paneLen - togLen + x; // NOTE: x is negative!
  7028. }
  7029. }
  7030. if (dir === "horz") { // north/south
  7031. var width = cssW($T, togLen);
  7032. $T.css({
  7033. width: width // account for borders & padding
  7034. , height: cssH($T, spacing) // ditto
  7035. , left: offset // TODO: VERIFY that toggler positions correctly for ALL values
  7036. , top: 0
  7037. });
  7038. // CENTER the toggler content SPAN
  7039. $T.children(".content").each(function(){
  7040. $TC = $(this);
  7041. $TC.css("marginLeft", round((width-$TC.outerWidth())/2)); // could be negative
  7042. });
  7043. }
  7044. else { // east/west
  7045. var height = cssH($T, togLen);
  7046. $T.css({
  7047. height: height // account for borders & padding
  7048. , width: cssW($T, spacing) // ditto
  7049. , top: offset // POSITION the toggler
  7050. , left: 0
  7051. });
  7052. // CENTER the toggler content SPAN
  7053. $T.children(".content").each(function(){
  7054. $TC = $(this);
  7055. $TC.css("marginTop", round((height-$TC.outerHeight())/2)); // could be negative
  7056. });
  7057. }
  7058. // remove ALL hover classes
  7059. removeHover( 0, $T );
  7060. }
  7061. // DONE measuring and sizing this resizer/toggler, so can be 'hidden' now
  7062. if (!state.initialized && (o.initHidden || s.isHidden)) {
  7063. $R.hide();
  7064. if ($T) $T.hide();
  7065. }
  7066. });
  7067. }
  7068. /**
  7069. * @param {(string|Object)} evt_or_pane
  7070. */
  7071. , enableClosable = function (evt_or_pane) {
  7072. if (!isInitialized()) return;
  7073. var pane = evtPane.call(this, evt_or_pane)
  7074. , $T = $Ts[pane]
  7075. , o = options[pane]
  7076. ;
  7077. if (!$T) return;
  7078. o.closable = true;
  7079. $T .bind("click."+ sID, function(evt){ evt.stopPropagation(); toggle(pane); })
  7080. .css("visibility", "visible")
  7081. .css("cursor", "pointer")
  7082. .attr("title", state[pane].isClosed ? o.tips.Open : o.tips.Close) // may be blank
  7083. .show();
  7084. }
  7085. /**
  7086. * @param {(string|Object)} evt_or_pane
  7087. * @param {boolean=} [hide=false]
  7088. */
  7089. , disableClosable = function (evt_or_pane, hide) {
  7090. if (!isInitialized()) return;
  7091. var pane = evtPane.call(this, evt_or_pane)
  7092. , $T = $Ts[pane]
  7093. ;
  7094. if (!$T) return;
  7095. options[pane].closable = false;
  7096. // is closable is disable, then pane MUST be open!
  7097. if (state[pane].isClosed) open(pane, false, true);
  7098. $T .unbind("."+ sID)
  7099. .css("visibility", hide ? "hidden" : "visible") // instead of hide(), which creates logic issues
  7100. .css("cursor", "default")
  7101. .attr("title", "");
  7102. }
  7103. /**
  7104. * @param {(string|Object)} evt_or_pane
  7105. */
  7106. , enableSlidable = function (evt_or_pane) {
  7107. if (!isInitialized()) return;
  7108. var pane = evtPane.call(this, evt_or_pane)
  7109. , $R = $Rs[pane]
  7110. ;
  7111. if (!$R || !$R.data('draggable')) return;
  7112. options[pane].slidable = true;
  7113. if (state[pane].isClosed)
  7114. bindStartSlidingEvents(pane, true);
  7115. }
  7116. /**
  7117. * @param {(string|Object)} evt_or_pane
  7118. */
  7119. , disableSlidable = function (evt_or_pane) {
  7120. if (!isInitialized()) return;
  7121. var pane = evtPane.call(this, evt_or_pane)
  7122. , $R = $Rs[pane]
  7123. ;
  7124. if (!$R) return;
  7125. options[pane].slidable = false;
  7126. if (state[pane].isSliding)
  7127. close(pane, false, true);
  7128. else {
  7129. bindStartSlidingEvents(pane, false);
  7130. $R .css("cursor", "default")
  7131. .attr("title", "");
  7132. removeHover(null, $R[0]); // in case currently hovered
  7133. }
  7134. }
  7135. /**
  7136. * @param {(string|Object)} evt_or_pane
  7137. */
  7138. , enableResizable = function (evt_or_pane) {
  7139. if (!isInitialized()) return;
  7140. var pane = evtPane.call(this, evt_or_pane)
  7141. , $R = $Rs[pane]
  7142. , o = options[pane]
  7143. ;
  7144. if (!$R || !$R.data('draggable')) return;
  7145. o.resizable = true;
  7146. $R.draggable("enable");
  7147. if (!state[pane].isClosed)
  7148. $R .css("cursor", o.resizerCursor)
  7149. .attr("title", o.tips.Resize);
  7150. }
  7151. /**
  7152. * @param {(string|Object)} evt_or_pane
  7153. */
  7154. , disableResizable = function (evt_or_pane) {
  7155. if (!isInitialized()) return;
  7156. var pane = evtPane.call(this, evt_or_pane)
  7157. , $R = $Rs[pane]
  7158. ;
  7159. if (!$R || !$R.data('draggable')) return;
  7160. options[pane].resizable = false;
  7161. $R .draggable("disable")
  7162. .css("cursor", "default")
  7163. .attr("title", "");
  7164. removeHover(null, $R[0]); // in case currently hovered
  7165. }
  7166. /**
  7167. * Move a pane from source-side (eg, west) to target-side (eg, east)
  7168. * If pane exists on target-side, move that to source-side, ie, 'swap' the panes
  7169. *
  7170. * @param {(string|Object)} evt_or_pane1 The pane/edge being swapped
  7171. * @param {string} pane2 ditto
  7172. */
  7173. , swapPanes = function (evt_or_pane1, pane2) {
  7174. if (!isInitialized()) return;
  7175. var pane1 = evtPane.call(this, evt_or_pane1);
  7176. // change state.edge NOW so callbacks can know where pane is headed...
  7177. state[pane1].edge = pane2;
  7178. state[pane2].edge = pane1;
  7179. // run these even if NOT state.initialized
  7180. if (false === _runCallbacks("onswap_start", pane1)
  7181. || false === _runCallbacks("onswap_start", pane2)
  7182. ) {
  7183. state[pane1].edge = pane1; // reset
  7184. state[pane2].edge = pane2;
  7185. return;
  7186. }
  7187. var
  7188. oPane1 = copy( pane1 )
  7189. , oPane2 = copy( pane2 )
  7190. , sizes = {}
  7191. ;
  7192. sizes[pane1] = oPane1 ? oPane1.state.size : 0;
  7193. sizes[pane2] = oPane2 ? oPane2.state.size : 0;
  7194. // clear pointers & state
  7195. $Ps[pane1] = false;
  7196. $Ps[pane2] = false;
  7197. state[pane1] = {};
  7198. state[pane2] = {};
  7199. // ALWAYS remove the resizer & toggler elements
  7200. if ($Ts[pane1]) $Ts[pane1].remove();
  7201. if ($Ts[pane2]) $Ts[pane2].remove();
  7202. if ($Rs[pane1]) $Rs[pane1].remove();
  7203. if ($Rs[pane2]) $Rs[pane2].remove();
  7204. $Rs[pane1] = $Rs[pane2] = $Ts[pane1] = $Ts[pane2] = false;
  7205. // transfer element pointers and data to NEW Layout keys
  7206. move( oPane1, pane2 );
  7207. move( oPane2, pane1 );
  7208. // cleanup objects
  7209. oPane1 = oPane2 = sizes = null;
  7210. // make panes 'visible' again
  7211. if ($Ps[pane1]) $Ps[pane1].css(_c.visible);
  7212. if ($Ps[pane2]) $Ps[pane2].css(_c.visible);
  7213. // fix any size discrepancies caused by swap
  7214. resizeAll();
  7215. // run these even if NOT state.initialized
  7216. _runCallbacks("onswap_end", pane1);
  7217. _runCallbacks("onswap_end", pane2);
  7218. return;
  7219. function copy (n) { // n = pane
  7220. var
  7221. $P = $Ps[n]
  7222. , $C = $Cs[n]
  7223. ;
  7224. return !$P ? false : {
  7225. pane: n
  7226. , P: $P ? $P[0] : false
  7227. , C: $C ? $C[0] : false
  7228. , state: $.extend(true, {}, state[n])
  7229. , options: $.extend(true, {}, options[n])
  7230. }
  7231. };
  7232. function move (oPane, pane) {
  7233. if (!oPane) return;
  7234. var
  7235. P = oPane.P
  7236. , C = oPane.C
  7237. , oldPane = oPane.pane
  7238. , c = _c[pane]
  7239. // save pane-options that should be retained
  7240. , s = $.extend(true, {}, state[pane])
  7241. , o = options[pane]
  7242. // RETAIN side-specific FX Settings - more below
  7243. , fx = { resizerCursor: o.resizerCursor }
  7244. , re, size, pos
  7245. ;
  7246. $.each("fxName,fxSpeed,fxSettings".split(","), function (i, k) {
  7247. fx[k +"_open"] = o[k +"_open"];
  7248. fx[k +"_close"] = o[k +"_close"];
  7249. fx[k +"_size"] = o[k +"_size"];
  7250. });
  7251. // update object pointers and attributes
  7252. $Ps[pane] = $(P)
  7253. .data({
  7254. layoutPane: Instance[pane] // NEW pointer to pane-alias-object
  7255. , layoutEdge: pane
  7256. })
  7257. .css(_c.hidden)
  7258. .css(c.cssReq)
  7259. ;
  7260. $Cs[pane] = C ? $(C) : false;
  7261. // set options and state
  7262. options[pane] = $.extend(true, {}, oPane.options, fx);
  7263. state[pane] = $.extend(true, {}, oPane.state);
  7264. // change classNames on the pane, eg: ui-layout-pane-east ==> ui-layout-pane-west
  7265. re = new RegExp(o.paneClass +"-"+ oldPane, "g");
  7266. P.className = P.className.replace(re, o.paneClass +"-"+ pane);
  7267. // ALWAYS regenerate the resizer & toggler elements
  7268. initHandles(pane); // create the required resizer & toggler
  7269. // if moving to different orientation, then keep 'target' pane size
  7270. if (c.dir != _c[oldPane].dir) {
  7271. size = sizes[pane] || 0;
  7272. setSizeLimits(pane); // update pane-state
  7273. size = max(size, state[pane].minSize);
  7274. // use manualSizePane to disable autoResize - not useful after panes are swapped
  7275. manualSizePane(pane, size, true, true); // true/true = skipCallback/noAnimation
  7276. }
  7277. else // move the resizer here
  7278. $Rs[pane].css(c.side, sC.inset[c.side] + (state[pane].isVisible ? getPaneSize(pane) : 0));
  7279. // ADD CLASSNAMES & SLIDE-BINDINGS
  7280. if (oPane.state.isVisible && !s.isVisible)
  7281. setAsOpen(pane, true); // true = skipCallback
  7282. else {
  7283. setAsClosed(pane);
  7284. bindStartSlidingEvents(pane, true); // will enable events IF option is set
  7285. }
  7286. // DESTROY the object
  7287. oPane = null;
  7288. };
  7289. }
  7290. /**
  7291. * INTERNAL method to sync pin-buttons when pane is opened or closed
  7292. * Unpinned means the pane is 'sliding' - ie, over-top of the adjacent panes
  7293. *
  7294. * @see open(), setAsOpen(), setAsClosed()
  7295. * @param {string} pane These are the params returned to callbacks by layout()
  7296. * @param {boolean} doPin True means set the pin 'down', False means 'up'
  7297. */
  7298. , syncPinBtns = function (pane, doPin) {
  7299. if ($.layout.plugins.buttons)
  7300. $.each(state[pane].pins, function (i, selector) {
  7301. $.layout.buttons.setPinState(Instance, $(selector), pane, doPin);
  7302. });
  7303. }
  7304. ; // END var DECLARATIONS
  7305. /**
  7306. * Capture keys when enableCursorHotkey - toggle pane if hotkey pressed
  7307. *
  7308. * @see document.keydown()
  7309. */
  7310. function keyDown (evt) {
  7311. if (!evt) return true;
  7312. var code = evt.keyCode;
  7313. if (code < 33) return true; // ignore special keys: ENTER, TAB, etc
  7314. var
  7315. PANE = {
  7316. 38: "north" // Up Cursor - $.ui.keyCode.UP
  7317. , 40: "south" // Down Cursor - $.ui.keyCode.DOWN
  7318. , 37: "west" // Left Cursor - $.ui.keyCode.LEFT
  7319. , 39: "east" // Right Cursor - $.ui.keyCode.RIGHT
  7320. }
  7321. , ALT = evt.altKey // no worky!
  7322. , SHIFT = evt.shiftKey
  7323. , CTRL = evt.ctrlKey
  7324. , CURSOR = (CTRL && code >= 37 && code <= 40)
  7325. , o, k, m, pane
  7326. ;
  7327. if (CURSOR && options[PANE[code]].enableCursorHotkey) // valid cursor-hotkey
  7328. pane = PANE[code];
  7329. else if (CTRL || SHIFT) // check to see if this matches a custom-hotkey
  7330. $.each(_c.borderPanes, function (i, p) { // loop each pane to check its hotkey
  7331. o = options[p];
  7332. k = o.customHotkey;
  7333. m = o.customHotkeyModifier; // if missing or invalid, treated as "CTRL+SHIFT"
  7334. if ((SHIFT && m=="SHIFT") || (CTRL && m=="CTRL") || (CTRL && SHIFT)) { // Modifier matches
  7335. if (k && code === (isNaN(k) || k <= 9 ? k.toUpperCase().charCodeAt(0) : k)) { // Key matches
  7336. pane = p;
  7337. return false; // BREAK
  7338. }
  7339. }
  7340. });
  7341. // validate pane
  7342. if (!pane || !$Ps[pane] || !options[pane].closable || state[pane].isHidden)
  7343. return true;
  7344. toggle(pane);
  7345. evt.stopPropagation();
  7346. evt.returnValue = false; // CANCEL key
  7347. return false;
  7348. };
  7349. /*
  7350. * ######################################
  7351. * UTILITY METHODS
  7352. * called externally or by initButtons
  7353. * ######################################
  7354. */
  7355. /**
  7356. * Change/reset a pane overflow setting & zIndex to allow popups/drop-downs to work
  7357. *
  7358. * @param {Object=} [el] (optional) Can also be 'bound' to a click, mouseOver, or other event
  7359. */
  7360. function allowOverflow (el) {
  7361. if (!isInitialized()) return;
  7362. if (this && this.tagName) el = this; // BOUND to element
  7363. var $P;
  7364. if (isStr(el))
  7365. $P = $Ps[el];
  7366. else if ($(el).data("layoutRole"))
  7367. $P = $(el);
  7368. else
  7369. $(el).parents().each(function(){
  7370. if ($(this).data("layoutRole")) {
  7371. $P = $(this);
  7372. return false; // BREAK
  7373. }
  7374. });
  7375. if (!$P || !$P.length) return; // INVALID
  7376. var
  7377. pane = $P.data("layoutEdge")
  7378. , s = state[pane]
  7379. ;
  7380. // if pane is already raised, then reset it before doing it again!
  7381. // this would happen if allowOverflow is attached to BOTH the pane and an element
  7382. if (s.cssSaved)
  7383. resetOverflow(pane); // reset previous CSS before continuing
  7384. // if pane is raised by sliding or resizing, or its closed, then abort
  7385. if (s.isSliding || s.isResizing || s.isClosed) {
  7386. s.cssSaved = false;
  7387. return;
  7388. }
  7389. var
  7390. newCSS = { zIndex: (options.zIndexes.resizer_normal + 1) }
  7391. , curCSS = {}
  7392. , of = $P.css("overflow")
  7393. , ofX = $P.css("overflowX")
  7394. , ofY = $P.css("overflowY")
  7395. ;
  7396. // determine which, if any, overflow settings need to be changed
  7397. if (of != "visible") {
  7398. curCSS.overflow = of;
  7399. newCSS.overflow = "visible";
  7400. }
  7401. if (ofX && !ofX.match(/(visible|auto)/)) {
  7402. curCSS.overflowX = ofX;
  7403. newCSS.overflowX = "visible";
  7404. }
  7405. if (ofY && !ofY.match(/(visible|auto)/)) {
  7406. curCSS.overflowY = ofX;
  7407. newCSS.overflowY = "visible";
  7408. }
  7409. // save the current overflow settings - even if blank!
  7410. s.cssSaved = curCSS;
  7411. // apply new CSS to raise zIndex and, if necessary, make overflow 'visible'
  7412. $P.css( newCSS );
  7413. // make sure the zIndex of all other panes is normal
  7414. $.each(_c.allPanes, function(i, p) {
  7415. if (p != pane) resetOverflow(p);
  7416. });
  7417. };
  7418. /**
  7419. * @param {Object=} [el] (optional) Can also be 'bound' to a click, mouseOver, or other event
  7420. */
  7421. function resetOverflow (el) {
  7422. if (!isInitialized()) return;
  7423. if (this && this.tagName) el = this; // BOUND to element
  7424. var $P;
  7425. if (isStr(el))
  7426. $P = $Ps[el];
  7427. else if ($(el).data("layoutRole"))
  7428. $P = $(el);
  7429. else
  7430. $(el).parents().each(function(){
  7431. if ($(this).data("layoutRole")) {
  7432. $P = $(this);
  7433. return false; // BREAK
  7434. }
  7435. });
  7436. if (!$P || !$P.length) return; // INVALID
  7437. var
  7438. pane = $P.data("layoutEdge")
  7439. , s = state[pane]
  7440. , CSS = s.cssSaved || {}
  7441. ;
  7442. // reset the zIndex
  7443. if (!s.isSliding && !s.isResizing)
  7444. $P.css("zIndex", options.zIndexes.pane_normal);
  7445. // reset Overflow - if necessary
  7446. $P.css( CSS );
  7447. // clear var
  7448. s.cssSaved = false;
  7449. };
  7450. /*
  7451. * #####################
  7452. * CREATE/RETURN LAYOUT
  7453. * #####################
  7454. */
  7455. try {
  7456. // validate that container exists
  7457. var $N = $(this).eq(0); // FIRST matching Container element
  7458. if (!$N.length) {
  7459. return _log( options.errors.containerMissing );
  7460. };
  7461. // Users retrieve Instance of a layout with: $N.layout() OR $N.data("layout")
  7462. // return the Instance-pointer if layout has already been initialized
  7463. if ($N.data("layoutContainer") && $N.data("layout"))
  7464. return $N.data("layout"); // cached pointer
  7465. } catch(e){
  7466. console.log("eeeee",e)
  7467. }
  7468. // init global vars
  7469. var
  7470. $Ps = {} // Panes x5 - set in initPanes()
  7471. , $Cs = {} // Content x5 - set in initPanes()
  7472. , $Rs = {} // Resizers x4 - set in initHandles()
  7473. , $Ts = {} // Togglers x4 - set in initHandles()
  7474. , $Ms = $([]) // Masks - up to 2 masks per pane (IFRAME + DIV)
  7475. // aliases for code brevity
  7476. , sC = state.container // alias for easy access to 'container dimensions'
  7477. , sID = state.id // alias for unique layout ID/namespace - eg: "layout435"
  7478. ;
  7479. // create Instance object to expose data & option Properties, and primary action Methods
  7480. var Instance = {
  7481. // layout data
  7482. options: options // property - options hash
  7483. , state: state // property - dimensions hash
  7484. // object pointers
  7485. , container: $N // property - object pointers for layout container
  7486. , panes: $Ps // property - object pointers for ALL Panes: panes.north, panes.center
  7487. , contents: $Cs // property - object pointers for ALL Content: contents.north, contents.center
  7488. , resizers: $Rs // property - object pointers for ALL Resizers, eg: resizers.north
  7489. , togglers: $Ts // property - object pointers for ALL Togglers, eg: togglers.north
  7490. // border-pane open/close
  7491. , hide: hide // method - ditto
  7492. , show: show // method - ditto
  7493. , toggle: toggle // method - pass a 'pane' ("north", "west", etc)
  7494. , open: open // method - ditto
  7495. , close: close // method - ditto
  7496. , slideOpen: slideOpen // method - ditto
  7497. , slideClose: slideClose // method - ditto
  7498. , slideToggle: slideToggle // method - ditto
  7499. // pane actions
  7500. , setSizeLimits: setSizeLimits // method - pass a 'pane' - update state min/max data
  7501. , _sizePane: sizePane // method -intended for user by plugins only!
  7502. , sizePane: manualSizePane // method - pass a 'pane' AND an 'outer-size' in pixels or percent, or 'auto'
  7503. , sizeContent: sizeContent // method - pass a 'pane'
  7504. , swapPanes: swapPanes // method - pass TWO 'panes' - will swap them
  7505. , showMasks: showMasks // method - pass a 'pane' OR list of panes - default = all panes with mask option set
  7506. , hideMasks: hideMasks // method - ditto'
  7507. // pane element methods
  7508. , initContent: initContent // method - ditto
  7509. , addPane: addPane // method - pass a 'pane'
  7510. , removePane: removePane // method - pass a 'pane' to remove from layout, add 'true' to delete the pane-elem
  7511. , createChildren: createChildren // method - pass a 'pane' and (optional) layout-options (OVERRIDES options[pane].children
  7512. , refreshChildren: refreshChildren // method - pass a 'pane' and a layout-instance
  7513. // special pane option setting
  7514. , enableClosable: enableClosable // method - pass a 'pane'
  7515. , disableClosable: disableClosable // method - ditto
  7516. , enableSlidable: enableSlidable // method - ditto
  7517. , disableSlidable: disableSlidable // method - ditto
  7518. , enableResizable: enableResizable // method - ditto
  7519. , disableResizable: disableResizable// method - ditto
  7520. // utility methods for panes
  7521. , allowOverflow: allowOverflow // utility - pass calling element (this)
  7522. , resetOverflow: resetOverflow // utility - ditto
  7523. // layout control
  7524. , destroy: destroy // method - no parameters
  7525. , initPanes: isInitialized // method - no parameters
  7526. , resizeAll: resizeAll // method - no parameters
  7527. // callback triggering
  7528. , runCallbacks: _runCallbacks // method - pass evtName & pane (if a pane-event), eg: trigger("onopen", "west")
  7529. // alias collections of options, state and children - created in addPane and extended elsewhere
  7530. , hasParentLayout: false // set by initContainer()
  7531. , children: children // pointers to child-layouts, eg: Instance.children.west.layoutName
  7532. , north: false // alias group: { name: pane, pane: $Ps[pane], options: options[pane], state: state[pane], children: children[pane] }
  7533. , south: false // ditto
  7534. , west: false // ditto
  7535. , east: false // ditto
  7536. , center: false // ditto
  7537. };
  7538. // create the border layout NOW
  7539. if (_create() === 'cancel') // onload_start callback returned false to CANCEL layout creation
  7540. return null;
  7541. else // true OR false -- if layout-elements did NOT init (hidden or do not exist), can auto-init later
  7542. return Instance; // return the Instance object
  7543. }
  7544. })( jQuery );
  7545. // END Layout - keep internal vars internal!
  7546. // START Plugins - shared wrapper, no global vars
  7547. (function ($) {
  7548. /**
  7549. * jquery.layout.state 1.0
  7550. * $Date: 2011-07-16 08:00:00 (Sat, 16 July 2011) $
  7551. *
  7552. * Copyright (c) 2012
  7553. * Kevin Dalman (http://allpro.net)
  7554. *
  7555. * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
  7556. * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
  7557. *
  7558. * @requires: UI Layout 1.3.0.rc30.1 or higher
  7559. * @requires: $.ui.cookie (above)
  7560. *
  7561. * @see: http://groups.google.com/group/jquery-ui-layout
  7562. */
  7563. /*
  7564. * State-management options stored in options.stateManagement, which includes a .cookie hash
  7565. * Default options saves ALL KEYS for ALL PANES, ie: pane.size, pane.isClosed, pane.isHidden
  7566. *
  7567. * // STATE/COOKIE OPTIONS
  7568. * @example $(el).layout({
  7569. stateManagement: {
  7570. enabled: true
  7571. , stateKeys: "east.size,west.size,east.isClosed,west.isClosed"
  7572. , cookie: { name: "appLayout", path: "/" }
  7573. }
  7574. })
  7575. * @example $(el).layout({ stateManagement__enabled: true }) // enable auto-state-management using cookies
  7576. * @example $(el).layout({ stateManagement__cookie: { name: "appLayout", path: "/" } })
  7577. * @example $(el).layout({ stateManagement__cookie__name: "appLayout", stateManagement__cookie__path: "/" })
  7578. *
  7579. * // STATE/COOKIE METHODS
  7580. * @example myLayout.saveCookie( "west.isClosed,north.size,south.isHidden", {expires: 7} );
  7581. * @example myLayout.loadCookie();
  7582. * @example myLayout.deleteCookie();
  7583. * @example var JSON = myLayout.readState(); // CURRENT Layout State
  7584. * @example var JSON = myLayout.readCookie(); // SAVED Layout State (from cookie)
  7585. * @example var JSON = myLayout.state.stateData; // LAST LOADED Layout State (cookie saved in layout.state hash)
  7586. *
  7587. * CUSTOM STATE-MANAGEMENT (eg, saved in a database)
  7588. * @example var JSON = myLayout.readState( "west.isClosed,north.size,south.isHidden" );
  7589. * @example myLayout.loadState( JSON );
  7590. */
  7591. /**
  7592. * UI COOKIE UTILITY
  7593. *
  7594. * A $.cookie OR $.ui.cookie namespace *should be standard*, but until then...
  7595. * This creates $.ui.cookie so Layout does not need the cookie.jquery.js plugin
  7596. * NOTE: This utility is REQUIRED by the layout.state plugin
  7597. *
  7598. * Cookie methods in Layout are created as part of State Management
  7599. */
  7600. if (!$.ui) $.ui = {};
  7601. $.ui.cookie = {
  7602. // cookieEnabled is not in DOM specs, but DOES works in all browsers,including IE6
  7603. acceptsCookies: !!navigator.cookieEnabled
  7604. , read: function (name) {
  7605. var c = document.cookie
  7606. , cs = c ? c.split(';') : []
  7607. , pair // loop var
  7608. ;
  7609. for (var i=0, n=cs.length; i < n; i++) {
  7610. pair = $.trim(cs[i]).split('='); // name=value pair
  7611. if (pair[0] == name) // found the layout cookie
  7612. return decodeURIComponent(pair[1]);
  7613. }
  7614. return null;
  7615. }
  7616. , write: function (name, val, cookieOpts) {
  7617. var params = ""
  7618. , date = ""
  7619. , clear = false
  7620. , o = cookieOpts || {}
  7621. , x = o.expires || null
  7622. , t = $.type(x)
  7623. ;
  7624. if (t === "date")
  7625. date = x;
  7626. else if (t === "string" && x > 0) {
  7627. x = parseInt(x,10);
  7628. t = "number";
  7629. }
  7630. if (t === "number") {
  7631. date = new Date();
  7632. if (x > 0)
  7633. date.setDate(date.getDate() + x);
  7634. else {
  7635. date.setFullYear(1970);
  7636. clear = true;
  7637. }
  7638. }
  7639. if (date) params += ";expires="+ date.toUTCString();
  7640. if (o.path) params += ";path="+ o.path;
  7641. if (o.domain) params += ";domain="+ o.domain;
  7642. if (o.secure) params += ";secure";
  7643. document.cookie = name +"="+ (clear ? "" : encodeURIComponent( val )) + params; // write or clear cookie
  7644. }
  7645. , clear: function (name) {
  7646. $.ui.cookie.write(name, "", {expires: -1});
  7647. }
  7648. };
  7649. // if cookie.jquery.js is not loaded, create an alias to replicate it
  7650. // this may be useful to other plugins or code dependent on that plugin
  7651. if (!$.cookie) $.cookie = function (k, v, o) {
  7652. var C = $.ui.cookie;
  7653. if (v === null)
  7654. C.clear(k);
  7655. else if (v === undefined)
  7656. return C.read(k);
  7657. else
  7658. C.write(k, v, o);
  7659. };
  7660. // tell Layout that the state plugin is available
  7661. $.layout.plugins.stateManagement = true;
  7662. // Add State-Management options to layout.defaults
  7663. $.layout.config.optionRootKeys.push("stateManagement");
  7664. $.layout.defaults.stateManagement = {
  7665. enabled: false // true = enable state-management, even if not using cookies
  7666. , autoSave: true // Save a state-cookie when page exits?
  7667. , autoLoad: true // Load the state-cookie when Layout inits?
  7668. , animateLoad: true // animate panes when loading state into an active layout
  7669. , includeChildren: true // recurse into child layouts to include their state as well
  7670. // List state-data to save - must be pane-specific
  7671. , stateKeys: "north.size,south.size,east.size,west.size,"+
  7672. "north.isClosed,south.isClosed,east.isClosed,west.isClosed,"+
  7673. "north.isHidden,south.isHidden,east.isHidden,west.isHidden"
  7674. , cookie: {
  7675. name: "" // If not specified, will use Layout.name, else just "Layout"
  7676. , domain: "" // blank = current domain
  7677. , path: "" // blank = current page, "/" = entire website
  7678. , expires: "" // 'days' to keep cookie - leave blank for 'session cookie'
  7679. , secure: false
  7680. }
  7681. };
  7682. // Set stateManagement as a layout-option, NOT a pane-option
  7683. $.layout.optionsMap.layout.push("stateManagement");
  7684. /*
  7685. * State Management methods
  7686. */
  7687. $.layout.state = {
  7688. /**
  7689. * Get the current layout state and save it to a cookie
  7690. *
  7691. * myLayout.saveCookie( keys, cookieOpts )
  7692. *
  7693. * @param {Object} inst
  7694. * @param {(string|Array)=} keys
  7695. * @param {Object=} cookieOpts
  7696. */
  7697. saveCookie: function (inst, keys, cookieOpts) {
  7698. var o = inst.options
  7699. , sm = o.stateManagement
  7700. , oC = $.extend(true, {}, sm.cookie, cookieOpts || null)
  7701. , data = inst.state.stateData = inst.readState( keys || sm.stateKeys ) // read current panes-state
  7702. ;
  7703. $.ui.cookie.write( oC.name || o.name || "Layout", $.layout.state.encodeJSON(data), oC );
  7704. return $.extend(true, {}, data); // return COPY of state.stateData data
  7705. }
  7706. /**
  7707. * Remove the state cookie
  7708. *
  7709. * @param {Object} inst
  7710. */
  7711. , deleteCookie: function (inst) {
  7712. var o = inst.options;
  7713. $.ui.cookie.clear( o.stateManagement.cookie.name || o.name || "Layout" );
  7714. }
  7715. /**
  7716. * Read & return data from the cookie - as JSON
  7717. *
  7718. * @param {Object} inst
  7719. */
  7720. , readCookie: function (inst) {
  7721. var o = inst.options;
  7722. var c = $.ui.cookie.read( o.stateManagement.cookie.name || o.name || "Layout" );
  7723. // convert cookie string back to a hash and return it
  7724. return c ? $.layout.state.decodeJSON(c) : {};
  7725. }
  7726. /**
  7727. * Get data from the cookie and USE IT to loadState
  7728. *
  7729. * @param {Object} inst
  7730. */
  7731. , loadCookie: function (inst) {
  7732. var c = $.layout.state.readCookie(inst); // READ the cookie
  7733. if (c) {
  7734. inst.state.stateData = $.extend(true, {}, c); // SET state.stateData
  7735. inst.loadState(c); // LOAD the retrieved state
  7736. }
  7737. return c;
  7738. }
  7739. /**
  7740. * Update layout options from the cookie, if one exists
  7741. *
  7742. * @param {Object} inst
  7743. * @param {Object=} stateData
  7744. * @param {boolean=} animate
  7745. */
  7746. , loadState: function (inst, data, opts) {
  7747. if (!$.isPlainObject( data ) || $.isEmptyObject( data )) return;
  7748. // normalize data & cache in the state object
  7749. data = inst.state.stateData = $.layout.transformData( data ); // panes = default subkey
  7750. // add missing/default state-restore options
  7751. var smo = inst.options.stateManagement;
  7752. opts = $.extend({
  7753. animateLoad: false //smo.animateLoad
  7754. , includeChildren: smo.includeChildren
  7755. }, opts );
  7756. if (!inst.state.initialized) {
  7757. /*
  7758. * layout NOT initialized, so just update its options
  7759. */
  7760. // MUST remove pane.children keys before applying to options
  7761. // use a copy so we don't remove keys from original data
  7762. var o = $.extend(true, {}, data);
  7763. //delete o.center; // center has no state-data - only children
  7764. $.each($.layout.config.allPanes, function (idx, pane) {
  7765. if (o[pane]) delete o[pane].children;
  7766. });
  7767. // update CURRENT layout-options with saved state data
  7768. $.extend(true, inst.options, o);
  7769. }
  7770. else {
  7771. /*
  7772. * layout already initialized, so modify layout's configuration
  7773. */
  7774. var noAnimate = !opts.animateLoad
  7775. , o, c, h, state, open
  7776. ;
  7777. $.each($.layout.config.borderPanes, function (idx, pane) {
  7778. o = data[ pane ];
  7779. if (!$.isPlainObject( o )) return; // no key, skip pane
  7780. s = o.size;
  7781. c = o.initClosed;
  7782. h = o.initHidden;
  7783. ar = o.autoResize
  7784. state = inst.state[pane];
  7785. open = state.isVisible;
  7786. // reset autoResize
  7787. if (ar)
  7788. state.autoResize = ar;
  7789. // resize BEFORE opening
  7790. if (!open)
  7791. inst._sizePane(pane, s, false, false, false); // false=skipCallback/noAnimation/forceResize
  7792. // open/close as necessary - DO NOT CHANGE THIS ORDER!
  7793. if (h === true) inst.hide(pane, noAnimate);
  7794. else if (c === true) inst.close(pane, false, noAnimate);
  7795. else if (c === false) inst.open (pane, false, noAnimate);
  7796. else if (h === false) inst.show (pane, false, noAnimate);
  7797. // resize AFTER any other actions
  7798. if (open)
  7799. inst._sizePane(pane, s, false, false, noAnimate); // animate resize if option passed
  7800. });
  7801. /*
  7802. * RECURSE INTO CHILD-LAYOUTS
  7803. */
  7804. if (opts.includeChildren) {
  7805. var paneStateChildren, childState;
  7806. $.each(inst.children, function (pane, paneChildren) {
  7807. paneStateChildren = data[pane] ? data[pane].children : 0;
  7808. if (paneStateChildren && paneChildren) {
  7809. $.each(paneChildren, function (stateKey, child) {
  7810. childState = paneStateChildren[stateKey];
  7811. if (child && childState)
  7812. child.loadState( childState );
  7813. });
  7814. }
  7815. });
  7816. }
  7817. }
  7818. }
  7819. /**
  7820. * Get the *current layout state* and return it as a hash
  7821. *
  7822. * @param {Object=} inst // Layout instance to get state for
  7823. * @param {object=} [opts] // State-Managements override options
  7824. */
  7825. , readState: function (inst, opts) {
  7826. // backward compatility
  7827. if ($.type(opts) === 'string') opts = { keys: opts };
  7828. if (!opts) opts = {};
  7829. var sm = inst.options.stateManagement
  7830. , ic = opts.includeChildren
  7831. , recurse = ic !== undefined ? ic : sm.includeChildren
  7832. , keys = opts.stateKeys || sm.stateKeys
  7833. , alt = { isClosed: 'initClosed', isHidden: 'initHidden' }
  7834. , state = inst.state
  7835. , panes = $.layout.config.allPanes
  7836. , data = {}
  7837. , pair, pane, key, val
  7838. , ps, pC, child, array, count, branch
  7839. ;
  7840. if ($.isArray(keys)) keys = keys.join(",");
  7841. // convert keys to an array and change delimiters from '__' to '.'
  7842. keys = keys.replace(/__/g, ".").split(',');
  7843. // loop keys and create a data hash
  7844. for (var i=0, n=keys.length; i < n; i++) {
  7845. pair = keys[i].split(".");
  7846. pane = pair[0];
  7847. key = pair[1];
  7848. if ($.inArray(pane, panes) < 0) continue; // bad pane!
  7849. val = state[ pane ][ key ];
  7850. if (val == undefined) continue;
  7851. if (key=="isClosed" && state[pane]["isSliding"])
  7852. val = true; // if sliding, then *really* isClosed
  7853. ( data[pane] || (data[pane]={}) )[ alt[key] ? alt[key] : key ] = val;
  7854. }
  7855. // recurse into the child-layouts for each pane
  7856. if (recurse) {
  7857. $.each(panes, function (idx, pane) {
  7858. pC = inst.children[pane];
  7859. ps = state.stateData[pane];
  7860. if ($.isPlainObject( pC ) && !$.isEmptyObject( pC )) {
  7861. // ensure a key exists for this 'pane', eg: branch = data.center
  7862. branch = data[pane] || (data[pane] = {});
  7863. if (!branch.children) branch.children = {};
  7864. $.each( pC, function (key, child) {
  7865. // ONLY read state from an initialize layout
  7866. if ( child.state.initialized )
  7867. branch.children[ key ] = $.layout.state.readState( child );
  7868. // if we have PREVIOUS (onLoad) state for this child-layout, KEEP IT!
  7869. else if ( ps && ps.children && ps.children[ key ] ) {
  7870. branch.children[ key ] = $.extend(true, {}, ps.children[ key ] );
  7871. }
  7872. });
  7873. }
  7874. });
  7875. }
  7876. return data;
  7877. }
  7878. /**
  7879. * Stringify a JSON hash so can save in a cookie or db-field
  7880. */
  7881. , encodeJSON: function (JSON) {
  7882. return parse(JSON);
  7883. function parse (h) {
  7884. var D=[], i=0, k, v, t // k = key, v = value
  7885. , a = $.isArray(h)
  7886. ;
  7887. for (k in h) {
  7888. v = h[k];
  7889. t = typeof v;
  7890. if (t == 'string') // STRING - add quotes
  7891. v = '"'+ v +'"';
  7892. else if (t == 'object') // SUB-KEY - recurse into it
  7893. v = parse(v);
  7894. D[i++] = (!a ? '"'+ k +'":' : '') + v;
  7895. }
  7896. return (a ? '[' : '{') + D.join(',') + (a ? ']' : '}');
  7897. };
  7898. }
  7899. /**
  7900. * Convert stringified JSON back to a hash object
  7901. * @see $.parseJSON(), adding in jQuery 1.4.1
  7902. */
  7903. , decodeJSON: function (str) {
  7904. try { return $.parseJSON ? $.parseJSON(str) : window["eval"]("("+ str +")") || {}; }
  7905. catch (e) { return {}; }
  7906. }
  7907. , _create: function (inst) {
  7908. var _ = $.layout.state
  7909. , o = inst.options
  7910. , sm = o.stateManagement
  7911. ;
  7912. // ADD State-Management plugin methods to inst
  7913. $.extend( inst, {
  7914. // readCookie - update options from cookie - returns hash of cookie data
  7915. readCookie: function () { return _.readCookie(inst); }
  7916. // deleteCookie
  7917. , deleteCookie: function () { _.deleteCookie(inst); }
  7918. // saveCookie - optionally pass keys-list and cookie-options (hash)
  7919. , saveCookie: function (keys, cookieOpts) { return _.saveCookie(inst, keys, cookieOpts); }
  7920. // loadCookie - readCookie and use to loadState() - returns hash of cookie data
  7921. , loadCookie: function () { return _.loadCookie(inst); }
  7922. // loadState - pass a hash of state to use to update options
  7923. , loadState: function (stateData, opts) { _.loadState(inst, stateData, opts); }
  7924. // readState - returns hash of current layout-state
  7925. , readState: function (keys) { return _.readState(inst, keys); }
  7926. // add JSON utility methods too...
  7927. , encodeJSON: _.encodeJSON
  7928. , decodeJSON: _.decodeJSON
  7929. });
  7930. // init state.stateData key, even if plugin is initially disabled
  7931. inst.state.stateData = {};
  7932. // autoLoad MUST BE one of: data-array, data-hash, callback-function, or TRUE
  7933. if ( !sm.autoLoad ) return;
  7934. // When state-data exists in the autoLoad key USE IT,
  7935. // even if stateManagement.enabled == false
  7936. if ($.isPlainObject( sm.autoLoad )) {
  7937. if (!$.isEmptyObject( sm.autoLoad )) {
  7938. inst.loadState( sm.autoLoad );
  7939. }
  7940. }
  7941. else if ( sm.enabled ) {
  7942. // update the options from cookie or callback
  7943. // if options is a function, call it to get stateData
  7944. if ($.isFunction( sm.autoLoad )) {
  7945. var d = {};
  7946. try {
  7947. d = sm.autoLoad( inst, inst.state, inst.options, inst.options.name || '' ); // try to get data from fn
  7948. } catch (e) {}
  7949. if (d && $.isPlainObject( d ) && !$.isEmptyObject( d ))
  7950. inst.loadState(d);
  7951. }
  7952. else // any other truthy value will trigger loadCookie
  7953. inst.loadCookie();
  7954. }
  7955. }
  7956. , _unload: function (inst) {
  7957. var sm = inst.options.stateManagement;
  7958. if (sm.enabled && sm.autoSave) {
  7959. // if options is a function, call it to save the stateData
  7960. if ($.isFunction( sm.autoSave )) {
  7961. try {
  7962. sm.autoSave( inst, inst.state, inst.options, inst.options.name || '' ); // try to get data from fn
  7963. } catch (e) {}
  7964. }
  7965. else // any truthy value will trigger saveCookie
  7966. inst.saveCookie();
  7967. }
  7968. }
  7969. };
  7970. // add state initialization method to Layout's onCreate array of functions
  7971. $.layout.onCreate.push( $.layout.state._create );
  7972. $.layout.onUnload.push( $.layout.state._unload );
  7973. /**
  7974. * jquery.layout.buttons 1.0
  7975. * $Date: 2011-07-16 08:00:00 (Sat, 16 July 2011) $
  7976. *
  7977. * Copyright (c) 2012
  7978. * Kevin Dalman (http://allpro.net)
  7979. *
  7980. * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
  7981. * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
  7982. *
  7983. * @requires: UI Layout 1.3.0.rc30.1 or higher
  7984. *
  7985. * @see: http://groups.google.com/group/jquery-ui-layout
  7986. *
  7987. * Docs: [ to come ]
  7988. * Tips: [ to come ]
  7989. */
  7990. // tell Layout that the state plugin is available
  7991. $.layout.plugins.buttons = true;
  7992. // Add buttons options to layout.defaults
  7993. $.layout.defaults.autoBindCustomButtons = false;
  7994. // Specify autoBindCustomButtons as a layout-option, NOT a pane-option
  7995. $.layout.optionsMap.layout.push("autoBindCustomButtons");
  7996. /*
  7997. * Button methods
  7998. */
  7999. $.layout.buttons = {
  8000. /**
  8001. * Searches for .ui-layout-button-xxx elements and auto-binds them as layout-buttons
  8002. *
  8003. * @see _create()
  8004. *
  8005. * @param {Object} inst Layout Instance object
  8006. */
  8007. init: function (inst) {
  8008. var pre = "ui-layout-button-"
  8009. , layout = inst.options.name || ""
  8010. , name;
  8011. $.each("toggle,open,close,pin,toggle-slide,open-slide".split(","), function (i, action) {
  8012. $.each($.layout.config.borderPanes, function (ii, pane) {
  8013. $("."+pre+action+"-"+pane).each(function(){
  8014. // if button was previously 'bound', data.layoutName was set, but is blank if layout has no 'name'
  8015. name = $(this).data("layoutName") || $(this).attr("layoutName");
  8016. if (name == undefined || name === layout)
  8017. inst.bindButton(this, action, pane);
  8018. });
  8019. });
  8020. });
  8021. }
  8022. /**
  8023. * Helper function to validate params received by addButton utilities
  8024. *
  8025. * Two classes are added to the element, based on the buttonClass...
  8026. * The type of button is appended to create the 2nd className:
  8027. * - ui-layout-button-pin // action btnClass
  8028. * - ui-layout-button-pin-west // action btnClass + pane
  8029. * - ui-layout-button-toggle
  8030. * - ui-layout-button-open
  8031. * - ui-layout-button-close
  8032. *
  8033. * @param {Object} inst Layout Instance object
  8034. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  8035. * @param {string} pane Name of the pane the button is for: 'north', 'south', etc.
  8036. *
  8037. * @return {Array.<Object>} If both params valid, the element matching 'selector' in a jQuery wrapper - otherwise returns null
  8038. */
  8039. , get: function (inst, selector, pane, action) {
  8040. var $E = $(selector)
  8041. , o = inst.options
  8042. , err = o.errors.addButtonError
  8043. ;
  8044. if (!$E.length) { // element not found
  8045. $.layout.msg(err +" "+ o.errors.selector +": "+ selector, true);
  8046. }
  8047. else if ($.inArray(pane, $.layout.config.borderPanes) < 0) { // invalid 'pane' sepecified
  8048. $.layout.msg(err +" "+ o.errors.pane +": "+ pane, true);
  8049. $E = $(""); // NO BUTTON
  8050. }
  8051. else { // VALID
  8052. var btn = o[pane].buttonClass +"-"+ action;
  8053. $E .addClass( btn +" "+ btn +"-"+ pane )
  8054. .data("layoutName", o.name); // add layout identifier - even if blank!
  8055. }
  8056. return $E;
  8057. }
  8058. /**
  8059. * NEW syntax for binding layout-buttons - will eventually replace addToggle, addOpen, etc.
  8060. *
  8061. * @param {Object} inst Layout Instance object
  8062. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  8063. * @param {string} action
  8064. * @param {string} pane
  8065. */
  8066. , bind: function (inst, selector, action, pane) {
  8067. var _ = $.layout.buttons;
  8068. switch (action.toLowerCase()) {
  8069. case "toggle": _.addToggle (inst, selector, pane); break;
  8070. case "open": _.addOpen (inst, selector, pane); break;
  8071. case "close": _.addClose (inst, selector, pane); break;
  8072. case "pin": _.addPin (inst, selector, pane); break;
  8073. case "toggle-slide": _.addToggle (inst, selector, pane, true); break;
  8074. case "open-slide": _.addOpen (inst, selector, pane, true); break;
  8075. }
  8076. return inst;
  8077. }
  8078. /**
  8079. * Add a custom Toggler button for a pane
  8080. *
  8081. * @param {Object} inst Layout Instance object
  8082. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  8083. * @param {string} pane Name of the pane the button is for: 'north', 'south', etc.
  8084. * @param {boolean=} slide true = slide-open, false = pin-open
  8085. */
  8086. , addToggle: function (inst, selector, pane, slide) {
  8087. $.layout.buttons.get(inst, selector, pane, "toggle")
  8088. .click(function(evt){
  8089. inst.toggle(pane, !!slide);
  8090. evt.stopPropagation();
  8091. });
  8092. return inst;
  8093. }
  8094. /**
  8095. * Add a custom Open button for a pane
  8096. *
  8097. * @param {Object} inst Layout Instance object
  8098. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  8099. * @param {string} pane Name of the pane the button is for: 'north', 'south', etc.
  8100. * @param {boolean=} slide true = slide-open, false = pin-open
  8101. */
  8102. , addOpen: function (inst, selector, pane, slide) {
  8103. $.layout.buttons.get(inst, selector, pane, "open")
  8104. .attr("title", inst.options[pane].tips.Open)
  8105. .click(function (evt) {
  8106. inst.open(pane, !!slide);
  8107. evt.stopPropagation();
  8108. });
  8109. return inst;
  8110. }
  8111. /**
  8112. * Add a custom Close button for a pane
  8113. *
  8114. * @param {Object} inst Layout Instance object
  8115. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  8116. * @param {string} pane Name of the pane the button is for: 'north', 'south', etc.
  8117. */
  8118. , addClose: function (inst, selector, pane) {
  8119. $.layout.buttons.get(inst, selector, pane, "close")
  8120. .attr("title", inst.options[pane].tips.Close)
  8121. .click(function (evt) {
  8122. inst.close(pane);
  8123. evt.stopPropagation();
  8124. });
  8125. return inst;
  8126. }
  8127. /**
  8128. * Add a custom Pin button for a pane
  8129. *
  8130. * Four classes are added to the element, based on the paneClass for the associated pane...
  8131. * Assuming the default paneClass and the pin is 'up', these classes are added for a west-pane pin:
  8132. * - ui-layout-pane-pin
  8133. * - ui-layout-pane-west-pin
  8134. * - ui-layout-pane-pin-up
  8135. * - ui-layout-pane-west-pin-up
  8136. *
  8137. * @param {Object} inst Layout Instance object
  8138. * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
  8139. * @param {string} pane Name of the pane the pin is for: 'north', 'south', etc.
  8140. */
  8141. , addPin: function (inst, selector, pane) {
  8142. var _ = $.layout.buttons
  8143. , $E = _.get(inst, selector, pane, "pin");
  8144. if ($E.length) {
  8145. var s = inst.state[pane];
  8146. $E.click(function (evt) {
  8147. _.setPinState(inst, $(this), pane, (s.isSliding || s.isClosed));
  8148. if (s.isSliding || s.isClosed) inst.open( pane ); // change from sliding to open
  8149. else inst.close( pane ); // slide-closed
  8150. evt.stopPropagation();
  8151. });
  8152. // add up/down pin attributes and classes
  8153. _.setPinState(inst, $E, pane, (!s.isClosed && !s.isSliding));
  8154. // add this pin to the pane data so we can 'sync it' automatically
  8155. // PANE.pins key is an array so we can store multiple pins for each pane
  8156. s.pins.push( selector ); // just save the selector string
  8157. }
  8158. return inst;
  8159. }
  8160. /**
  8161. * Change the class of the pin button to make it look 'up' or 'down'
  8162. *
  8163. * @see addPin(), syncPins()
  8164. *
  8165. * @param {Object} inst Layout Instance object
  8166. * @param {Array.<Object>} $Pin The pin-span element in a jQuery wrapper
  8167. * @param {string} pane These are the params returned to callbacks by layout()
  8168. * @param {boolean} doPin true = set the pin 'down', false = set it 'up'
  8169. */
  8170. , setPinState: function (inst, $Pin, pane, doPin) {
  8171. var updown = $Pin.attr("pin");
  8172. if (updown && doPin === (updown=="down")) return; // already in correct state
  8173. var
  8174. o = inst.options[pane]
  8175. , pin = o.buttonClass +"-pin"
  8176. , side = pin +"-"+ pane
  8177. , UP = pin +"-up "+ side +"-up"
  8178. , DN = pin +"-down "+side +"-down"
  8179. ;
  8180. $Pin
  8181. .attr("pin", doPin ? "down" : "up") // logic
  8182. .attr("title", doPin ? o.tips.Unpin : o.tips.Pin)
  8183. .removeClass( doPin ? UP : DN )
  8184. .addClass( doPin ? DN : UP )
  8185. ;
  8186. }
  8187. /**
  8188. * INTERNAL function to sync 'pin buttons' when pane is opened or closed
  8189. * Unpinned means the pane is 'sliding' - ie, over-top of the adjacent panes
  8190. *
  8191. * @see open(), close()
  8192. *
  8193. * @param {Object} inst Layout Instance object
  8194. * @param {string} pane These are the params returned to callbacks by layout()
  8195. * @param {boolean} doPin True means set the pin 'down', False means 'up'
  8196. */
  8197. , syncPinBtns: function (inst, pane, doPin) {
  8198. // REAL METHOD IS _INSIDE_ LAYOUT - THIS IS HERE JUST FOR REFERENCE
  8199. $.each(inst.state[pane].pins, function (i, selector) {
  8200. $.layout.buttons.setPinState(inst, $(selector), pane, doPin);
  8201. });
  8202. }
  8203. , _load: function (inst) {
  8204. var _ = $.layout.buttons;
  8205. // ADD Button methods to Layout Instance
  8206. // Note: sel = jQuery Selector string
  8207. $.extend( inst, {
  8208. bindButton: function (sel, action, pane) { return _.bind(inst, sel, action, pane); }
  8209. // DEPRECATED METHODS
  8210. , addToggleBtn: function (sel, pane, slide) { return _.addToggle(inst, sel, pane, slide); }
  8211. , addOpenBtn: function (sel, pane, slide) { return _.addOpen(inst, sel, pane, slide); }
  8212. , addCloseBtn: function (sel, pane) { return _.addClose(inst, sel, pane); }
  8213. , addPinBtn: function (sel, pane) { return _.addPin(inst, sel, pane); }
  8214. });
  8215. // init state array to hold pin-buttons
  8216. for (var i=0; i<4; i++) {
  8217. var pane = $.layout.config.borderPanes[i];
  8218. inst.state[pane].pins = [];
  8219. }
  8220. // auto-init buttons onLoad if option is enabled
  8221. if ( inst.options.autoBindCustomButtons )
  8222. _.init(inst);
  8223. }
  8224. , _unload: function (inst) {
  8225. // TODO: unbind all buttons???
  8226. }
  8227. };
  8228. // add initialization method to Layout's onLoad array of functions
  8229. $.layout.onLoad.push( $.layout.buttons._load );
  8230. //$.layout.onUnload.push( $.layout.buttons._unload );
  8231. /**
  8232. * jquery.layout.browserZoom 1.0
  8233. * $Date: 2011-12-29 08:00:00 (Thu, 29 Dec 2011) $
  8234. *
  8235. * Copyright (c) 2012
  8236. * Kevin Dalman (http://allpro.net)
  8237. *
  8238. * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
  8239. * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
  8240. *
  8241. * @requires: UI Layout 1.3.0.rc30.1 or higher
  8242. *
  8243. * @see: http://groups.google.com/group/jquery-ui-layout
  8244. *
  8245. * TODO: Extend logic to handle other problematic zooming in browsers
  8246. * TODO: Add hotkey/mousewheel bindings to _instantly_ respond to these zoom event
  8247. */
  8248. // tell Layout that the plugin is available
  8249. $.layout.plugins.browserZoom = true;
  8250. $.layout.defaults.browserZoomCheckInterval = 1000;
  8251. $.layout.optionsMap.layout.push("browserZoomCheckInterval");
  8252. /*
  8253. * browserZoom methods
  8254. */
  8255. $.layout.browserZoom = {
  8256. _init: function (inst) {
  8257. // abort if browser does not need this check
  8258. if ($.layout.browserZoom.ratio() !== false)
  8259. $.layout.browserZoom._setTimer(inst);
  8260. }
  8261. , _setTimer: function (inst) {
  8262. // abort if layout destroyed or browser does not need this check
  8263. if (inst.destroyed) return;
  8264. var o = inst.options
  8265. , s = inst.state
  8266. // don't need check if inst has parentLayout, but check occassionally in case parent destroyed!
  8267. // MINIMUM 100ms interval, for performance
  8268. , ms = inst.hasParentLayout ? 5000 : Math.max( o.browserZoomCheckInterval, 100 )
  8269. ;
  8270. // set the timer
  8271. setTimeout(function(){
  8272. if (inst.destroyed || !o.resizeWithWindow) return;
  8273. var d = $.layout.browserZoom.ratio();
  8274. if (d !== s.browserZoom) {
  8275. s.browserZoom = d;
  8276. inst.resizeAll();
  8277. }
  8278. // set a NEW timeout
  8279. $.layout.browserZoom._setTimer(inst);
  8280. }
  8281. , ms );
  8282. }
  8283. , ratio: function () {
  8284. var w = window
  8285. , s = screen
  8286. , d = document
  8287. , dE = d.documentElement || d.body
  8288. , b = $.layout.browser
  8289. , v = b.version
  8290. , r, sW, cW
  8291. ;
  8292. // we can ignore all browsers that fire window.resize event onZoom
  8293. if ((b.msie && v > 8)
  8294. || !b.msie
  8295. ) return false; // don't need to track zoom
  8296. if (s.deviceXDPI && s.systemXDPI) // syntax compiler hack
  8297. return calc(s.deviceXDPI, s.systemXDPI);
  8298. // everything below is just for future reference!
  8299. if (b.webkit && (r = d.body.getBoundingClientRect))
  8300. return calc((r.left - r.right), d.body.offsetWidth);
  8301. if (b.webkit && (sW = w.outerWidth))
  8302. return calc(sW, w.innerWidth);
  8303. if ((sW = s.width) && (cW = dE.clientWidth))
  8304. return calc(sW, cW);
  8305. return false; // no match, so cannot - or don't need to - track zoom
  8306. function calc (x,y) { return (parseInt(x,10) / parseInt(y,10) * 100).toFixed(); }
  8307. }
  8308. };
  8309. // add initialization method to Layout's onLoad array of functions
  8310. $.layout.onReady.push( $.layout.browserZoom._init );
  8311. })( jQuery );