jquery.fancytree-all-deps.js 374 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596
  1. /*! jQuery Fancytree Plugin - 2.28.1 - 2018-03-19T06:47:37Z
  2. * https://github.com/mar10/fancytree
  3. * Copyright (c) 2018 Martin Wendt; Licensed MIT
  4. */
  5. /*! jQuery UI - v1.12.1 - 2017-02-23
  6. * http://jqueryui.com
  7. * Includes: widget.js, position.js, keycode.js, scroll-parent.js, unique-id.js, effect.js, effects/effect-blind.js
  8. * Copyright jQuery Foundation and other contributors; Licensed MIT */
  9. /*
  10. NOTE: Original jQuery UI wrapper was replaced with a simple IIFE.
  11. See README-Fancytree.md
  12. */
  13. (function( $ ) {
  14. $.ui = $.ui || {};
  15. var version = $.ui.version = "1.12.1";
  16. /*!
  17. * jQuery UI Widget 1.12.1
  18. * http://jqueryui.com
  19. *
  20. * Copyright jQuery Foundation and other contributors
  21. * Released under the MIT license.
  22. * http://jquery.org/license
  23. */
  24. //>>label: Widget
  25. //>>group: Core
  26. //>>description: Provides a factory for creating stateful widgets with a common API.
  27. //>>docs: http://api.jqueryui.com/jQuery.widget/
  28. //>>demos: http://jqueryui.com/widget/
  29. var widgetUuid = 0;
  30. var widgetSlice = Array.prototype.slice;
  31. $.cleanData = ( function( orig ) {
  32. return function( elems ) {
  33. var events, elem, i;
  34. for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) {
  35. try {
  36. // Only trigger remove when necessary to save time
  37. events = $._data( elem, "events" );
  38. if ( events && events.remove ) {
  39. $( elem ).triggerHandler( "remove" );
  40. }
  41. // Http://bugs.jquery.com/ticket/8235
  42. } catch ( e ) {}
  43. }
  44. orig( elems );
  45. };
  46. } )( $.cleanData );
  47. $.widget = function( name, base, prototype ) {
  48. var existingConstructor, constructor, basePrototype;
  49. // ProxiedPrototype allows the provided prototype to remain unmodified
  50. // so that it can be used as a mixin for multiple widgets (#8876)
  51. var proxiedPrototype = {};
  52. var namespace = name.split( "." )[ 0 ];
  53. name = name.split( "." )[ 1 ];
  54. var fullName = namespace + "-" + name;
  55. if ( !prototype ) {
  56. prototype = base;
  57. base = $.Widget;
  58. }
  59. if ( $.isArray( prototype ) ) {
  60. prototype = $.extend.apply( null, [ {} ].concat( prototype ) );
  61. }
  62. // Create selector for plugin
  63. $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
  64. return !!$.data( elem, fullName );
  65. };
  66. $[ namespace ] = $[ namespace ] || {};
  67. existingConstructor = $[ namespace ][ name ];
  68. constructor = $[ namespace ][ name ] = function( options, element ) {
  69. // Allow instantiation without "new" keyword
  70. if ( !this._createWidget ) {
  71. return new constructor( options, element );
  72. }
  73. // Allow instantiation without initializing for simple inheritance
  74. // must use "new" keyword (the code above always passes args)
  75. if ( arguments.length ) {
  76. this._createWidget( options, element );
  77. }
  78. };
  79. // Extend with the existing constructor to carry over any static properties
  80. $.extend( constructor, existingConstructor, {
  81. version: prototype.version,
  82. // Copy the object used to create the prototype in case we need to
  83. // redefine the widget later
  84. _proto: $.extend( {}, prototype ),
  85. // Track widgets that inherit from this widget in case this widget is
  86. // redefined after a widget inherits from it
  87. _childConstructors: []
  88. } );
  89. basePrototype = new base();
  90. // We need to make the options hash a property directly on the new instance
  91. // otherwise we'll modify the options hash on the prototype that we're
  92. // inheriting from
  93. basePrototype.options = $.widget.extend( {}, basePrototype.options );
  94. $.each( prototype, function( prop, value ) {
  95. if ( !$.isFunction( value ) ) {
  96. proxiedPrototype[ prop ] = value;
  97. return;
  98. }
  99. proxiedPrototype[ prop ] = ( function() {
  100. function _super() {
  101. return base.prototype[ prop ].apply( this, arguments );
  102. }
  103. function _superApply( args ) {
  104. return base.prototype[ prop ].apply( this, args );
  105. }
  106. return function() {
  107. var __super = this._super;
  108. var __superApply = this._superApply;
  109. var returnValue;
  110. this._super = _super;
  111. this._superApply = _superApply;
  112. returnValue = value.apply( this, arguments );
  113. this._super = __super;
  114. this._superApply = __superApply;
  115. return returnValue;
  116. };
  117. } )();
  118. } );
  119. constructor.prototype = $.widget.extend( basePrototype, {
  120. // TODO: remove support for widgetEventPrefix
  121. // always use the name + a colon as the prefix, e.g., draggable:start
  122. // don't prefix for widgets that aren't DOM-based
  123. widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name
  124. }, proxiedPrototype, {
  125. constructor: constructor,
  126. namespace: namespace,
  127. widgetName: name,
  128. widgetFullName: fullName
  129. } );
  130. // If this widget is being redefined then we need to find all widgets that
  131. // are inheriting from it and redefine all of them so that they inherit from
  132. // the new version of this widget. We're essentially trying to replace one
  133. // level in the prototype chain.
  134. if ( existingConstructor ) {
  135. $.each( existingConstructor._childConstructors, function( i, child ) {
  136. var childPrototype = child.prototype;
  137. // Redefine the child widget using the same prototype that was
  138. // originally used, but inherit from the new version of the base
  139. $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor,
  140. child._proto );
  141. } );
  142. // Remove the list of existing child constructors from the old constructor
  143. // so the old child constructors can be garbage collected
  144. delete existingConstructor._childConstructors;
  145. } else {
  146. base._childConstructors.push( constructor );
  147. }
  148. $.widget.bridge( name, constructor );
  149. return constructor;
  150. };
  151. $.widget.extend = function( target ) {
  152. var input = widgetSlice.call( arguments, 1 );
  153. var inputIndex = 0;
  154. var inputLength = input.length;
  155. var key;
  156. var value;
  157. for ( ; inputIndex < inputLength; inputIndex++ ) {
  158. for ( key in input[ inputIndex ] ) {
  159. value = input[ inputIndex ][ key ];
  160. if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
  161. // Clone objects
  162. if ( $.isPlainObject( value ) ) {
  163. target[ key ] = $.isPlainObject( target[ key ] ) ?
  164. $.widget.extend( {}, target[ key ], value ) :
  165. // Don't extend strings, arrays, etc. with objects
  166. $.widget.extend( {}, value );
  167. // Copy everything else by reference
  168. } else {
  169. target[ key ] = value;
  170. }
  171. }
  172. }
  173. }
  174. return target;
  175. };
  176. $.widget.bridge = function( name, object ) {
  177. var fullName = object.prototype.widgetFullName || name;
  178. $.fn[ name ] = function( options ) {
  179. var isMethodCall = typeof options === "string";
  180. var args = widgetSlice.call( arguments, 1 );
  181. var returnValue = this;
  182. if ( isMethodCall ) {
  183. // If this is an empty collection, we need to have the instance method
  184. // return undefined instead of the jQuery instance
  185. if ( !this.length && options === "instance" ) {
  186. returnValue = undefined;
  187. } else {
  188. this.each( function() {
  189. var methodValue;
  190. var instance = $.data( this, fullName );
  191. if ( options === "instance" ) {
  192. returnValue = instance;
  193. return false;
  194. }
  195. if ( !instance ) {
  196. return $.error( "cannot call methods on " + name +
  197. " prior to initialization; " +
  198. "attempted to call method '" + options + "'" );
  199. }
  200. if ( !$.isFunction( instance[ options ] ) || options.charAt( 0 ) === "_" ) {
  201. return $.error( "no such method '" + options + "' for " + name +
  202. " widget instance" );
  203. }
  204. methodValue = instance[ options ].apply( instance, args );
  205. if ( methodValue !== instance && methodValue !== undefined ) {
  206. returnValue = methodValue && methodValue.jquery ?
  207. returnValue.pushStack( methodValue.get() ) :
  208. methodValue;
  209. return false;
  210. }
  211. } );
  212. }
  213. } else {
  214. // Allow multiple hashes to be passed on init
  215. if ( args.length ) {
  216. options = $.widget.extend.apply( null, [ options ].concat( args ) );
  217. }
  218. this.each( function() {
  219. var instance = $.data( this, fullName );
  220. if ( instance ) {
  221. instance.option( options || {} );
  222. if ( instance._init ) {
  223. instance._init();
  224. }
  225. } else {
  226. $.data( this, fullName, new object( options, this ) );
  227. }
  228. } );
  229. }
  230. return returnValue;
  231. };
  232. };
  233. $.Widget = function( /* options, element */ ) {};
  234. $.Widget._childConstructors = [];
  235. $.Widget.prototype = {
  236. widgetName: "widget",
  237. widgetEventPrefix: "",
  238. defaultElement: "<div>",
  239. options: {
  240. classes: {},
  241. disabled: false,
  242. // Callbacks
  243. create: null
  244. },
  245. _createWidget: function( options, element ) {
  246. element = $( element || this.defaultElement || this )[ 0 ];
  247. this.element = $( element );
  248. this.uuid = widgetUuid++;
  249. this.eventNamespace = "." + this.widgetName + this.uuid;
  250. this.bindings = $();
  251. this.hoverable = $();
  252. this.focusable = $();
  253. this.classesElementLookup = {};
  254. if ( element !== this ) {
  255. $.data( element, this.widgetFullName, this );
  256. this._on( true, this.element, {
  257. remove: function( event ) {
  258. if ( event.target === element ) {
  259. this.destroy();
  260. }
  261. }
  262. } );
  263. this.document = $( element.style ?
  264. // Element within the document
  265. element.ownerDocument :
  266. // Element is window or document
  267. element.document || element );
  268. this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow );
  269. }
  270. this.options = $.widget.extend( {},
  271. this.options,
  272. this._getCreateOptions(),
  273. options );
  274. this._create();
  275. if ( this.options.disabled ) {
  276. this._setOptionDisabled( this.options.disabled );
  277. }
  278. this._trigger( "create", null, this._getCreateEventData() );
  279. this._init();
  280. },
  281. _getCreateOptions: function() {
  282. return {};
  283. },
  284. _getCreateEventData: $.noop,
  285. _create: $.noop,
  286. _init: $.noop,
  287. destroy: function() {
  288. var that = this;
  289. this._destroy();
  290. $.each( this.classesElementLookup, function( key, value ) {
  291. that._removeClass( value, key );
  292. } );
  293. // We can probably remove the unbind calls in 2.0
  294. // all event bindings should go through this._on()
  295. this.element
  296. .off( this.eventNamespace )
  297. .removeData( this.widgetFullName );
  298. this.widget()
  299. .off( this.eventNamespace )
  300. .removeAttr( "aria-disabled" );
  301. // Clean up events and states
  302. this.bindings.off( this.eventNamespace );
  303. },
  304. _destroy: $.noop,
  305. widget: function() {
  306. return this.element;
  307. },
  308. option: function( key, value ) {
  309. var options = key;
  310. var parts;
  311. var curOption;
  312. var i;
  313. if ( arguments.length === 0 ) {
  314. // Don't return a reference to the internal hash
  315. return $.widget.extend( {}, this.options );
  316. }
  317. if ( typeof key === "string" ) {
  318. // Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
  319. options = {};
  320. parts = key.split( "." );
  321. key = parts.shift();
  322. if ( parts.length ) {
  323. curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
  324. for ( i = 0; i < parts.length - 1; i++ ) {
  325. curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
  326. curOption = curOption[ parts[ i ] ];
  327. }
  328. key = parts.pop();
  329. if ( arguments.length === 1 ) {
  330. return curOption[ key ] === undefined ? null : curOption[ key ];
  331. }
  332. curOption[ key ] = value;
  333. } else {
  334. if ( arguments.length === 1 ) {
  335. return this.options[ key ] === undefined ? null : this.options[ key ];
  336. }
  337. options[ key ] = value;
  338. }
  339. }
  340. this._setOptions( options );
  341. return this;
  342. },
  343. _setOptions: function( options ) {
  344. var key;
  345. for ( key in options ) {
  346. this._setOption( key, options[ key ] );
  347. }
  348. return this;
  349. },
  350. _setOption: function( key, value ) {
  351. if ( key === "classes" ) {
  352. this._setOptionClasses( value );
  353. }
  354. this.options[ key ] = value;
  355. if ( key === "disabled" ) {
  356. this._setOptionDisabled( value );
  357. }
  358. return this;
  359. },
  360. _setOptionClasses: function( value ) {
  361. var classKey, elements, currentElements;
  362. for ( classKey in value ) {
  363. currentElements = this.classesElementLookup[ classKey ];
  364. if ( value[ classKey ] === this.options.classes[ classKey ] ||
  365. !currentElements ||
  366. !currentElements.length ) {
  367. continue;
  368. }
  369. // We are doing this to create a new jQuery object because the _removeClass() call
  370. // on the next line is going to destroy the reference to the current elements being
  371. // tracked. We need to save a copy of this collection so that we can add the new classes
  372. // below.
  373. elements = $( currentElements.get() );
  374. this._removeClass( currentElements, classKey );
  375. // We don't use _addClass() here, because that uses this.options.classes
  376. // for generating the string of classes. We want to use the value passed in from
  377. // _setOption(), this is the new value of the classes option which was passed to
  378. // _setOption(). We pass this value directly to _classes().
  379. elements.addClass( this._classes( {
  380. element: elements,
  381. keys: classKey,
  382. classes: value,
  383. add: true
  384. } ) );
  385. }
  386. },
  387. _setOptionDisabled: function( value ) {
  388. this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value );
  389. // If the widget is becoming disabled, then nothing is interactive
  390. if ( value ) {
  391. this._removeClass( this.hoverable, null, "ui-state-hover" );
  392. this._removeClass( this.focusable, null, "ui-state-focus" );
  393. }
  394. },
  395. enable: function() {
  396. return this._setOptions( { disabled: false } );
  397. },
  398. disable: function() {
  399. return this._setOptions( { disabled: true } );
  400. },
  401. _classes: function( options ) {
  402. var full = [];
  403. var that = this;
  404. options = $.extend( {
  405. element: this.element,
  406. classes: this.options.classes || {}
  407. }, options );
  408. function processClassString( classes, checkOption ) {
  409. var current, i;
  410. for ( i = 0; i < classes.length; i++ ) {
  411. current = that.classesElementLookup[ classes[ i ] ] || $();
  412. if ( options.add ) {
  413. current = $( $.unique( current.get().concat( options.element.get() ) ) );
  414. } else {
  415. current = $( current.not( options.element ).get() );
  416. }
  417. that.classesElementLookup[ classes[ i ] ] = current;
  418. full.push( classes[ i ] );
  419. if ( checkOption && options.classes[ classes[ i ] ] ) {
  420. full.push( options.classes[ classes[ i ] ] );
  421. }
  422. }
  423. }
  424. this._on( options.element, {
  425. "remove": "_untrackClassesElement"
  426. } );
  427. if ( options.keys ) {
  428. processClassString( options.keys.match( /\S+/g ) || [], true );
  429. }
  430. if ( options.extra ) {
  431. processClassString( options.extra.match( /\S+/g ) || [] );
  432. }
  433. return full.join( " " );
  434. },
  435. _untrackClassesElement: function( event ) {
  436. var that = this;
  437. $.each( that.classesElementLookup, function( key, value ) {
  438. if ( $.inArray( event.target, value ) !== -1 ) {
  439. that.classesElementLookup[ key ] = $( value.not( event.target ).get() );
  440. }
  441. } );
  442. },
  443. _removeClass: function( element, keys, extra ) {
  444. return this._toggleClass( element, keys, extra, false );
  445. },
  446. _addClass: function( element, keys, extra ) {
  447. return this._toggleClass( element, keys, extra, true );
  448. },
  449. _toggleClass: function( element, keys, extra, add ) {
  450. add = ( typeof add === "boolean" ) ? add : extra;
  451. var shift = ( typeof element === "string" || element === null ),
  452. options = {
  453. extra: shift ? keys : extra,
  454. keys: shift ? element : keys,
  455. element: shift ? this.element : element,
  456. add: add
  457. };
  458. options.element.toggleClass( this._classes( options ), add );
  459. return this;
  460. },
  461. _on: function( suppressDisabledCheck, element, handlers ) {
  462. var delegateElement;
  463. var instance = this;
  464. // No suppressDisabledCheck flag, shuffle arguments
  465. if ( typeof suppressDisabledCheck !== "boolean" ) {
  466. handlers = element;
  467. element = suppressDisabledCheck;
  468. suppressDisabledCheck = false;
  469. }
  470. // No element argument, shuffle and use this.element
  471. if ( !handlers ) {
  472. handlers = element;
  473. element = this.element;
  474. delegateElement = this.widget();
  475. } else {
  476. element = delegateElement = $( element );
  477. this.bindings = this.bindings.add( element );
  478. }
  479. $.each( handlers, function( event, handler ) {
  480. function handlerProxy() {
  481. // Allow widgets to customize the disabled handling
  482. // - disabled as an array instead of boolean
  483. // - disabled class as method for disabling individual parts
  484. if ( !suppressDisabledCheck &&
  485. ( instance.options.disabled === true ||
  486. $( this ).hasClass( "ui-state-disabled" ) ) ) {
  487. return;
  488. }
  489. return ( typeof handler === "string" ? instance[ handler ] : handler )
  490. .apply( instance, arguments );
  491. }
  492. // Copy the guid so direct unbinding works
  493. if ( typeof handler !== "string" ) {
  494. handlerProxy.guid = handler.guid =
  495. handler.guid || handlerProxy.guid || $.guid++;
  496. }
  497. var match = event.match( /^([\w:-]*)\s*(.*)$/ );
  498. var eventName = match[ 1 ] + instance.eventNamespace;
  499. var selector = match[ 2 ];
  500. if ( selector ) {
  501. delegateElement.on( eventName, selector, handlerProxy );
  502. } else {
  503. element.on( eventName, handlerProxy );
  504. }
  505. } );
  506. },
  507. _off: function( element, eventName ) {
  508. eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) +
  509. this.eventNamespace;
  510. element.off( eventName ).off( eventName );
  511. // Clear the stack to avoid memory leaks (#10056)
  512. this.bindings = $( this.bindings.not( element ).get() );
  513. this.focusable = $( this.focusable.not( element ).get() );
  514. this.hoverable = $( this.hoverable.not( element ).get() );
  515. },
  516. _delay: function( handler, delay ) {
  517. function handlerProxy() {
  518. return ( typeof handler === "string" ? instance[ handler ] : handler )
  519. .apply( instance, arguments );
  520. }
  521. var instance = this;
  522. return setTimeout( handlerProxy, delay || 0 );
  523. },
  524. _hoverable: function( element ) {
  525. this.hoverable = this.hoverable.add( element );
  526. this._on( element, {
  527. mouseenter: function( event ) {
  528. this._addClass( $( event.currentTarget ), null, "ui-state-hover" );
  529. },
  530. mouseleave: function( event ) {
  531. this._removeClass( $( event.currentTarget ), null, "ui-state-hover" );
  532. }
  533. } );
  534. },
  535. _focusable: function( element ) {
  536. this.focusable = this.focusable.add( element );
  537. this._on( element, {
  538. focusin: function( event ) {
  539. this._addClass( $( event.currentTarget ), null, "ui-state-focus" );
  540. },
  541. focusout: function( event ) {
  542. this._removeClass( $( event.currentTarget ), null, "ui-state-focus" );
  543. }
  544. } );
  545. },
  546. _trigger: function( type, event, data ) {
  547. var prop, orig;
  548. var callback = this.options[ type ];
  549. data = data || {};
  550. event = $.Event( event );
  551. event.type = ( type === this.widgetEventPrefix ?
  552. type :
  553. this.widgetEventPrefix + type ).toLowerCase();
  554. // The original event may come from any element
  555. // so we need to reset the target on the new event
  556. event.target = this.element[ 0 ];
  557. // Copy original event properties over to the new event
  558. orig = event.originalEvent;
  559. if ( orig ) {
  560. for ( prop in orig ) {
  561. if ( !( prop in event ) ) {
  562. event[ prop ] = orig[ prop ];
  563. }
  564. }
  565. }
  566. this.element.trigger( event, data );
  567. return !( $.isFunction( callback ) &&
  568. callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false ||
  569. event.isDefaultPrevented() );
  570. }
  571. };
  572. $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
  573. $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
  574. if ( typeof options === "string" ) {
  575. options = { effect: options };
  576. }
  577. var hasOptions;
  578. var effectName = !options ?
  579. method :
  580. options === true || typeof options === "number" ?
  581. defaultEffect :
  582. options.effect || defaultEffect;
  583. options = options || {};
  584. if ( typeof options === "number" ) {
  585. options = { duration: options };
  586. }
  587. hasOptions = !$.isEmptyObject( options );
  588. options.complete = callback;
  589. if ( options.delay ) {
  590. element.delay( options.delay );
  591. }
  592. if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
  593. element[ method ]( options );
  594. } else if ( effectName !== method && element[ effectName ] ) {
  595. element[ effectName ]( options.duration, options.easing, callback );
  596. } else {
  597. element.queue( function( next ) {
  598. $( this )[ method ]();
  599. if ( callback ) {
  600. callback.call( element[ 0 ] );
  601. }
  602. next();
  603. } );
  604. }
  605. };
  606. } );
  607. var widget = $.widget;
  608. /*!
  609. * jQuery UI Position 1.12.1
  610. * http://jqueryui.com
  611. *
  612. * Copyright jQuery Foundation and other contributors
  613. * Released under the MIT license.
  614. * http://jquery.org/license
  615. *
  616. * http://api.jqueryui.com/position/
  617. */
  618. //>>label: Position
  619. //>>group: Core
  620. //>>description: Positions elements relative to other elements.
  621. //>>docs: http://api.jqueryui.com/position/
  622. //>>demos: http://jqueryui.com/position/
  623. ( function() {
  624. var cachedScrollbarWidth,
  625. max = Math.max,
  626. abs = Math.abs,
  627. rhorizontal = /left|center|right/,
  628. rvertical = /top|center|bottom/,
  629. roffset = /[\+\-]\d+(\.[\d]+)?%?/,
  630. rposition = /^\w+/,
  631. rpercent = /%$/,
  632. _position = $.fn.position;
  633. function getOffsets( offsets, width, height ) {
  634. return [
  635. parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
  636. parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
  637. ];
  638. }
  639. function parseCss( element, property ) {
  640. return parseInt( $.css( element, property ), 10 ) || 0;
  641. }
  642. function getDimensions( elem ) {
  643. var raw = elem[ 0 ];
  644. if ( raw.nodeType === 9 ) {
  645. return {
  646. width: elem.width(),
  647. height: elem.height(),
  648. offset: { top: 0, left: 0 }
  649. };
  650. }
  651. if ( $.isWindow( raw ) ) {
  652. return {
  653. width: elem.width(),
  654. height: elem.height(),
  655. offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
  656. };
  657. }
  658. if ( raw.preventDefault ) {
  659. return {
  660. width: 0,
  661. height: 0,
  662. offset: { top: raw.pageY, left: raw.pageX }
  663. };
  664. }
  665. return {
  666. width: elem.outerWidth(),
  667. height: elem.outerHeight(),
  668. offset: elem.offset()
  669. };
  670. }
  671. $.position = {
  672. scrollbarWidth: function() {
  673. if ( cachedScrollbarWidth !== undefined ) {
  674. return cachedScrollbarWidth;
  675. }
  676. var w1, w2,
  677. div = $( "<div " +
  678. "style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'>" +
  679. "<div style='height:100px;width:auto;'></div></div>" ),
  680. innerDiv = div.children()[ 0 ];
  681. $( "body" ).append( div );
  682. w1 = innerDiv.offsetWidth;
  683. div.css( "overflow", "scroll" );
  684. w2 = innerDiv.offsetWidth;
  685. if ( w1 === w2 ) {
  686. w2 = div[ 0 ].clientWidth;
  687. }
  688. div.remove();
  689. return ( cachedScrollbarWidth = w1 - w2 );
  690. },
  691. getScrollInfo: function( within ) {
  692. var overflowX = within.isWindow || within.isDocument ? "" :
  693. within.element.css( "overflow-x" ),
  694. overflowY = within.isWindow || within.isDocument ? "" :
  695. within.element.css( "overflow-y" ),
  696. hasOverflowX = overflowX === "scroll" ||
  697. ( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ),
  698. hasOverflowY = overflowY === "scroll" ||
  699. ( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight );
  700. return {
  701. width: hasOverflowY ? $.position.scrollbarWidth() : 0,
  702. height: hasOverflowX ? $.position.scrollbarWidth() : 0
  703. };
  704. },
  705. getWithinInfo: function( element ) {
  706. var withinElement = $( element || window ),
  707. isWindow = $.isWindow( withinElement[ 0 ] ),
  708. isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9,
  709. hasOffset = !isWindow && !isDocument;
  710. return {
  711. element: withinElement,
  712. isWindow: isWindow,
  713. isDocument: isDocument,
  714. offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 },
  715. scrollLeft: withinElement.scrollLeft(),
  716. scrollTop: withinElement.scrollTop(),
  717. width: withinElement.outerWidth(),
  718. height: withinElement.outerHeight()
  719. };
  720. }
  721. };
  722. $.fn.position = function( options ) {
  723. if ( !options || !options.of ) {
  724. return _position.apply( this, arguments );
  725. }
  726. // Make a copy, we don't want to modify arguments
  727. options = $.extend( {}, options );
  728. var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
  729. target = $( options.of ),
  730. within = $.position.getWithinInfo( options.within ),
  731. scrollInfo = $.position.getScrollInfo( within ),
  732. collision = ( options.collision || "flip" ).split( " " ),
  733. offsets = {};
  734. dimensions = getDimensions( target );
  735. if ( target[ 0 ].preventDefault ) {
  736. // Force left top to allow flipping
  737. options.at = "left top";
  738. }
  739. targetWidth = dimensions.width;
  740. targetHeight = dimensions.height;
  741. targetOffset = dimensions.offset;
  742. // Clone to reuse original targetOffset later
  743. basePosition = $.extend( {}, targetOffset );
  744. // Force my and at to have valid horizontal and vertical positions
  745. // if a value is missing or invalid, it will be converted to center
  746. $.each( [ "my", "at" ], function() {
  747. var pos = ( options[ this ] || "" ).split( " " ),
  748. horizontalOffset,
  749. verticalOffset;
  750. if ( pos.length === 1 ) {
  751. pos = rhorizontal.test( pos[ 0 ] ) ?
  752. pos.concat( [ "center" ] ) :
  753. rvertical.test( pos[ 0 ] ) ?
  754. [ "center" ].concat( pos ) :
  755. [ "center", "center" ];
  756. }
  757. pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
  758. pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
  759. // Calculate offsets
  760. horizontalOffset = roffset.exec( pos[ 0 ] );
  761. verticalOffset = roffset.exec( pos[ 1 ] );
  762. offsets[ this ] = [
  763. horizontalOffset ? horizontalOffset[ 0 ] : 0,
  764. verticalOffset ? verticalOffset[ 0 ] : 0
  765. ];
  766. // Reduce to just the positions without the offsets
  767. options[ this ] = [
  768. rposition.exec( pos[ 0 ] )[ 0 ],
  769. rposition.exec( pos[ 1 ] )[ 0 ]
  770. ];
  771. } );
  772. // Normalize collision option
  773. if ( collision.length === 1 ) {
  774. collision[ 1 ] = collision[ 0 ];
  775. }
  776. if ( options.at[ 0 ] === "right" ) {
  777. basePosition.left += targetWidth;
  778. } else if ( options.at[ 0 ] === "center" ) {
  779. basePosition.left += targetWidth / 2;
  780. }
  781. if ( options.at[ 1 ] === "bottom" ) {
  782. basePosition.top += targetHeight;
  783. } else if ( options.at[ 1 ] === "center" ) {
  784. basePosition.top += targetHeight / 2;
  785. }
  786. atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
  787. basePosition.left += atOffset[ 0 ];
  788. basePosition.top += atOffset[ 1 ];
  789. return this.each( function() {
  790. var collisionPosition, using,
  791. elem = $( this ),
  792. elemWidth = elem.outerWidth(),
  793. elemHeight = elem.outerHeight(),
  794. marginLeft = parseCss( this, "marginLeft" ),
  795. marginTop = parseCss( this, "marginTop" ),
  796. collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) +
  797. scrollInfo.width,
  798. collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) +
  799. scrollInfo.height,
  800. position = $.extend( {}, basePosition ),
  801. myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
  802. if ( options.my[ 0 ] === "right" ) {
  803. position.left -= elemWidth;
  804. } else if ( options.my[ 0 ] === "center" ) {
  805. position.left -= elemWidth / 2;
  806. }
  807. if ( options.my[ 1 ] === "bottom" ) {
  808. position.top -= elemHeight;
  809. } else if ( options.my[ 1 ] === "center" ) {
  810. position.top -= elemHeight / 2;
  811. }
  812. position.left += myOffset[ 0 ];
  813. position.top += myOffset[ 1 ];
  814. collisionPosition = {
  815. marginLeft: marginLeft,
  816. marginTop: marginTop
  817. };
  818. $.each( [ "left", "top" ], function( i, dir ) {
  819. if ( $.ui.position[ collision[ i ] ] ) {
  820. $.ui.position[ collision[ i ] ][ dir ]( position, {
  821. targetWidth: targetWidth,
  822. targetHeight: targetHeight,
  823. elemWidth: elemWidth,
  824. elemHeight: elemHeight,
  825. collisionPosition: collisionPosition,
  826. collisionWidth: collisionWidth,
  827. collisionHeight: collisionHeight,
  828. offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
  829. my: options.my,
  830. at: options.at,
  831. within: within,
  832. elem: elem
  833. } );
  834. }
  835. } );
  836. if ( options.using ) {
  837. // Adds feedback as second argument to using callback, if present
  838. using = function( props ) {
  839. var left = targetOffset.left - position.left,
  840. right = left + targetWidth - elemWidth,
  841. top = targetOffset.top - position.top,
  842. bottom = top + targetHeight - elemHeight,
  843. feedback = {
  844. target: {
  845. element: target,
  846. left: targetOffset.left,
  847. top: targetOffset.top,
  848. width: targetWidth,
  849. height: targetHeight
  850. },
  851. element: {
  852. element: elem,
  853. left: position.left,
  854. top: position.top,
  855. width: elemWidth,
  856. height: elemHeight
  857. },
  858. horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
  859. vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
  860. };
  861. if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
  862. feedback.horizontal = "center";
  863. }
  864. if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
  865. feedback.vertical = "middle";
  866. }
  867. if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
  868. feedback.important = "horizontal";
  869. } else {
  870. feedback.important = "vertical";
  871. }
  872. options.using.call( this, props, feedback );
  873. };
  874. }
  875. elem.offset( $.extend( position, { using: using } ) );
  876. } );
  877. };
  878. $.ui.position = {
  879. fit: {
  880. left: function( position, data ) {
  881. var within = data.within,
  882. withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
  883. outerWidth = within.width,
  884. collisionPosLeft = position.left - data.collisionPosition.marginLeft,
  885. overLeft = withinOffset - collisionPosLeft,
  886. overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
  887. newOverRight;
  888. // Element is wider than within
  889. if ( data.collisionWidth > outerWidth ) {
  890. // Element is initially over the left side of within
  891. if ( overLeft > 0 && overRight <= 0 ) {
  892. newOverRight = position.left + overLeft + data.collisionWidth - outerWidth -
  893. withinOffset;
  894. position.left += overLeft - newOverRight;
  895. // Element is initially over right side of within
  896. } else if ( overRight > 0 && overLeft <= 0 ) {
  897. position.left = withinOffset;
  898. // Element is initially over both left and right sides of within
  899. } else {
  900. if ( overLeft > overRight ) {
  901. position.left = withinOffset + outerWidth - data.collisionWidth;
  902. } else {
  903. position.left = withinOffset;
  904. }
  905. }
  906. // Too far left -> align with left edge
  907. } else if ( overLeft > 0 ) {
  908. position.left += overLeft;
  909. // Too far right -> align with right edge
  910. } else if ( overRight > 0 ) {
  911. position.left -= overRight;
  912. // Adjust based on position and margin
  913. } else {
  914. position.left = max( position.left - collisionPosLeft, position.left );
  915. }
  916. },
  917. top: function( position, data ) {
  918. var within = data.within,
  919. withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
  920. outerHeight = data.within.height,
  921. collisionPosTop = position.top - data.collisionPosition.marginTop,
  922. overTop = withinOffset - collisionPosTop,
  923. overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
  924. newOverBottom;
  925. // Element is taller than within
  926. if ( data.collisionHeight > outerHeight ) {
  927. // Element is initially over the top of within
  928. if ( overTop > 0 && overBottom <= 0 ) {
  929. newOverBottom = position.top + overTop + data.collisionHeight - outerHeight -
  930. withinOffset;
  931. position.top += overTop - newOverBottom;
  932. // Element is initially over bottom of within
  933. } else if ( overBottom > 0 && overTop <= 0 ) {
  934. position.top = withinOffset;
  935. // Element is initially over both top and bottom of within
  936. } else {
  937. if ( overTop > overBottom ) {
  938. position.top = withinOffset + outerHeight - data.collisionHeight;
  939. } else {
  940. position.top = withinOffset;
  941. }
  942. }
  943. // Too far up -> align with top
  944. } else if ( overTop > 0 ) {
  945. position.top += overTop;
  946. // Too far down -> align with bottom edge
  947. } else if ( overBottom > 0 ) {
  948. position.top -= overBottom;
  949. // Adjust based on position and margin
  950. } else {
  951. position.top = max( position.top - collisionPosTop, position.top );
  952. }
  953. }
  954. },
  955. flip: {
  956. left: function( position, data ) {
  957. var within = data.within,
  958. withinOffset = within.offset.left + within.scrollLeft,
  959. outerWidth = within.width,
  960. offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
  961. collisionPosLeft = position.left - data.collisionPosition.marginLeft,
  962. overLeft = collisionPosLeft - offsetLeft,
  963. overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
  964. myOffset = data.my[ 0 ] === "left" ?
  965. -data.elemWidth :
  966. data.my[ 0 ] === "right" ?
  967. data.elemWidth :
  968. 0,
  969. atOffset = data.at[ 0 ] === "left" ?
  970. data.targetWidth :
  971. data.at[ 0 ] === "right" ?
  972. -data.targetWidth :
  973. 0,
  974. offset = -2 * data.offset[ 0 ],
  975. newOverRight,
  976. newOverLeft;
  977. if ( overLeft < 0 ) {
  978. newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth -
  979. outerWidth - withinOffset;
  980. if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
  981. position.left += myOffset + atOffset + offset;
  982. }
  983. } else if ( overRight > 0 ) {
  984. newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset +
  985. atOffset + offset - offsetLeft;
  986. if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
  987. position.left += myOffset + atOffset + offset;
  988. }
  989. }
  990. },
  991. top: function( position, data ) {
  992. var within = data.within,
  993. withinOffset = within.offset.top + within.scrollTop,
  994. outerHeight = within.height,
  995. offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
  996. collisionPosTop = position.top - data.collisionPosition.marginTop,
  997. overTop = collisionPosTop - offsetTop,
  998. overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
  999. top = data.my[ 1 ] === "top",
  1000. myOffset = top ?
  1001. -data.elemHeight :
  1002. data.my[ 1 ] === "bottom" ?
  1003. data.elemHeight :
  1004. 0,
  1005. atOffset = data.at[ 1 ] === "top" ?
  1006. data.targetHeight :
  1007. data.at[ 1 ] === "bottom" ?
  1008. -data.targetHeight :
  1009. 0,
  1010. offset = -2 * data.offset[ 1 ],
  1011. newOverTop,
  1012. newOverBottom;
  1013. if ( overTop < 0 ) {
  1014. newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight -
  1015. outerHeight - withinOffset;
  1016. if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) {
  1017. position.top += myOffset + atOffset + offset;
  1018. }
  1019. } else if ( overBottom > 0 ) {
  1020. newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset +
  1021. offset - offsetTop;
  1022. if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) {
  1023. position.top += myOffset + atOffset + offset;
  1024. }
  1025. }
  1026. }
  1027. },
  1028. flipfit: {
  1029. left: function() {
  1030. $.ui.position.flip.left.apply( this, arguments );
  1031. $.ui.position.fit.left.apply( this, arguments );
  1032. },
  1033. top: function() {
  1034. $.ui.position.flip.top.apply( this, arguments );
  1035. $.ui.position.fit.top.apply( this, arguments );
  1036. }
  1037. }
  1038. };
  1039. } )();
  1040. var position = $.ui.position;
  1041. /*!
  1042. * jQuery UI Keycode 1.12.1
  1043. * http://jqueryui.com
  1044. *
  1045. * Copyright jQuery Foundation and other contributors
  1046. * Released under the MIT license.
  1047. * http://jquery.org/license
  1048. */
  1049. //>>label: Keycode
  1050. //>>group: Core
  1051. //>>description: Provide keycodes as keynames
  1052. //>>docs: http://api.jqueryui.com/jQuery.ui.keyCode/
  1053. var keycode = $.ui.keyCode = {
  1054. BACKSPACE: 8,
  1055. COMMA: 188,
  1056. DELETE: 46,
  1057. DOWN: 40,
  1058. END: 35,
  1059. ENTER: 13,
  1060. ESCAPE: 27,
  1061. HOME: 36,
  1062. LEFT: 37,
  1063. PAGE_DOWN: 34,
  1064. PAGE_UP: 33,
  1065. PERIOD: 190,
  1066. RIGHT: 39,
  1067. SPACE: 32,
  1068. TAB: 9,
  1069. UP: 38
  1070. };
  1071. /*!
  1072. * jQuery UI Scroll Parent 1.12.1
  1073. * http://jqueryui.com
  1074. *
  1075. * Copyright jQuery Foundation and other contributors
  1076. * Released under the MIT license.
  1077. * http://jquery.org/license
  1078. */
  1079. //>>label: scrollParent
  1080. //>>group: Core
  1081. //>>description: Get the closest ancestor element that is scrollable.
  1082. //>>docs: http://api.jqueryui.com/scrollParent/
  1083. var scrollParent = $.fn.scrollParent = function( includeHidden ) {
  1084. var position = this.css( "position" ),
  1085. excludeStaticParent = position === "absolute",
  1086. overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/,
  1087. scrollParent = this.parents().filter( function() {
  1088. var parent = $( this );
  1089. if ( excludeStaticParent && parent.css( "position" ) === "static" ) {
  1090. return false;
  1091. }
  1092. return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) +
  1093. parent.css( "overflow-x" ) );
  1094. } ).eq( 0 );
  1095. return position === "fixed" || !scrollParent.length ?
  1096. $( this[ 0 ].ownerDocument || document ) :
  1097. scrollParent;
  1098. };
  1099. /*!
  1100. * jQuery UI Unique ID 1.12.1
  1101. * http://jqueryui.com
  1102. *
  1103. * Copyright jQuery Foundation and other contributors
  1104. * Released under the MIT license.
  1105. * http://jquery.org/license
  1106. */
  1107. //>>label: uniqueId
  1108. //>>group: Core
  1109. //>>description: Functions to generate and remove uniqueId's
  1110. //>>docs: http://api.jqueryui.com/uniqueId/
  1111. var uniqueId = $.fn.extend( {
  1112. uniqueId: ( function() {
  1113. var uuid = 0;
  1114. return function() {
  1115. return this.each( function() {
  1116. if ( !this.id ) {
  1117. this.id = "ui-id-" + ( ++uuid );
  1118. }
  1119. } );
  1120. };
  1121. } )(),
  1122. removeUniqueId: function() {
  1123. return this.each( function() {
  1124. if ( /^ui-id-\d+$/.test( this.id ) ) {
  1125. $( this ).removeAttr( "id" );
  1126. }
  1127. } );
  1128. }
  1129. } );
  1130. /*!
  1131. * jQuery UI Effects 1.12.1
  1132. * http://jqueryui.com
  1133. *
  1134. * Copyright jQuery Foundation and other contributors
  1135. * Released under the MIT license.
  1136. * http://jquery.org/license
  1137. */
  1138. //>>label: Effects Core
  1139. //>>group: Effects
  1140. // jscs:disable maximumLineLength
  1141. //>>description: Extends the internal jQuery effects. Includes morphing and easing. Required by all other effects.
  1142. // jscs:enable maximumLineLength
  1143. //>>docs: http://api.jqueryui.com/category/effects-core/
  1144. //>>demos: http://jqueryui.com/effect/
  1145. var dataSpace = "ui-effects-",
  1146. dataSpaceStyle = "ui-effects-style",
  1147. dataSpaceAnimated = "ui-effects-animated",
  1148. // Create a local jQuery because jQuery Color relies on it and the
  1149. // global may not exist with AMD and a custom build (#10199)
  1150. jQuery = $;
  1151. $.effects = {
  1152. effect: {}
  1153. };
  1154. /*!
  1155. * jQuery Color Animations v2.1.2
  1156. * https://github.com/jquery/jquery-color
  1157. *
  1158. * Copyright 2014 jQuery Foundation and other contributors
  1159. * Released under the MIT license.
  1160. * http://jquery.org/license
  1161. *
  1162. * Date: Wed Jan 16 08:47:09 2013 -0600
  1163. */
  1164. ( function( jQuery, undefined ) {
  1165. var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor " +
  1166. "borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
  1167. // Plusequals test for += 100 -= 100
  1168. rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
  1169. // A set of RE's that can match strings and generate color tuples.
  1170. stringParsers = [ {
  1171. re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
  1172. parse: function( execResult ) {
  1173. return [
  1174. execResult[ 1 ],
  1175. execResult[ 2 ],
  1176. execResult[ 3 ],
  1177. execResult[ 4 ]
  1178. ];
  1179. }
  1180. }, {
  1181. re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
  1182. parse: function( execResult ) {
  1183. return [
  1184. execResult[ 1 ] * 2.55,
  1185. execResult[ 2 ] * 2.55,
  1186. execResult[ 3 ] * 2.55,
  1187. execResult[ 4 ]
  1188. ];
  1189. }
  1190. }, {
  1191. // This regex ignores A-F because it's compared against an already lowercased string
  1192. re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
  1193. parse: function( execResult ) {
  1194. return [
  1195. parseInt( execResult[ 1 ], 16 ),
  1196. parseInt( execResult[ 2 ], 16 ),
  1197. parseInt( execResult[ 3 ], 16 )
  1198. ];
  1199. }
  1200. }, {
  1201. // This regex ignores A-F because it's compared against an already lowercased string
  1202. re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
  1203. parse: function( execResult ) {
  1204. return [
  1205. parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
  1206. parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
  1207. parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
  1208. ];
  1209. }
  1210. }, {
  1211. re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
  1212. space: "hsla",
  1213. parse: function( execResult ) {
  1214. return [
  1215. execResult[ 1 ],
  1216. execResult[ 2 ] / 100,
  1217. execResult[ 3 ] / 100,
  1218. execResult[ 4 ]
  1219. ];
  1220. }
  1221. } ],
  1222. // JQuery.Color( )
  1223. color = jQuery.Color = function( color, green, blue, alpha ) {
  1224. return new jQuery.Color.fn.parse( color, green, blue, alpha );
  1225. },
  1226. spaces = {
  1227. rgba: {
  1228. props: {
  1229. red: {
  1230. idx: 0,
  1231. type: "byte"
  1232. },
  1233. green: {
  1234. idx: 1,
  1235. type: "byte"
  1236. },
  1237. blue: {
  1238. idx: 2,
  1239. type: "byte"
  1240. }
  1241. }
  1242. },
  1243. hsla: {
  1244. props: {
  1245. hue: {
  1246. idx: 0,
  1247. type: "degrees"
  1248. },
  1249. saturation: {
  1250. idx: 1,
  1251. type: "percent"
  1252. },
  1253. lightness: {
  1254. idx: 2,
  1255. type: "percent"
  1256. }
  1257. }
  1258. }
  1259. },
  1260. propTypes = {
  1261. "byte": {
  1262. floor: true,
  1263. max: 255
  1264. },
  1265. "percent": {
  1266. max: 1
  1267. },
  1268. "degrees": {
  1269. mod: 360,
  1270. floor: true
  1271. }
  1272. },
  1273. support = color.support = {},
  1274. // Element for support tests
  1275. supportElem = jQuery( "<p>" )[ 0 ],
  1276. // Colors = jQuery.Color.names
  1277. colors,
  1278. // Local aliases of functions called often
  1279. each = jQuery.each;
  1280. // Determine rgba support immediately
  1281. supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
  1282. support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
  1283. // Define cache name and alpha properties
  1284. // for rgba and hsla spaces
  1285. each( spaces, function( spaceName, space ) {
  1286. space.cache = "_" + spaceName;
  1287. space.props.alpha = {
  1288. idx: 3,
  1289. type: "percent",
  1290. def: 1
  1291. };
  1292. } );
  1293. function clamp( value, prop, allowEmpty ) {
  1294. var type = propTypes[ prop.type ] || {};
  1295. if ( value == null ) {
  1296. return ( allowEmpty || !prop.def ) ? null : prop.def;
  1297. }
  1298. // ~~ is an short way of doing floor for positive numbers
  1299. value = type.floor ? ~~value : parseFloat( value );
  1300. // IE will pass in empty strings as value for alpha,
  1301. // which will hit this case
  1302. if ( isNaN( value ) ) {
  1303. return prop.def;
  1304. }
  1305. if ( type.mod ) {
  1306. // We add mod before modding to make sure that negatives values
  1307. // get converted properly: -10 -> 350
  1308. return ( value + type.mod ) % type.mod;
  1309. }
  1310. // For now all property types without mod have min and max
  1311. return 0 > value ? 0 : type.max < value ? type.max : value;
  1312. }
  1313. function stringParse( string ) {
  1314. var inst = color(),
  1315. rgba = inst._rgba = [];
  1316. string = string.toLowerCase();
  1317. each( stringParsers, function( i, parser ) {
  1318. var parsed,
  1319. match = parser.re.exec( string ),
  1320. values = match && parser.parse( match ),
  1321. spaceName = parser.space || "rgba";
  1322. if ( values ) {
  1323. parsed = inst[ spaceName ]( values );
  1324. // If this was an rgba parse the assignment might happen twice
  1325. // oh well....
  1326. inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
  1327. rgba = inst._rgba = parsed._rgba;
  1328. // Exit each( stringParsers ) here because we matched
  1329. return false;
  1330. }
  1331. } );
  1332. // Found a stringParser that handled it
  1333. if ( rgba.length ) {
  1334. // If this came from a parsed string, force "transparent" when alpha is 0
  1335. // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
  1336. if ( rgba.join() === "0,0,0,0" ) {
  1337. jQuery.extend( rgba, colors.transparent );
  1338. }
  1339. return inst;
  1340. }
  1341. // Named colors
  1342. return colors[ string ];
  1343. }
  1344. color.fn = jQuery.extend( color.prototype, {
  1345. parse: function( red, green, blue, alpha ) {
  1346. if ( red === undefined ) {
  1347. this._rgba = [ null, null, null, null ];
  1348. return this;
  1349. }
  1350. if ( red.jquery || red.nodeType ) {
  1351. red = jQuery( red ).css( green );
  1352. green = undefined;
  1353. }
  1354. var inst = this,
  1355. type = jQuery.type( red ),
  1356. rgba = this._rgba = [];
  1357. // More than 1 argument specified - assume ( red, green, blue, alpha )
  1358. if ( green !== undefined ) {
  1359. red = [ red, green, blue, alpha ];
  1360. type = "array";
  1361. }
  1362. if ( type === "string" ) {
  1363. return this.parse( stringParse( red ) || colors._default );
  1364. }
  1365. if ( type === "array" ) {
  1366. each( spaces.rgba.props, function( key, prop ) {
  1367. rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
  1368. } );
  1369. return this;
  1370. }
  1371. if ( type === "object" ) {
  1372. if ( red instanceof color ) {
  1373. each( spaces, function( spaceName, space ) {
  1374. if ( red[ space.cache ] ) {
  1375. inst[ space.cache ] = red[ space.cache ].slice();
  1376. }
  1377. } );
  1378. } else {
  1379. each( spaces, function( spaceName, space ) {
  1380. var cache = space.cache;
  1381. each( space.props, function( key, prop ) {
  1382. // If the cache doesn't exist, and we know how to convert
  1383. if ( !inst[ cache ] && space.to ) {
  1384. // If the value was null, we don't need to copy it
  1385. // if the key was alpha, we don't need to copy it either
  1386. if ( key === "alpha" || red[ key ] == null ) {
  1387. return;
  1388. }
  1389. inst[ cache ] = space.to( inst._rgba );
  1390. }
  1391. // This is the only case where we allow nulls for ALL properties.
  1392. // call clamp with alwaysAllowEmpty
  1393. inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
  1394. } );
  1395. // Everything defined but alpha?
  1396. if ( inst[ cache ] &&
  1397. jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
  1398. // Use the default of 1
  1399. inst[ cache ][ 3 ] = 1;
  1400. if ( space.from ) {
  1401. inst._rgba = space.from( inst[ cache ] );
  1402. }
  1403. }
  1404. } );
  1405. }
  1406. return this;
  1407. }
  1408. },
  1409. is: function( compare ) {
  1410. var is = color( compare ),
  1411. same = true,
  1412. inst = this;
  1413. each( spaces, function( _, space ) {
  1414. var localCache,
  1415. isCache = is[ space.cache ];
  1416. if ( isCache ) {
  1417. localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
  1418. each( space.props, function( _, prop ) {
  1419. if ( isCache[ prop.idx ] != null ) {
  1420. same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
  1421. return same;
  1422. }
  1423. } );
  1424. }
  1425. return same;
  1426. } );
  1427. return same;
  1428. },
  1429. _space: function() {
  1430. var used = [],
  1431. inst = this;
  1432. each( spaces, function( spaceName, space ) {
  1433. if ( inst[ space.cache ] ) {
  1434. used.push( spaceName );
  1435. }
  1436. } );
  1437. return used.pop();
  1438. },
  1439. transition: function( other, distance ) {
  1440. var end = color( other ),
  1441. spaceName = end._space(),
  1442. space = spaces[ spaceName ],
  1443. startColor = this.alpha() === 0 ? color( "transparent" ) : this,
  1444. start = startColor[ space.cache ] || space.to( startColor._rgba ),
  1445. result = start.slice();
  1446. end = end[ space.cache ];
  1447. each( space.props, function( key, prop ) {
  1448. var index = prop.idx,
  1449. startValue = start[ index ],
  1450. endValue = end[ index ],
  1451. type = propTypes[ prop.type ] || {};
  1452. // If null, don't override start value
  1453. if ( endValue === null ) {
  1454. return;
  1455. }
  1456. // If null - use end
  1457. if ( startValue === null ) {
  1458. result[ index ] = endValue;
  1459. } else {
  1460. if ( type.mod ) {
  1461. if ( endValue - startValue > type.mod / 2 ) {
  1462. startValue += type.mod;
  1463. } else if ( startValue - endValue > type.mod / 2 ) {
  1464. startValue -= type.mod;
  1465. }
  1466. }
  1467. result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
  1468. }
  1469. } );
  1470. return this[ spaceName ]( result );
  1471. },
  1472. blend: function( opaque ) {
  1473. // If we are already opaque - return ourself
  1474. if ( this._rgba[ 3 ] === 1 ) {
  1475. return this;
  1476. }
  1477. var rgb = this._rgba.slice(),
  1478. a = rgb.pop(),
  1479. blend = color( opaque )._rgba;
  1480. return color( jQuery.map( rgb, function( v, i ) {
  1481. return ( 1 - a ) * blend[ i ] + a * v;
  1482. } ) );
  1483. },
  1484. toRgbaString: function() {
  1485. var prefix = "rgba(",
  1486. rgba = jQuery.map( this._rgba, function( v, i ) {
  1487. return v == null ? ( i > 2 ? 1 : 0 ) : v;
  1488. } );
  1489. if ( rgba[ 3 ] === 1 ) {
  1490. rgba.pop();
  1491. prefix = "rgb(";
  1492. }
  1493. return prefix + rgba.join() + ")";
  1494. },
  1495. toHslaString: function() {
  1496. var prefix = "hsla(",
  1497. hsla = jQuery.map( this.hsla(), function( v, i ) {
  1498. if ( v == null ) {
  1499. v = i > 2 ? 1 : 0;
  1500. }
  1501. // Catch 1 and 2
  1502. if ( i && i < 3 ) {
  1503. v = Math.round( v * 100 ) + "%";
  1504. }
  1505. return v;
  1506. } );
  1507. if ( hsla[ 3 ] === 1 ) {
  1508. hsla.pop();
  1509. prefix = "hsl(";
  1510. }
  1511. return prefix + hsla.join() + ")";
  1512. },
  1513. toHexString: function( includeAlpha ) {
  1514. var rgba = this._rgba.slice(),
  1515. alpha = rgba.pop();
  1516. if ( includeAlpha ) {
  1517. rgba.push( ~~( alpha * 255 ) );
  1518. }
  1519. return "#" + jQuery.map( rgba, function( v ) {
  1520. // Default to 0 when nulls exist
  1521. v = ( v || 0 ).toString( 16 );
  1522. return v.length === 1 ? "0" + v : v;
  1523. } ).join( "" );
  1524. },
  1525. toString: function() {
  1526. return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
  1527. }
  1528. } );
  1529. color.fn.parse.prototype = color.fn;
  1530. // Hsla conversions adapted from:
  1531. // https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
  1532. function hue2rgb( p, q, h ) {
  1533. h = ( h + 1 ) % 1;
  1534. if ( h * 6 < 1 ) {
  1535. return p + ( q - p ) * h * 6;
  1536. }
  1537. if ( h * 2 < 1 ) {
  1538. return q;
  1539. }
  1540. if ( h * 3 < 2 ) {
  1541. return p + ( q - p ) * ( ( 2 / 3 ) - h ) * 6;
  1542. }
  1543. return p;
  1544. }
  1545. spaces.hsla.to = function( rgba ) {
  1546. if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
  1547. return [ null, null, null, rgba[ 3 ] ];
  1548. }
  1549. var r = rgba[ 0 ] / 255,
  1550. g = rgba[ 1 ] / 255,
  1551. b = rgba[ 2 ] / 255,
  1552. a = rgba[ 3 ],
  1553. max = Math.max( r, g, b ),
  1554. min = Math.min( r, g, b ),
  1555. diff = max - min,
  1556. add = max + min,
  1557. l = add * 0.5,
  1558. h, s;
  1559. if ( min === max ) {
  1560. h = 0;
  1561. } else if ( r === max ) {
  1562. h = ( 60 * ( g - b ) / diff ) + 360;
  1563. } else if ( g === max ) {
  1564. h = ( 60 * ( b - r ) / diff ) + 120;
  1565. } else {
  1566. h = ( 60 * ( r - g ) / diff ) + 240;
  1567. }
  1568. // Chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
  1569. // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
  1570. if ( diff === 0 ) {
  1571. s = 0;
  1572. } else if ( l <= 0.5 ) {
  1573. s = diff / add;
  1574. } else {
  1575. s = diff / ( 2 - add );
  1576. }
  1577. return [ Math.round( h ) % 360, s, l, a == null ? 1 : a ];
  1578. };
  1579. spaces.hsla.from = function( hsla ) {
  1580. if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
  1581. return [ null, null, null, hsla[ 3 ] ];
  1582. }
  1583. var h = hsla[ 0 ] / 360,
  1584. s = hsla[ 1 ],
  1585. l = hsla[ 2 ],
  1586. a = hsla[ 3 ],
  1587. q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
  1588. p = 2 * l - q;
  1589. return [
  1590. Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
  1591. Math.round( hue2rgb( p, q, h ) * 255 ),
  1592. Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
  1593. a
  1594. ];
  1595. };
  1596. each( spaces, function( spaceName, space ) {
  1597. var props = space.props,
  1598. cache = space.cache,
  1599. to = space.to,
  1600. from = space.from;
  1601. // Makes rgba() and hsla()
  1602. color.fn[ spaceName ] = function( value ) {
  1603. // Generate a cache for this space if it doesn't exist
  1604. if ( to && !this[ cache ] ) {
  1605. this[ cache ] = to( this._rgba );
  1606. }
  1607. if ( value === undefined ) {
  1608. return this[ cache ].slice();
  1609. }
  1610. var ret,
  1611. type = jQuery.type( value ),
  1612. arr = ( type === "array" || type === "object" ) ? value : arguments,
  1613. local = this[ cache ].slice();
  1614. each( props, function( key, prop ) {
  1615. var val = arr[ type === "object" ? key : prop.idx ];
  1616. if ( val == null ) {
  1617. val = local[ prop.idx ];
  1618. }
  1619. local[ prop.idx ] = clamp( val, prop );
  1620. } );
  1621. if ( from ) {
  1622. ret = color( from( local ) );
  1623. ret[ cache ] = local;
  1624. return ret;
  1625. } else {
  1626. return color( local );
  1627. }
  1628. };
  1629. // Makes red() green() blue() alpha() hue() saturation() lightness()
  1630. each( props, function( key, prop ) {
  1631. // Alpha is included in more than one space
  1632. if ( color.fn[ key ] ) {
  1633. return;
  1634. }
  1635. color.fn[ key ] = function( value ) {
  1636. var vtype = jQuery.type( value ),
  1637. fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
  1638. local = this[ fn ](),
  1639. cur = local[ prop.idx ],
  1640. match;
  1641. if ( vtype === "undefined" ) {
  1642. return cur;
  1643. }
  1644. if ( vtype === "function" ) {
  1645. value = value.call( this, cur );
  1646. vtype = jQuery.type( value );
  1647. }
  1648. if ( value == null && prop.empty ) {
  1649. return this;
  1650. }
  1651. if ( vtype === "string" ) {
  1652. match = rplusequals.exec( value );
  1653. if ( match ) {
  1654. value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
  1655. }
  1656. }
  1657. local[ prop.idx ] = value;
  1658. return this[ fn ]( local );
  1659. };
  1660. } );
  1661. } );
  1662. // Add cssHook and .fx.step function for each named hook.
  1663. // accept a space separated string of properties
  1664. color.hook = function( hook ) {
  1665. var hooks = hook.split( " " );
  1666. each( hooks, function( i, hook ) {
  1667. jQuery.cssHooks[ hook ] = {
  1668. set: function( elem, value ) {
  1669. var parsed, curElem,
  1670. backgroundColor = "";
  1671. if ( value !== "transparent" && ( jQuery.type( value ) !== "string" ||
  1672. ( parsed = stringParse( value ) ) ) ) {
  1673. value = color( parsed || value );
  1674. if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
  1675. curElem = hook === "backgroundColor" ? elem.parentNode : elem;
  1676. while (
  1677. ( backgroundColor === "" || backgroundColor === "transparent" ) &&
  1678. curElem && curElem.style
  1679. ) {
  1680. try {
  1681. backgroundColor = jQuery.css( curElem, "backgroundColor" );
  1682. curElem = curElem.parentNode;
  1683. } catch ( e ) {
  1684. }
  1685. }
  1686. value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
  1687. backgroundColor :
  1688. "_default" );
  1689. }
  1690. value = value.toRgbaString();
  1691. }
  1692. try {
  1693. elem.style[ hook ] = value;
  1694. } catch ( e ) {
  1695. // Wrapped to prevent IE from throwing errors on "invalid" values like
  1696. // 'auto' or 'inherit'
  1697. }
  1698. }
  1699. };
  1700. jQuery.fx.step[ hook ] = function( fx ) {
  1701. if ( !fx.colorInit ) {
  1702. fx.start = color( fx.elem, hook );
  1703. fx.end = color( fx.end );
  1704. fx.colorInit = true;
  1705. }
  1706. jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
  1707. };
  1708. } );
  1709. };
  1710. color.hook( stepHooks );
  1711. jQuery.cssHooks.borderColor = {
  1712. expand: function( value ) {
  1713. var expanded = {};
  1714. each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
  1715. expanded[ "border" + part + "Color" ] = value;
  1716. } );
  1717. return expanded;
  1718. }
  1719. };
  1720. // Basic color names only.
  1721. // Usage of any of the other color names requires adding yourself or including
  1722. // jquery.color.svg-names.js.
  1723. colors = jQuery.Color.names = {
  1724. // 4.1. Basic color keywords
  1725. aqua: "#00ffff",
  1726. black: "#000000",
  1727. blue: "#0000ff",
  1728. fuchsia: "#ff00ff",
  1729. gray: "#808080",
  1730. green: "#008000",
  1731. lime: "#00ff00",
  1732. maroon: "#800000",
  1733. navy: "#000080",
  1734. olive: "#808000",
  1735. purple: "#800080",
  1736. red: "#ff0000",
  1737. silver: "#c0c0c0",
  1738. teal: "#008080",
  1739. white: "#ffffff",
  1740. yellow: "#ffff00",
  1741. // 4.2.3. "transparent" color keyword
  1742. transparent: [ null, null, null, 0 ],
  1743. _default: "#ffffff"
  1744. };
  1745. } )( jQuery );
  1746. /******************************************************************************/
  1747. /****************************** CLASS ANIMATIONS ******************************/
  1748. /******************************************************************************/
  1749. ( function() {
  1750. var classAnimationActions = [ "add", "remove", "toggle" ],
  1751. shorthandStyles = {
  1752. border: 1,
  1753. borderBottom: 1,
  1754. borderColor: 1,
  1755. borderLeft: 1,
  1756. borderRight: 1,
  1757. borderTop: 1,
  1758. borderWidth: 1,
  1759. margin: 1,
  1760. padding: 1
  1761. };
  1762. $.each(
  1763. [ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ],
  1764. function( _, prop ) {
  1765. $.fx.step[ prop ] = function( fx ) {
  1766. if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
  1767. jQuery.style( fx.elem, prop, fx.end );
  1768. fx.setAttr = true;
  1769. }
  1770. };
  1771. }
  1772. );
  1773. function getElementStyles( elem ) {
  1774. var key, len,
  1775. style = elem.ownerDocument.defaultView ?
  1776. elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
  1777. elem.currentStyle,
  1778. styles = {};
  1779. if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
  1780. len = style.length;
  1781. while ( len-- ) {
  1782. key = style[ len ];
  1783. if ( typeof style[ key ] === "string" ) {
  1784. styles[ $.camelCase( key ) ] = style[ key ];
  1785. }
  1786. }
  1787. // Support: Opera, IE <9
  1788. } else {
  1789. for ( key in style ) {
  1790. if ( typeof style[ key ] === "string" ) {
  1791. styles[ key ] = style[ key ];
  1792. }
  1793. }
  1794. }
  1795. return styles;
  1796. }
  1797. function styleDifference( oldStyle, newStyle ) {
  1798. var diff = {},
  1799. name, value;
  1800. for ( name in newStyle ) {
  1801. value = newStyle[ name ];
  1802. if ( oldStyle[ name ] !== value ) {
  1803. if ( !shorthandStyles[ name ] ) {
  1804. if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
  1805. diff[ name ] = value;
  1806. }
  1807. }
  1808. }
  1809. }
  1810. return diff;
  1811. }
  1812. // Support: jQuery <1.8
  1813. if ( !$.fn.addBack ) {
  1814. $.fn.addBack = function( selector ) {
  1815. return this.add( selector == null ?
  1816. this.prevObject : this.prevObject.filter( selector )
  1817. );
  1818. };
  1819. }
  1820. $.effects.animateClass = function( value, duration, easing, callback ) {
  1821. var o = $.speed( duration, easing, callback );
  1822. return this.queue( function() {
  1823. var animated = $( this ),
  1824. baseClass = animated.attr( "class" ) || "",
  1825. applyClassChange,
  1826. allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
  1827. // Map the animated objects to store the original styles.
  1828. allAnimations = allAnimations.map( function() {
  1829. var el = $( this );
  1830. return {
  1831. el: el,
  1832. start: getElementStyles( this )
  1833. };
  1834. } );
  1835. // Apply class change
  1836. applyClassChange = function() {
  1837. $.each( classAnimationActions, function( i, action ) {
  1838. if ( value[ action ] ) {
  1839. animated[ action + "Class" ]( value[ action ] );
  1840. }
  1841. } );
  1842. };
  1843. applyClassChange();
  1844. // Map all animated objects again - calculate new styles and diff
  1845. allAnimations = allAnimations.map( function() {
  1846. this.end = getElementStyles( this.el[ 0 ] );
  1847. this.diff = styleDifference( this.start, this.end );
  1848. return this;
  1849. } );
  1850. // Apply original class
  1851. animated.attr( "class", baseClass );
  1852. // Map all animated objects again - this time collecting a promise
  1853. allAnimations = allAnimations.map( function() {
  1854. var styleInfo = this,
  1855. dfd = $.Deferred(),
  1856. opts = $.extend( {}, o, {
  1857. queue: false,
  1858. complete: function() {
  1859. dfd.resolve( styleInfo );
  1860. }
  1861. } );
  1862. this.el.animate( this.diff, opts );
  1863. return dfd.promise();
  1864. } );
  1865. // Once all animations have completed:
  1866. $.when.apply( $, allAnimations.get() ).done( function() {
  1867. // Set the final class
  1868. applyClassChange();
  1869. // For each animated element,
  1870. // clear all css properties that were animated
  1871. $.each( arguments, function() {
  1872. var el = this.el;
  1873. $.each( this.diff, function( key ) {
  1874. el.css( key, "" );
  1875. } );
  1876. } );
  1877. // This is guarnteed to be there if you use jQuery.speed()
  1878. // it also handles dequeuing the next anim...
  1879. o.complete.call( animated[ 0 ] );
  1880. } );
  1881. } );
  1882. };
  1883. $.fn.extend( {
  1884. addClass: ( function( orig ) {
  1885. return function( classNames, speed, easing, callback ) {
  1886. return speed ?
  1887. $.effects.animateClass.call( this,
  1888. { add: classNames }, speed, easing, callback ) :
  1889. orig.apply( this, arguments );
  1890. };
  1891. } )( $.fn.addClass ),
  1892. removeClass: ( function( orig ) {
  1893. return function( classNames, speed, easing, callback ) {
  1894. return arguments.length > 1 ?
  1895. $.effects.animateClass.call( this,
  1896. { remove: classNames }, speed, easing, callback ) :
  1897. orig.apply( this, arguments );
  1898. };
  1899. } )( $.fn.removeClass ),
  1900. toggleClass: ( function( orig ) {
  1901. return function( classNames, force, speed, easing, callback ) {
  1902. if ( typeof force === "boolean" || force === undefined ) {
  1903. if ( !speed ) {
  1904. // Without speed parameter
  1905. return orig.apply( this, arguments );
  1906. } else {
  1907. return $.effects.animateClass.call( this,
  1908. ( force ? { add: classNames } : { remove: classNames } ),
  1909. speed, easing, callback );
  1910. }
  1911. } else {
  1912. // Without force parameter
  1913. return $.effects.animateClass.call( this,
  1914. { toggle: classNames }, force, speed, easing );
  1915. }
  1916. };
  1917. } )( $.fn.toggleClass ),
  1918. switchClass: function( remove, add, speed, easing, callback ) {
  1919. return $.effects.animateClass.call( this, {
  1920. add: add,
  1921. remove: remove
  1922. }, speed, easing, callback );
  1923. }
  1924. } );
  1925. } )();
  1926. /******************************************************************************/
  1927. /*********************************** EFFECTS **********************************/
  1928. /******************************************************************************/
  1929. ( function() {
  1930. if ( $.expr && $.expr.filters && $.expr.filters.animated ) {
  1931. $.expr.filters.animated = ( function( orig ) {
  1932. return function( elem ) {
  1933. return !!$( elem ).data( dataSpaceAnimated ) || orig( elem );
  1934. };
  1935. } )( $.expr.filters.animated );
  1936. }
  1937. if ( $.uiBackCompat !== false ) {
  1938. $.extend( $.effects, {
  1939. // Saves a set of properties in a data storage
  1940. save: function( element, set ) {
  1941. var i = 0, length = set.length;
  1942. for ( ; i < length; i++ ) {
  1943. if ( set[ i ] !== null ) {
  1944. element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
  1945. }
  1946. }
  1947. },
  1948. // Restores a set of previously saved properties from a data storage
  1949. restore: function( element, set ) {
  1950. var val, i = 0, length = set.length;
  1951. for ( ; i < length; i++ ) {
  1952. if ( set[ i ] !== null ) {
  1953. val = element.data( dataSpace + set[ i ] );
  1954. element.css( set[ i ], val );
  1955. }
  1956. }
  1957. },
  1958. setMode: function( el, mode ) {
  1959. if ( mode === "toggle" ) {
  1960. mode = el.is( ":hidden" ) ? "show" : "hide";
  1961. }
  1962. return mode;
  1963. },
  1964. // Wraps the element around a wrapper that copies position properties
  1965. createWrapper: function( element ) {
  1966. // If the element is already wrapped, return it
  1967. if ( element.parent().is( ".ui-effects-wrapper" ) ) {
  1968. return element.parent();
  1969. }
  1970. // Wrap the element
  1971. var props = {
  1972. width: element.outerWidth( true ),
  1973. height: element.outerHeight( true ),
  1974. "float": element.css( "float" )
  1975. },
  1976. wrapper = $( "<div></div>" )
  1977. .addClass( "ui-effects-wrapper" )
  1978. .css( {
  1979. fontSize: "100%",
  1980. background: "transparent",
  1981. border: "none",
  1982. margin: 0,
  1983. padding: 0
  1984. } ),
  1985. // Store the size in case width/height are defined in % - Fixes #5245
  1986. size = {
  1987. width: element.width(),
  1988. height: element.height()
  1989. },
  1990. active = document.activeElement;
  1991. // Support: Firefox
  1992. // Firefox incorrectly exposes anonymous content
  1993. // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
  1994. try {
  1995. active.id;
  1996. } catch ( e ) {
  1997. active = document.body;
  1998. }
  1999. element.wrap( wrapper );
  2000. // Fixes #7595 - Elements lose focus when wrapped.
  2001. if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
  2002. $( active ).trigger( "focus" );
  2003. }
  2004. // Hotfix for jQuery 1.4 since some change in wrap() seems to actually
  2005. // lose the reference to the wrapped element
  2006. wrapper = element.parent();
  2007. // Transfer positioning properties to the wrapper
  2008. if ( element.css( "position" ) === "static" ) {
  2009. wrapper.css( { position: "relative" } );
  2010. element.css( { position: "relative" } );
  2011. } else {
  2012. $.extend( props, {
  2013. position: element.css( "position" ),
  2014. zIndex: element.css( "z-index" )
  2015. } );
  2016. $.each( [ "top", "left", "bottom", "right" ], function( i, pos ) {
  2017. props[ pos ] = element.css( pos );
  2018. if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
  2019. props[ pos ] = "auto";
  2020. }
  2021. } );
  2022. element.css( {
  2023. position: "relative",
  2024. top: 0,
  2025. left: 0,
  2026. right: "auto",
  2027. bottom: "auto"
  2028. } );
  2029. }
  2030. element.css( size );
  2031. return wrapper.css( props ).show();
  2032. },
  2033. removeWrapper: function( element ) {
  2034. var active = document.activeElement;
  2035. if ( element.parent().is( ".ui-effects-wrapper" ) ) {
  2036. element.parent().replaceWith( element );
  2037. // Fixes #7595 - Elements lose focus when wrapped.
  2038. if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
  2039. $( active ).trigger( "focus" );
  2040. }
  2041. }
  2042. return element;
  2043. }
  2044. } );
  2045. }
  2046. $.extend( $.effects, {
  2047. version: "1.12.1",
  2048. define: function( name, mode, effect ) {
  2049. if ( !effect ) {
  2050. effect = mode;
  2051. mode = "effect";
  2052. }
  2053. $.effects.effect[ name ] = effect;
  2054. $.effects.effect[ name ].mode = mode;
  2055. return effect;
  2056. },
  2057. scaledDimensions: function( element, percent, direction ) {
  2058. if ( percent === 0 ) {
  2059. return {
  2060. height: 0,
  2061. width: 0,
  2062. outerHeight: 0,
  2063. outerWidth: 0
  2064. };
  2065. }
  2066. var x = direction !== "horizontal" ? ( ( percent || 100 ) / 100 ) : 1,
  2067. y = direction !== "vertical" ? ( ( percent || 100 ) / 100 ) : 1;
  2068. return {
  2069. height: element.height() * y,
  2070. width: element.width() * x,
  2071. outerHeight: element.outerHeight() * y,
  2072. outerWidth: element.outerWidth() * x
  2073. };
  2074. },
  2075. clipToBox: function( animation ) {
  2076. return {
  2077. width: animation.clip.right - animation.clip.left,
  2078. height: animation.clip.bottom - animation.clip.top,
  2079. left: animation.clip.left,
  2080. top: animation.clip.top
  2081. };
  2082. },
  2083. // Injects recently queued functions to be first in line (after "inprogress")
  2084. unshift: function( element, queueLength, count ) {
  2085. var queue = element.queue();
  2086. if ( queueLength > 1 ) {
  2087. queue.splice.apply( queue,
  2088. [ 1, 0 ].concat( queue.splice( queueLength, count ) ) );
  2089. }
  2090. element.dequeue();
  2091. },
  2092. saveStyle: function( element ) {
  2093. element.data( dataSpaceStyle, element[ 0 ].style.cssText );
  2094. },
  2095. restoreStyle: function( element ) {
  2096. element[ 0 ].style.cssText = element.data( dataSpaceStyle ) || "";
  2097. element.removeData( dataSpaceStyle );
  2098. },
  2099. mode: function( element, mode ) {
  2100. var hidden = element.is( ":hidden" );
  2101. if ( mode === "toggle" ) {
  2102. mode = hidden ? "show" : "hide";
  2103. }
  2104. if ( hidden ? mode === "hide" : mode === "show" ) {
  2105. mode = "none";
  2106. }
  2107. return mode;
  2108. },
  2109. // Translates a [top,left] array into a baseline value
  2110. getBaseline: function( origin, original ) {
  2111. var y, x;
  2112. switch ( origin[ 0 ] ) {
  2113. case "top":
  2114. y = 0;
  2115. break;
  2116. case "middle":
  2117. y = 0.5;
  2118. break;
  2119. case "bottom":
  2120. y = 1;
  2121. break;
  2122. default:
  2123. y = origin[ 0 ] / original.height;
  2124. }
  2125. switch ( origin[ 1 ] ) {
  2126. case "left":
  2127. x = 0;
  2128. break;
  2129. case "center":
  2130. x = 0.5;
  2131. break;
  2132. case "right":
  2133. x = 1;
  2134. break;
  2135. default:
  2136. x = origin[ 1 ] / original.width;
  2137. }
  2138. return {
  2139. x: x,
  2140. y: y
  2141. };
  2142. },
  2143. // Creates a placeholder element so that the original element can be made absolute
  2144. createPlaceholder: function( element ) {
  2145. var placeholder,
  2146. cssPosition = element.css( "position" ),
  2147. position = element.position();
  2148. // Lock in margins first to account for form elements, which
  2149. // will change margin if you explicitly set height
  2150. // see: http://jsfiddle.net/JZSMt/3/ https://bugs.webkit.org/show_bug.cgi?id=107380
  2151. // Support: Safari
  2152. element.css( {
  2153. marginTop: element.css( "marginTop" ),
  2154. marginBottom: element.css( "marginBottom" ),
  2155. marginLeft: element.css( "marginLeft" ),
  2156. marginRight: element.css( "marginRight" )
  2157. } )
  2158. .outerWidth( element.outerWidth() )
  2159. .outerHeight( element.outerHeight() );
  2160. if ( /^(static|relative)/.test( cssPosition ) ) {
  2161. cssPosition = "absolute";
  2162. placeholder = $( "<" + element[ 0 ].nodeName + ">" ).insertAfter( element ).css( {
  2163. // Convert inline to inline block to account for inline elements
  2164. // that turn to inline block based on content (like img)
  2165. display: /^(inline|ruby)/.test( element.css( "display" ) ) ?
  2166. "inline-block" :
  2167. "block",
  2168. visibility: "hidden",
  2169. // Margins need to be set to account for margin collapse
  2170. marginTop: element.css( "marginTop" ),
  2171. marginBottom: element.css( "marginBottom" ),
  2172. marginLeft: element.css( "marginLeft" ),
  2173. marginRight: element.css( "marginRight" ),
  2174. "float": element.css( "float" )
  2175. } )
  2176. .outerWidth( element.outerWidth() )
  2177. .outerHeight( element.outerHeight() )
  2178. .addClass( "ui-effects-placeholder" );
  2179. element.data( dataSpace + "placeholder", placeholder );
  2180. }
  2181. element.css( {
  2182. position: cssPosition,
  2183. left: position.left,
  2184. top: position.top
  2185. } );
  2186. return placeholder;
  2187. },
  2188. removePlaceholder: function( element ) {
  2189. var dataKey = dataSpace + "placeholder",
  2190. placeholder = element.data( dataKey );
  2191. if ( placeholder ) {
  2192. placeholder.remove();
  2193. element.removeData( dataKey );
  2194. }
  2195. },
  2196. // Removes a placeholder if it exists and restores
  2197. // properties that were modified during placeholder creation
  2198. cleanUp: function( element ) {
  2199. $.effects.restoreStyle( element );
  2200. $.effects.removePlaceholder( element );
  2201. },
  2202. setTransition: function( element, list, factor, value ) {
  2203. value = value || {};
  2204. $.each( list, function( i, x ) {
  2205. var unit = element.cssUnit( x );
  2206. if ( unit[ 0 ] > 0 ) {
  2207. value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
  2208. }
  2209. } );
  2210. return value;
  2211. }
  2212. } );
  2213. // Return an effect options object for the given parameters:
  2214. function _normalizeArguments( effect, options, speed, callback ) {
  2215. // Allow passing all options as the first parameter
  2216. if ( $.isPlainObject( effect ) ) {
  2217. options = effect;
  2218. effect = effect.effect;
  2219. }
  2220. // Convert to an object
  2221. effect = { effect: effect };
  2222. // Catch (effect, null, ...)
  2223. if ( options == null ) {
  2224. options = {};
  2225. }
  2226. // Catch (effect, callback)
  2227. if ( $.isFunction( options ) ) {
  2228. callback = options;
  2229. speed = null;
  2230. options = {};
  2231. }
  2232. // Catch (effect, speed, ?)
  2233. if ( typeof options === "number" || $.fx.speeds[ options ] ) {
  2234. callback = speed;
  2235. speed = options;
  2236. options = {};
  2237. }
  2238. // Catch (effect, options, callback)
  2239. if ( $.isFunction( speed ) ) {
  2240. callback = speed;
  2241. speed = null;
  2242. }
  2243. // Add options to effect
  2244. if ( options ) {
  2245. $.extend( effect, options );
  2246. }
  2247. speed = speed || options.duration;
  2248. effect.duration = $.fx.off ? 0 :
  2249. typeof speed === "number" ? speed :
  2250. speed in $.fx.speeds ? $.fx.speeds[ speed ] :
  2251. $.fx.speeds._default;
  2252. effect.complete = callback || options.complete;
  2253. return effect;
  2254. }
  2255. function standardAnimationOption( option ) {
  2256. // Valid standard speeds (nothing, number, named speed)
  2257. if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
  2258. return true;
  2259. }
  2260. // Invalid strings - treat as "normal" speed
  2261. if ( typeof option === "string" && !$.effects.effect[ option ] ) {
  2262. return true;
  2263. }
  2264. // Complete callback
  2265. if ( $.isFunction( option ) ) {
  2266. return true;
  2267. }
  2268. // Options hash (but not naming an effect)
  2269. if ( typeof option === "object" && !option.effect ) {
  2270. return true;
  2271. }
  2272. // Didn't match any standard API
  2273. return false;
  2274. }
  2275. $.fn.extend( {
  2276. effect: function( /* effect, options, speed, callback */ ) {
  2277. var args = _normalizeArguments.apply( this, arguments ),
  2278. effectMethod = $.effects.effect[ args.effect ],
  2279. defaultMode = effectMethod.mode,
  2280. queue = args.queue,
  2281. queueName = queue || "fx",
  2282. complete = args.complete,
  2283. mode = args.mode,
  2284. modes = [],
  2285. prefilter = function( next ) {
  2286. var el = $( this ),
  2287. normalizedMode = $.effects.mode( el, mode ) || defaultMode;
  2288. // Sentinel for duck-punching the :animated psuedo-selector
  2289. el.data( dataSpaceAnimated, true );
  2290. // Save effect mode for later use,
  2291. // we can't just call $.effects.mode again later,
  2292. // as the .show() below destroys the initial state
  2293. modes.push( normalizedMode );
  2294. // See $.uiBackCompat inside of run() for removal of defaultMode in 1.13
  2295. if ( defaultMode && ( normalizedMode === "show" ||
  2296. ( normalizedMode === defaultMode && normalizedMode === "hide" ) ) ) {
  2297. el.show();
  2298. }
  2299. if ( !defaultMode || normalizedMode !== "none" ) {
  2300. $.effects.saveStyle( el );
  2301. }
  2302. if ( $.isFunction( next ) ) {
  2303. next();
  2304. }
  2305. };
  2306. if ( $.fx.off || !effectMethod ) {
  2307. // Delegate to the original method (e.g., .show()) if possible
  2308. if ( mode ) {
  2309. return this[ mode ]( args.duration, complete );
  2310. } else {
  2311. return this.each( function() {
  2312. if ( complete ) {
  2313. complete.call( this );
  2314. }
  2315. } );
  2316. }
  2317. }
  2318. function run( next ) {
  2319. var elem = $( this );
  2320. function cleanup() {
  2321. elem.removeData( dataSpaceAnimated );
  2322. $.effects.cleanUp( elem );
  2323. if ( args.mode === "hide" ) {
  2324. elem.hide();
  2325. }
  2326. done();
  2327. }
  2328. function done() {
  2329. if ( $.isFunction( complete ) ) {
  2330. complete.call( elem[ 0 ] );
  2331. }
  2332. if ( $.isFunction( next ) ) {
  2333. next();
  2334. }
  2335. }
  2336. // Override mode option on a per element basis,
  2337. // as toggle can be either show or hide depending on element state
  2338. args.mode = modes.shift();
  2339. if ( $.uiBackCompat !== false && !defaultMode ) {
  2340. if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
  2341. // Call the core method to track "olddisplay" properly
  2342. elem[ mode ]();
  2343. done();
  2344. } else {
  2345. effectMethod.call( elem[ 0 ], args, done );
  2346. }
  2347. } else {
  2348. if ( args.mode === "none" ) {
  2349. // Call the core method to track "olddisplay" properly
  2350. elem[ mode ]();
  2351. done();
  2352. } else {
  2353. effectMethod.call( elem[ 0 ], args, cleanup );
  2354. }
  2355. }
  2356. }
  2357. // Run prefilter on all elements first to ensure that
  2358. // any showing or hiding happens before placeholder creation,
  2359. // which ensures that any layout changes are correctly captured.
  2360. return queue === false ?
  2361. this.each( prefilter ).each( run ) :
  2362. this.queue( queueName, prefilter ).queue( queueName, run );
  2363. },
  2364. show: ( function( orig ) {
  2365. return function( option ) {
  2366. if ( standardAnimationOption( option ) ) {
  2367. return orig.apply( this, arguments );
  2368. } else {
  2369. var args = _normalizeArguments.apply( this, arguments );
  2370. args.mode = "show";
  2371. return this.effect.call( this, args );
  2372. }
  2373. };
  2374. } )( $.fn.show ),
  2375. hide: ( function( orig ) {
  2376. return function( option ) {
  2377. if ( standardAnimationOption( option ) ) {
  2378. return orig.apply( this, arguments );
  2379. } else {
  2380. var args = _normalizeArguments.apply( this, arguments );
  2381. args.mode = "hide";
  2382. return this.effect.call( this, args );
  2383. }
  2384. };
  2385. } )( $.fn.hide ),
  2386. toggle: ( function( orig ) {
  2387. return function( option ) {
  2388. if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
  2389. return orig.apply( this, arguments );
  2390. } else {
  2391. var args = _normalizeArguments.apply( this, arguments );
  2392. args.mode = "toggle";
  2393. return this.effect.call( this, args );
  2394. }
  2395. };
  2396. } )( $.fn.toggle ),
  2397. cssUnit: function( key ) {
  2398. var style = this.css( key ),
  2399. val = [];
  2400. $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
  2401. if ( style.indexOf( unit ) > 0 ) {
  2402. val = [ parseFloat( style ), unit ];
  2403. }
  2404. } );
  2405. return val;
  2406. },
  2407. cssClip: function( clipObj ) {
  2408. if ( clipObj ) {
  2409. return this.css( "clip", "rect(" + clipObj.top + "px " + clipObj.right + "px " +
  2410. clipObj.bottom + "px " + clipObj.left + "px)" );
  2411. }
  2412. return parseClip( this.css( "clip" ), this );
  2413. },
  2414. transfer: function( options, done ) {
  2415. var element = $( this ),
  2416. target = $( options.to ),
  2417. targetFixed = target.css( "position" ) === "fixed",
  2418. body = $( "body" ),
  2419. fixTop = targetFixed ? body.scrollTop() : 0,
  2420. fixLeft = targetFixed ? body.scrollLeft() : 0,
  2421. endPosition = target.offset(),
  2422. animation = {
  2423. top: endPosition.top - fixTop,
  2424. left: endPosition.left - fixLeft,
  2425. height: target.innerHeight(),
  2426. width: target.innerWidth()
  2427. },
  2428. startPosition = element.offset(),
  2429. transfer = $( "<div class='ui-effects-transfer'></div>" )
  2430. .appendTo( "body" )
  2431. .addClass( options.className )
  2432. .css( {
  2433. top: startPosition.top - fixTop,
  2434. left: startPosition.left - fixLeft,
  2435. height: element.innerHeight(),
  2436. width: element.innerWidth(),
  2437. position: targetFixed ? "fixed" : "absolute"
  2438. } )
  2439. .animate( animation, options.duration, options.easing, function() {
  2440. transfer.remove();
  2441. if ( $.isFunction( done ) ) {
  2442. done();
  2443. }
  2444. } );
  2445. }
  2446. } );
  2447. function parseClip( str, element ) {
  2448. var outerWidth = element.outerWidth(),
  2449. outerHeight = element.outerHeight(),
  2450. clipRegex = /^rect\((-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto)\)$/,
  2451. values = clipRegex.exec( str ) || [ "", 0, outerWidth, outerHeight, 0 ];
  2452. return {
  2453. top: parseFloat( values[ 1 ] ) || 0,
  2454. right: values[ 2 ] === "auto" ? outerWidth : parseFloat( values[ 2 ] ),
  2455. bottom: values[ 3 ] === "auto" ? outerHeight : parseFloat( values[ 3 ] ),
  2456. left: parseFloat( values[ 4 ] ) || 0
  2457. };
  2458. }
  2459. $.fx.step.clip = function( fx ) {
  2460. if ( !fx.clipInit ) {
  2461. fx.start = $( fx.elem ).cssClip();
  2462. if ( typeof fx.end === "string" ) {
  2463. fx.end = parseClip( fx.end, fx.elem );
  2464. }
  2465. fx.clipInit = true;
  2466. }
  2467. $( fx.elem ).cssClip( {
  2468. top: fx.pos * ( fx.end.top - fx.start.top ) + fx.start.top,
  2469. right: fx.pos * ( fx.end.right - fx.start.right ) + fx.start.right,
  2470. bottom: fx.pos * ( fx.end.bottom - fx.start.bottom ) + fx.start.bottom,
  2471. left: fx.pos * ( fx.end.left - fx.start.left ) + fx.start.left
  2472. } );
  2473. };
  2474. } )();
  2475. /******************************************************************************/
  2476. /*********************************** EASING ***********************************/
  2477. /******************************************************************************/
  2478. ( function() {
  2479. // Based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
  2480. var baseEasings = {};
  2481. $.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
  2482. baseEasings[ name ] = function( p ) {
  2483. return Math.pow( p, i + 2 );
  2484. };
  2485. } );
  2486. $.extend( baseEasings, {
  2487. Sine: function( p ) {
  2488. return 1 - Math.cos( p * Math.PI / 2 );
  2489. },
  2490. Circ: function( p ) {
  2491. return 1 - Math.sqrt( 1 - p * p );
  2492. },
  2493. Elastic: function( p ) {
  2494. return p === 0 || p === 1 ? p :
  2495. -Math.pow( 2, 8 * ( p - 1 ) ) * Math.sin( ( ( p - 1 ) * 80 - 7.5 ) * Math.PI / 15 );
  2496. },
  2497. Back: function( p ) {
  2498. return p * p * ( 3 * p - 2 );
  2499. },
  2500. Bounce: function( p ) {
  2501. var pow2,
  2502. bounce = 4;
  2503. while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
  2504. return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
  2505. }
  2506. } );
  2507. $.each( baseEasings, function( name, easeIn ) {
  2508. $.easing[ "easeIn" + name ] = easeIn;
  2509. $.easing[ "easeOut" + name ] = function( p ) {
  2510. return 1 - easeIn( 1 - p );
  2511. };
  2512. $.easing[ "easeInOut" + name ] = function( p ) {
  2513. return p < 0.5 ?
  2514. easeIn( p * 2 ) / 2 :
  2515. 1 - easeIn( p * -2 + 2 ) / 2;
  2516. };
  2517. } );
  2518. } )();
  2519. var effect = $.effects;
  2520. /*!
  2521. * jQuery UI Effects Blind 1.12.1
  2522. * http://jqueryui.com
  2523. *
  2524. * Copyright jQuery Foundation and other contributors
  2525. * Released under the MIT license.
  2526. * http://jquery.org/license
  2527. */
  2528. //>>label: Blind Effect
  2529. //>>group: Effects
  2530. //>>description: Blinds the element.
  2531. //>>docs: http://api.jqueryui.com/blind-effect/
  2532. //>>demos: http://jqueryui.com/effect/
  2533. var effectsEffectBlind = $.effects.define( "blind", "hide", function( options, done ) {
  2534. var map = {
  2535. up: [ "bottom", "top" ],
  2536. vertical: [ "bottom", "top" ],
  2537. down: [ "top", "bottom" ],
  2538. left: [ "right", "left" ],
  2539. horizontal: [ "right", "left" ],
  2540. right: [ "left", "right" ]
  2541. },
  2542. element = $( this ),
  2543. direction = options.direction || "up",
  2544. start = element.cssClip(),
  2545. animate = { clip: $.extend( {}, start ) },
  2546. placeholder = $.effects.createPlaceholder( element );
  2547. animate.clip[ map[ direction ][ 0 ] ] = animate.clip[ map[ direction ][ 1 ] ];
  2548. if ( options.mode === "show" ) {
  2549. element.cssClip( animate.clip );
  2550. if ( placeholder ) {
  2551. placeholder.css( $.effects.clipToBox( animate ) );
  2552. }
  2553. animate.clip = start;
  2554. }
  2555. if ( placeholder ) {
  2556. placeholder.animate( $.effects.clipToBox( animate ), options.duration, options.easing );
  2557. }
  2558. element.animate( animate, {
  2559. queue: false,
  2560. duration: options.duration,
  2561. easing: options.easing,
  2562. complete: done
  2563. } );
  2564. } );
  2565. // NOTE: Original jQuery UI wrapper was replaced. See README-Fancytree.md
  2566. // }));
  2567. })(jQuery);
  2568. (function( factory ) {
  2569. if ( typeof define === "function" && define.amd ) {
  2570. // AMD. Register as an anonymous module.
  2571. define( [ "jquery" ], factory );
  2572. } else if ( typeof module === "object" && module.exports ) {
  2573. // Node/CommonJS
  2574. module.exports = factory(require("jquery"));
  2575. } else {
  2576. // Browser globals
  2577. factory( jQuery );
  2578. }
  2579. }(function( $ ) {
  2580. /*! Fancytree Core *//*!
  2581. * jquery.fancytree.js
  2582. * Tree view control with support for lazy loading and much more.
  2583. * https://github.com/mar10/fancytree/
  2584. *
  2585. * Copyright (c) 2008-2018, Martin Wendt (http://wwWendt.de)
  2586. * Released under the MIT license
  2587. * https://github.com/mar10/fancytree/wiki/LicenseInfo
  2588. *
  2589. * @version 2.28.1
  2590. * @date 2018-03-19T06:47:37Z
  2591. */
  2592. /** Core Fancytree module.
  2593. */
  2594. // UMD wrapper for the Fancytree core module
  2595. ;(function( factory ) {
  2596. if ( typeof define === "function" && define.amd ) {
  2597. // AMD. Register as an anonymous module.
  2598. define( [ "jquery", "./jquery.fancytree.ui-deps" ], factory );
  2599. } else if ( typeof module === "object" && module.exports ) {
  2600. // Node/CommonJS
  2601. require("./jquery.fancytree.ui-deps");
  2602. module.exports = factory(require("jquery"));
  2603. } else {
  2604. // Browser globals
  2605. factory( jQuery );
  2606. }
  2607. }( function( $ ) {
  2608. "use strict";
  2609. // prevent duplicate loading
  2610. if ( $.ui && $.ui.fancytree ) {
  2611. $.ui.fancytree.warn("Fancytree: ignored duplicate include");
  2612. return;
  2613. }
  2614. /* *****************************************************************************
  2615. * Private functions and variables
  2616. */
  2617. var i, attr,
  2618. FT = null, // initialized below
  2619. TEST_IMG = new RegExp(/\.|\//), // strings are considered image urls if they contain '.' or '/'
  2620. REX_HTML = /[&<>"'\/]/g, // Escape those characters
  2621. REX_TOOLTIP = /[<>"'\/]/g, // Don't escape `&` in tooltips
  2622. RECURSIVE_REQUEST_ERROR = "$recursive_request",
  2623. ENTITY_MAP = {"&": "&amp;", "<": "&lt;", ">": "&gt;", "\"": "&quot;", "'": "&#39;", "/": "&#x2F;"},
  2624. IGNORE_KEYCODES = { 16: true, 17: true, 18: true },
  2625. SPECIAL_KEYCODES = {
  2626. 8: "backspace", 9: "tab", 10: "return", 13: "return",
  2627. // 16: null, 17: null, 18: null, // ignore shift, ctrl, alt
  2628. 19: "pause", 20: "capslock", 27: "esc", 32: "space", 33: "pageup",
  2629. 34: "pagedown", 35: "end", 36: "home", 37: "left", 38: "up",
  2630. 39: "right", 40: "down", 45: "insert", 46: "del", 59: ";", 61: "=",
  2631. 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6",
  2632. 103: "7", 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".",
  2633. 111: "/", 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5",
  2634. 117: "f6", 118: "f7", 119: "f8", 120: "f9", 121: "f10", 122: "f11",
  2635. 123: "f12", 144: "numlock", 145: "scroll", 173: "-", 186: ";", 187: "=",
  2636. 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
  2637. 221: "]", 222: "'"},
  2638. MOUSE_BUTTONS = { 0: "", 1: "left", 2: "middle", 3: "right" },
  2639. // Boolean attributes that can be set with equivalent class names in the LI tags
  2640. // Note: v2.23: checkbox and hideCheckbox are *not* in this list
  2641. CLASS_ATTRS = "active expanded focus folder lazy radiogroup selected unselectable unselectableIgnore".split(" "),
  2642. CLASS_ATTR_MAP = {},
  2643. // Top-level Fancytree attributes, that can be set by dict
  2644. TREE_ATTRS = "columns types".split(" "),
  2645. // TREE_ATTR_MAP = {},
  2646. // Top-level FancytreeNode attributes, that can be set by dict
  2647. NODE_ATTRS = "checkbox expanded extraClasses folder icon iconTooltip key lazy partsel radiogroup refKey selected statusNodeType title tooltip type unselectable unselectableIgnore unselectableStatus".split(" "),
  2648. NODE_ATTR_MAP = {},
  2649. // Mapping of lowercase -> real name (because HTML5 data-... attribute only supports lowercase)
  2650. NODE_ATTR_LOWERCASE_MAP = {},
  2651. // Attribute names that should NOT be added to node.data
  2652. NONE_NODE_DATA_MAP = {"active": true, "children": true, "data": true, "focus": true};
  2653. for(i=0; i<CLASS_ATTRS.length; i++){ CLASS_ATTR_MAP[CLASS_ATTRS[i]] = true; }
  2654. for(i=0; i<NODE_ATTRS.length; i++) {
  2655. attr = NODE_ATTRS[i];
  2656. NODE_ATTR_MAP[attr] = true;
  2657. if( attr !== attr.toLowerCase() ) {
  2658. NODE_ATTR_LOWERCASE_MAP[attr.toLowerCase()] = attr;
  2659. }
  2660. }
  2661. // for(i=0; i<TREE_ATTRS.length; i++) {
  2662. // TREE_ATTR_MAP[TREE_ATTRS[i]] = true;
  2663. // }
  2664. function _assert(cond, msg){
  2665. // TODO: see qunit.js extractStacktrace()
  2666. if(!cond){
  2667. msg = msg ? ": " + msg : "";
  2668. // consoleApply("assert", [!!cond, msg]);
  2669. $.error("Fancytree assertion failed" + msg);
  2670. }
  2671. }
  2672. _assert($.ui, "Fancytree requires jQuery UI (http://jqueryui.com)");
  2673. function consoleApply(method, args){
  2674. var i, s,
  2675. fn = window.console ? window.console[method] : null;
  2676. if(fn){
  2677. try{
  2678. fn.apply(window.console, args);
  2679. } catch(e) {
  2680. // IE 8?
  2681. s = "";
  2682. for( i=0; i<args.length; i++ ) {
  2683. s += args[i];
  2684. }
  2685. fn(s);
  2686. }
  2687. }
  2688. }
  2689. /* support: IE8 Polyfil for Date.now() */
  2690. if( !Date.now ) {
  2691. Date.now = function now() { return new Date().getTime(); };
  2692. }
  2693. /*Return true if x is a FancytreeNode.*/
  2694. function _isNode(x){
  2695. return !!(x.tree && x.statusNodeType !== undefined);
  2696. }
  2697. /** Return true if dotted version string is equal or higher than requested version.
  2698. *
  2699. * See http://jsfiddle.net/mar10/FjSAN/
  2700. */
  2701. function isVersionAtLeast(dottedVersion, major, minor, patch){
  2702. var i, v, t,
  2703. verParts = $.map($.trim(dottedVersion).split("."), function(e){ return parseInt(e, 10); }),
  2704. testParts = $.map(Array.prototype.slice.call(arguments, 1), function(e){ return parseInt(e, 10); });
  2705. for( i = 0; i < testParts.length; i++ ){
  2706. v = verParts[i] || 0;
  2707. t = testParts[i] || 0;
  2708. if( v !== t ){
  2709. return ( v > t );
  2710. }
  2711. }
  2712. return true;
  2713. }
  2714. /** Return a wrapper that calls sub.methodName() and exposes
  2715. * this : tree
  2716. * this._local : tree.ext.EXTNAME
  2717. * this._super : base.methodName.call()
  2718. * this._superApply : base.methodName.apply()
  2719. */
  2720. function _makeVirtualFunction(methodName, tree, base, extension, extName){
  2721. // $.ui.fancytree.debug("_makeVirtualFunction", methodName, tree, base, extension, extName);
  2722. // if(rexTestSuper && !rexTestSuper.test(func)){
  2723. // // extension.methodName() doesn't call _super(), so no wrapper required
  2724. // return func;
  2725. // }
  2726. // Use an immediate function as closure
  2727. var proxy = (function(){
  2728. var prevFunc = tree[methodName], // org. tree method or prev. proxy
  2729. baseFunc = extension[methodName], //
  2730. _local = tree.ext[extName],
  2731. _super = function(){
  2732. return prevFunc.apply(tree, arguments);
  2733. },
  2734. _superApply = function(args){
  2735. return prevFunc.apply(tree, args);
  2736. };
  2737. // Return the wrapper function
  2738. return function(){
  2739. var prevLocal = tree._local,
  2740. prevSuper = tree._super,
  2741. prevSuperApply = tree._superApply;
  2742. try{
  2743. tree._local = _local;
  2744. tree._super = _super;
  2745. tree._superApply = _superApply;
  2746. return baseFunc.apply(tree, arguments);
  2747. }finally{
  2748. tree._local = prevLocal;
  2749. tree._super = prevSuper;
  2750. tree._superApply = prevSuperApply;
  2751. }
  2752. };
  2753. })(); // end of Immediate Function
  2754. return proxy;
  2755. }
  2756. /**
  2757. * Subclass `base` by creating proxy functions
  2758. */
  2759. function _subclassObject(tree, base, extension, extName){
  2760. // $.ui.fancytree.debug("_subclassObject", tree, base, extension, extName);
  2761. for(var attrName in extension){
  2762. if(typeof extension[attrName] === "function"){
  2763. if(typeof tree[attrName] === "function"){
  2764. // override existing method
  2765. tree[attrName] = _makeVirtualFunction(attrName, tree, base, extension, extName);
  2766. }else if(attrName.charAt(0) === "_"){
  2767. // Create private methods in tree.ext.EXTENSION namespace
  2768. tree.ext[extName][attrName] = _makeVirtualFunction(attrName, tree, base, extension, extName);
  2769. }else{
  2770. $.error("Could not override tree." + attrName + ". Use prefix '_' to create tree." + extName + "._" + attrName);
  2771. }
  2772. }else{
  2773. // Create member variables in tree.ext.EXTENSION namespace
  2774. if(attrName !== "options"){
  2775. tree.ext[extName][attrName] = extension[attrName];
  2776. }
  2777. }
  2778. }
  2779. }
  2780. function _getResolvedPromise(context, argArray){
  2781. if(context === undefined){
  2782. return $.Deferred(function(){this.resolve();}).promise();
  2783. }else{
  2784. return $.Deferred(function(){this.resolveWith(context, argArray);}).promise();
  2785. }
  2786. }
  2787. function _getRejectedPromise(context, argArray){
  2788. if(context === undefined){
  2789. return $.Deferred(function(){this.reject();}).promise();
  2790. }else{
  2791. return $.Deferred(function(){this.rejectWith(context, argArray);}).promise();
  2792. }
  2793. }
  2794. function _makeResolveFunc(deferred, context){
  2795. return function(){
  2796. deferred.resolveWith(context);
  2797. };
  2798. }
  2799. function _getElementDataAsDict($el){
  2800. // Evaluate 'data-NAME' attributes with special treatment for 'data-json'.
  2801. var d = $.extend({}, $el.data()),
  2802. json = d.json;
  2803. delete d.fancytree; // added to container by widget factory (old jQuery UI)
  2804. delete d.uiFancytree; // added to container by widget factory
  2805. if( json ) {
  2806. delete d.json;
  2807. // <li data-json='...'> is already returned as object (http://api.jquery.com/data/#data-html5)
  2808. d = $.extend(d, json);
  2809. }
  2810. return d;
  2811. }
  2812. function _escapeTooltip(s){
  2813. return ("" + s).replace(REX_TOOLTIP, function(s) {
  2814. return ENTITY_MAP[s];
  2815. });
  2816. }
  2817. // TODO: use currying
  2818. function _makeNodeTitleMatcher(s){
  2819. s = s.toLowerCase();
  2820. return function(node){
  2821. return node.title.toLowerCase().indexOf(s) >= 0;
  2822. };
  2823. }
  2824. function _makeNodeTitleStartMatcher(s){
  2825. var reMatch = new RegExp("^" + s, "i");
  2826. return function(node){
  2827. return reMatch.test(node.title);
  2828. };
  2829. }
  2830. /* *****************************************************************************
  2831. * FancytreeNode
  2832. */
  2833. /**
  2834. * Creates a new FancytreeNode
  2835. *
  2836. * @class FancytreeNode
  2837. * @classdesc A FancytreeNode represents the hierarchical data model and operations.
  2838. *
  2839. * @param {FancytreeNode} parent
  2840. * @param {NodeData} obj
  2841. *
  2842. * @property {Fancytree} tree The tree instance
  2843. * @property {FancytreeNode} parent The parent node
  2844. * @property {string} key Node id (must be unique inside the tree)
  2845. * @property {string} title Display name (may contain HTML)
  2846. * @property {object} data Contains all extra data that was passed on node creation
  2847. * @property {FancytreeNode[] | null | undefined} children Array of child nodes.<br>
  2848. * For lazy nodes, null or undefined means 'not yet loaded'. Use an empty array
  2849. * to define a node that has no children.
  2850. * @property {boolean} expanded Use isExpanded(), setExpanded() to access this property.
  2851. * @property {string} extraClasses Additional CSS classes, added to the node's `&lt;span>`.<br>
  2852. * Note: use `node.add/remove/toggleClass()` to modify.
  2853. * @property {boolean} folder Folder nodes have different default icons and click behavior.<br>
  2854. * Note: Also non-folders may have children.
  2855. * @property {string} statusNodeType null for standard nodes. Otherwise type of special system node: 'error', 'loading', 'nodata', or 'paging'.
  2856. * @property {boolean} lazy True if this node is loaded on demand, i.e. on first expansion.
  2857. * @property {boolean} selected Use isSelected(), setSelected() to access this property.
  2858. * @property {string} tooltip Alternative description used as hover popup
  2859. * @property {string} iconTooltip Description used as hover popup for icon. @since 2.27
  2860. * @property {string} type Node type, used with tree.types map. @since 2.27
  2861. */
  2862. function FancytreeNode(parent, obj){
  2863. var i, l, name, cl;
  2864. this.parent = parent;
  2865. this.tree = parent.tree;
  2866. this.ul = null;
  2867. this.li = null; // <li id='key' ftnode=this> tag
  2868. this.statusNodeType = null; // if this is a temp. node to display the status of its parent
  2869. this._isLoading = false; // if this node itself is loading
  2870. this._error = null; // {message: '...'} if a load error occurred
  2871. this.data = {};
  2872. // TODO: merge this code with node.toDict()
  2873. // copy attributes from obj object
  2874. for(i=0, l=NODE_ATTRS.length; i<l; i++){
  2875. name = NODE_ATTRS[i];
  2876. this[name] = obj[name];
  2877. }
  2878. // unselectableIgnore and unselectableStatus imply unselectable
  2879. if( this.unselectableIgnore != null || this.unselectableStatus != null ) {
  2880. this.unselectable = true;
  2881. }
  2882. if( obj.hideCheckbox ) {
  2883. $.error("'hideCheckbox' node option was removed in v2.23.0: use 'checkbox: false'");
  2884. }
  2885. // node.data += obj.data
  2886. if(obj.data){
  2887. $.extend(this.data, obj.data);
  2888. }
  2889. // Copy all other attributes to this.data.NAME
  2890. for(name in obj){
  2891. if(!NODE_ATTR_MAP[name] && !$.isFunction(obj[name]) && !NONE_NODE_DATA_MAP[name]){
  2892. // node.data.NAME = obj.NAME
  2893. this.data[name] = obj[name];
  2894. }
  2895. }
  2896. // Fix missing key
  2897. if( this.key == null ){ // test for null OR undefined
  2898. if( this.tree.options.defaultKey ) {
  2899. this.key = this.tree.options.defaultKey(this);
  2900. _assert(this.key, "defaultKey() must return a unique key");
  2901. } else {
  2902. this.key = "_" + (FT._nextNodeKey++);
  2903. }
  2904. } else {
  2905. this.key = "" + this.key; // Convert to string (#217)
  2906. }
  2907. // Fix tree.activeNode
  2908. // TODO: not elegant: we use obj.active as marker to set tree.activeNode
  2909. // when loading from a dictionary.
  2910. if(obj.active){
  2911. _assert(this.tree.activeNode === null, "only one active node allowed");
  2912. this.tree.activeNode = this;
  2913. }
  2914. if( obj.selected ){ // #186
  2915. this.tree.lastSelectedNode = this;
  2916. }
  2917. // TODO: handle obj.focus = true
  2918. // Create child nodes
  2919. cl = obj.children;
  2920. if( cl ){
  2921. if( cl.length ){
  2922. this._setChildren(cl);
  2923. } else {
  2924. // if an empty array was passed for a lazy node, keep it, in order to mark it 'loaded'
  2925. this.children = this.lazy ? [] : null;
  2926. }
  2927. } else {
  2928. this.children = null;
  2929. }
  2930. // Add to key/ref map (except for root node)
  2931. // if( parent ) {
  2932. this.tree._callHook("treeRegisterNode", this.tree, true, this);
  2933. // }
  2934. }
  2935. FancytreeNode.prototype = /** @lends FancytreeNode# */{
  2936. /* Return the direct child FancytreeNode with a given key, index. */
  2937. _findDirectChild: function(ptr){
  2938. var i, l,
  2939. cl = this.children;
  2940. if(cl){
  2941. if(typeof ptr === "string"){
  2942. for(i=0, l=cl.length; i<l; i++){
  2943. if(cl[i].key === ptr){
  2944. return cl[i];
  2945. }
  2946. }
  2947. }else if(typeof ptr === "number"){
  2948. return this.children[ptr];
  2949. }else if(ptr.parent === this){
  2950. return ptr;
  2951. }
  2952. }
  2953. return null;
  2954. },
  2955. // TODO: activate()
  2956. // TODO: activateSilently()
  2957. /* Internal helper called in recursive addChildren sequence.*/
  2958. _setChildren: function(children){
  2959. _assert(children && (!this.children || this.children.length === 0), "only init supported");
  2960. this.children = [];
  2961. for(var i=0, l=children.length; i<l; i++){
  2962. this.children.push(new FancytreeNode(this, children[i]));
  2963. }
  2964. },
  2965. /**
  2966. * Append (or insert) a list of child nodes.
  2967. *
  2968. * @param {NodeData[]} children array of child node definitions (also single child accepted)
  2969. * @param {FancytreeNode | string | Integer} [insertBefore] child node (or key or index of such).
  2970. * If omitted, the new children are appended.
  2971. * @returns {FancytreeNode} first child added
  2972. *
  2973. * @see FancytreeNode#applyPatch
  2974. */
  2975. addChildren: function(children, insertBefore){
  2976. var i, l, pos,
  2977. origFirstChild = this.getFirstChild(),
  2978. origLastChild = this.getLastChild(),
  2979. firstNode = null,
  2980. nodeList = [];
  2981. if($.isPlainObject(children) ){
  2982. children = [children];
  2983. }
  2984. if(!this.children){
  2985. this.children = [];
  2986. }
  2987. for(i=0, l=children.length; i<l; i++){
  2988. nodeList.push(new FancytreeNode(this, children[i]));
  2989. }
  2990. firstNode = nodeList[0];
  2991. if(insertBefore == null){
  2992. this.children = this.children.concat(nodeList);
  2993. }else{
  2994. // Returns null if insertBefore is not a direct child:
  2995. insertBefore = this._findDirectChild(insertBefore);
  2996. pos = $.inArray(insertBefore, this.children);
  2997. _assert(pos >= 0, "insertBefore must be an existing child");
  2998. // insert nodeList after children[pos]
  2999. this.children.splice.apply(this.children, [pos, 0].concat(nodeList));
  3000. }
  3001. if ( origFirstChild && !insertBefore ) {
  3002. // #708: Fast path -- don't render every child of root, just the new ones!
  3003. // #723, #729: but only if it's appended to an existing child list
  3004. for(i=0, l=nodeList.length; i<l; i++) {
  3005. nodeList[i].render(); // New nodes were never rendered before
  3006. }
  3007. // Adjust classes where status may have changed
  3008. // Has a first child
  3009. if (origFirstChild !== this.getFirstChild()) {
  3010. // Different first child -- recompute classes
  3011. origFirstChild.renderStatus();
  3012. }
  3013. if (origLastChild !== this.getLastChild()) {
  3014. // Different last child -- recompute classes
  3015. origLastChild.renderStatus();
  3016. }
  3017. } else if( !this.parent || this.parent.ul || this.tr ){
  3018. // render if the parent was rendered (or this is a root node)
  3019. this.render();
  3020. }
  3021. if( this.tree.options.selectMode === 3 ){
  3022. this.fixSelection3FromEndNodes();
  3023. }
  3024. this.triggerModifyChild("add", nodeList.length === 1 ? nodeList[0] : null);
  3025. return firstNode;
  3026. },
  3027. /**
  3028. * Add class to node's span tag and to .extraClasses.
  3029. *
  3030. * @param {string} className class name
  3031. *
  3032. * @since 2.17
  3033. */
  3034. addClass: function(className){
  3035. return this.toggleClass(className, true);
  3036. },
  3037. /**
  3038. * Append or prepend a node, or append a child node.
  3039. *
  3040. * This a convenience function that calls addChildren()
  3041. *
  3042. * @param {NodeData} node node definition
  3043. * @param {string} [mode=child] 'before', 'after', 'firstChild', or 'child' ('over' is a synonym for 'child')
  3044. * @returns {FancytreeNode} new node
  3045. */
  3046. addNode: function(node, mode){
  3047. if(mode === undefined || mode === "over"){
  3048. mode = "child";
  3049. }
  3050. switch(mode){
  3051. case "after":
  3052. return this.getParent().addChildren(node, this.getNextSibling());
  3053. case "before":
  3054. return this.getParent().addChildren(node, this);
  3055. case "firstChild":
  3056. // Insert before the first child if any
  3057. var insertBefore = (this.children ? this.children[0] : null);
  3058. return this.addChildren(node, insertBefore);
  3059. case "child":
  3060. case "over":
  3061. return this.addChildren(node);
  3062. }
  3063. _assert(false, "Invalid mode: " + mode);
  3064. },
  3065. /**Add child status nodes that indicate 'More...', etc.
  3066. *
  3067. * This also maintains the node's `partload` property.
  3068. * @param {boolean|object} node optional node definition. Pass `false` to remove all paging nodes.
  3069. * @param {string} [mode='child'] 'child'|firstChild'
  3070. * @since 2.15
  3071. */
  3072. addPagingNode: function(node, mode){
  3073. var i, n;
  3074. mode = mode || "child";
  3075. if( node === false ) {
  3076. for(i=this.children.length-1; i >= 0; i--) {
  3077. n = this.children[i];
  3078. if( n.statusNodeType === "paging" ) {
  3079. this.removeChild(n);
  3080. }
  3081. }
  3082. this.partload = false;
  3083. return;
  3084. }
  3085. node = $.extend({
  3086. title: this.tree.options.strings.moreData,
  3087. statusNodeType: "paging",
  3088. icon: false
  3089. }, node);
  3090. this.partload = true;
  3091. return this.addNode(node, mode);
  3092. },
  3093. /**
  3094. * Append new node after this.
  3095. *
  3096. * This a convenience function that calls addNode(node, 'after')
  3097. *
  3098. * @param {NodeData} node node definition
  3099. * @returns {FancytreeNode} new node
  3100. */
  3101. appendSibling: function(node){
  3102. return this.addNode(node, "after");
  3103. },
  3104. /**
  3105. * Modify existing child nodes.
  3106. *
  3107. * @param {NodePatch} patch
  3108. * @returns {$.Promise}
  3109. * @see FancytreeNode#addChildren
  3110. */
  3111. applyPatch: function(patch) {
  3112. // patch [key, null] means 'remove'
  3113. if(patch === null){
  3114. this.remove();
  3115. return _getResolvedPromise(this);
  3116. }
  3117. // TODO: make sure that root node is not collapsed or modified
  3118. // copy (most) attributes to node.ATTR or node.data.ATTR
  3119. var name, promise, v,
  3120. IGNORE_MAP = { children: true, expanded: true, parent: true }; // TODO: should be global
  3121. for(name in patch){
  3122. v = patch[name];
  3123. if( !IGNORE_MAP[name] && !$.isFunction(v)){
  3124. if(NODE_ATTR_MAP[name]){
  3125. this[name] = v;
  3126. }else{
  3127. this.data[name] = v;
  3128. }
  3129. }
  3130. }
  3131. // Remove and/or create children
  3132. if(patch.hasOwnProperty("children")){
  3133. this.removeChildren();
  3134. if(patch.children){ // only if not null and not empty list
  3135. // TODO: addChildren instead?
  3136. this._setChildren(patch.children);
  3137. }
  3138. // TODO: how can we APPEND or INSERT child nodes?
  3139. }
  3140. if(this.isVisible()){
  3141. this.renderTitle();
  3142. this.renderStatus();
  3143. }
  3144. // Expand collapse (final step, since this may be async)
  3145. if(patch.hasOwnProperty("expanded")){
  3146. promise = this.setExpanded(patch.expanded);
  3147. }else{
  3148. promise = _getResolvedPromise(this);
  3149. }
  3150. return promise;
  3151. },
  3152. /** Collapse all sibling nodes.
  3153. * @returns {$.Promise}
  3154. */
  3155. collapseSiblings: function() {
  3156. return this.tree._callHook("nodeCollapseSiblings", this);
  3157. },
  3158. /** Copy this node as sibling or child of `node`.
  3159. *
  3160. * @param {FancytreeNode} node source node
  3161. * @param {string} [mode=child] 'before' | 'after' | 'child'
  3162. * @param {Function} [map] callback function(NodeData) that could modify the new node
  3163. * @returns {FancytreeNode} new
  3164. */
  3165. copyTo: function(node, mode, map) {
  3166. return node.addNode(this.toDict(true, map), mode);
  3167. },
  3168. /** Count direct and indirect children.
  3169. *
  3170. * @param {boolean} [deep=true] pass 'false' to only count direct children
  3171. * @returns {int} number of child nodes
  3172. */
  3173. countChildren: function(deep) {
  3174. var cl = this.children, i, l, n;
  3175. if( !cl ){
  3176. return 0;
  3177. }
  3178. n = cl.length;
  3179. if(deep !== false){
  3180. for(i=0, l=n; i<l; i++){
  3181. n += cl[i].countChildren();
  3182. }
  3183. }
  3184. return n;
  3185. },
  3186. // TODO: deactivate()
  3187. /** Write to browser console if debugLevel >= 4 (prepending node info)
  3188. *
  3189. * @param {*} msg string or object or array of such
  3190. */
  3191. debug: function(msg){
  3192. if( this.tree.options.debugLevel >= 4 ) {
  3193. Array.prototype.unshift.call(arguments, this.toString());
  3194. consoleApply("log", arguments);
  3195. }
  3196. },
  3197. /** Deprecated.
  3198. * @deprecated since 2014-02-16. Use resetLazy() instead.
  3199. */
  3200. discard: function(){
  3201. this.warn("FancytreeNode.discard() is deprecated since 2014-02-16. Use .resetLazy() instead.");
  3202. return this.resetLazy();
  3203. },
  3204. /** Remove DOM elements for all descendents. May be called on .collapse event
  3205. * to keep the DOM small.
  3206. * @param {boolean} [includeSelf=false]
  3207. */
  3208. discardMarkup: function(includeSelf){
  3209. var fn = includeSelf ? "nodeRemoveMarkup" : "nodeRemoveChildMarkup";
  3210. this.tree._callHook(fn, this);
  3211. },
  3212. /** Write error to browser console if debugLevel >= 1 (prepending tree info)
  3213. *
  3214. * @param {*} msg string or object or array of such
  3215. */
  3216. error: function(msg){
  3217. if( this.options.debugLevel >= 1 ) {
  3218. Array.prototype.unshift.call(arguments, this.toString());
  3219. consoleApply("error", arguments);
  3220. }
  3221. },
  3222. /**Find all nodes that match condition (excluding self).
  3223. *
  3224. * @param {string | function(node)} match title string to search for, or a
  3225. * callback function that returns `true` if a node is matched.
  3226. * @returns {FancytreeNode[]} array of nodes (may be empty)
  3227. */
  3228. findAll: function(match) {
  3229. match = $.isFunction(match) ? match : _makeNodeTitleMatcher(match);
  3230. var res = [];
  3231. this.visit(function(n){
  3232. if(match(n)){
  3233. res.push(n);
  3234. }
  3235. });
  3236. return res;
  3237. },
  3238. /**Find first node that matches condition (excluding self).
  3239. *
  3240. * @param {string | function(node)} match title string to search for, or a
  3241. * callback function that returns `true` if a node is matched.
  3242. * @returns {FancytreeNode} matching node or null
  3243. * @see FancytreeNode#findAll
  3244. */
  3245. findFirst: function(match) {
  3246. match = $.isFunction(match) ? match : _makeNodeTitleMatcher(match);
  3247. var res = null;
  3248. this.visit(function(n){
  3249. if(match(n)){
  3250. res = n;
  3251. return false;
  3252. }
  3253. });
  3254. return res;
  3255. },
  3256. /* Apply selection state (internal use only) */
  3257. _changeSelectStatusAttrs: function(state) {
  3258. var changed = false,
  3259. opts = this.tree.options,
  3260. unselectable = FT.evalOption("unselectable", this, this, opts, false),
  3261. unselectableStatus = FT.evalOption("unselectableStatus", this, this, opts, undefined);
  3262. if( unselectable && unselectableStatus != null ) {
  3263. state = unselectableStatus;
  3264. }
  3265. switch(state){
  3266. case false:
  3267. changed = ( this.selected || this.partsel );
  3268. this.selected = false;
  3269. this.partsel = false;
  3270. break;
  3271. case true:
  3272. changed = ( !this.selected || !this.partsel );
  3273. this.selected = true;
  3274. this.partsel = true;
  3275. break;
  3276. case undefined:
  3277. changed = ( this.selected || !this.partsel );
  3278. this.selected = false;
  3279. this.partsel = true;
  3280. break;
  3281. default:
  3282. _assert(false, "invalid state: " + state);
  3283. }
  3284. // this.debug("fixSelection3AfterLoad() _changeSelectStatusAttrs()", state, changed);
  3285. if( changed ){
  3286. this.renderStatus();
  3287. }
  3288. return changed;
  3289. },
  3290. /**
  3291. * Fix selection status, after this node was (de)selected in multi-hier mode.
  3292. * This includes (de)selecting all children.
  3293. */
  3294. fixSelection3AfterClick: function(callOpts) {
  3295. var flag = this.isSelected();
  3296. // this.debug("fixSelection3AfterClick()");
  3297. this.visit(function(node){
  3298. node._changeSelectStatusAttrs(flag);
  3299. });
  3300. this.fixSelection3FromEndNodes(callOpts);
  3301. },
  3302. /**
  3303. * Fix selection status for multi-hier mode.
  3304. * Only end-nodes are considered to update the descendants branch and parents.
  3305. * Should be called after this node has loaded new children or after
  3306. * children have been modified using the API.
  3307. */
  3308. fixSelection3FromEndNodes: function(callOpts) {
  3309. var opts = this.tree.options;
  3310. // this.debug("fixSelection3FromEndNodes()");
  3311. _assert(opts.selectMode === 3, "expected selectMode 3");
  3312. // Visit all end nodes and adjust their parent's `selected` and `partsel`
  3313. // attributes. Return selection state true, false, or undefined.
  3314. function _walk(node){
  3315. var i, l, child, s, state, allSelected, someSelected, unselIgnore, unselState,
  3316. children = node.children;
  3317. if( children && children.length ){
  3318. // check all children recursively
  3319. allSelected = true;
  3320. someSelected = false;
  3321. for( i=0, l=children.length; i<l; i++ ){
  3322. child = children[i];
  3323. // the selection state of a node is not relevant; we need the end-nodes
  3324. s = _walk(child);
  3325. // if( !child.unselectableIgnore ) {
  3326. unselIgnore = FT.evalOption("unselectableIgnore", child, child, opts, false);
  3327. if( !unselIgnore ) {
  3328. if( s !== false ) {
  3329. someSelected = true;
  3330. }
  3331. if( s !== true ) {
  3332. allSelected = false;
  3333. }
  3334. }
  3335. }
  3336. state = allSelected ? true : (someSelected ? undefined : false);
  3337. }else{
  3338. // This is an end-node: simply report the status
  3339. unselState = FT.evalOption("unselectableStatus", node, node, opts, undefined);
  3340. state = ( unselState == null ) ? !!node.selected : !!unselState;
  3341. }
  3342. node._changeSelectStatusAttrs(state);
  3343. return state;
  3344. }
  3345. _walk(this);
  3346. // Update parent's state
  3347. this.visitParents(function(node){
  3348. var i, l, child, state, unselIgnore, unselState,
  3349. children = node.children,
  3350. allSelected = true,
  3351. someSelected = false;
  3352. for( i=0, l=children.length; i<l; i++ ){
  3353. child = children[i];
  3354. unselIgnore = FT.evalOption("unselectableIgnore", child, child, opts, false);
  3355. if( !unselIgnore ) {
  3356. unselState = FT.evalOption("unselectableStatus", child, child, opts, undefined);
  3357. state = ( unselState == null ) ? !!child.selected : !!unselState;
  3358. // When fixing the parents, we trust the sibling status (i.e.
  3359. // we don't recurse)
  3360. if( state || child.partsel ) {
  3361. someSelected = true;
  3362. }
  3363. if( !state ) {
  3364. allSelected = false;
  3365. }
  3366. }
  3367. }
  3368. state = allSelected ? true : (someSelected ? undefined : false);
  3369. node._changeSelectStatusAttrs(state);
  3370. });
  3371. },
  3372. // TODO: focus()
  3373. /**
  3374. * Update node data. If dict contains 'children', then also replace
  3375. * the hole sub tree.
  3376. * @param {NodeData} dict
  3377. *
  3378. * @see FancytreeNode#addChildren
  3379. * @see FancytreeNode#applyPatch
  3380. */
  3381. fromDict: function(dict) {
  3382. // copy all other attributes to this.data.xxx
  3383. for(var name in dict){
  3384. if(NODE_ATTR_MAP[name]){
  3385. // node.NAME = dict.NAME
  3386. this[name] = dict[name];
  3387. }else if(name === "data"){
  3388. // node.data += dict.data
  3389. $.extend(this.data, dict.data);
  3390. }else if(!$.isFunction(dict[name]) && !NONE_NODE_DATA_MAP[name]){
  3391. // node.data.NAME = dict.NAME
  3392. this.data[name] = dict[name];
  3393. }
  3394. }
  3395. if(dict.children){
  3396. // recursively set children and render
  3397. this.removeChildren();
  3398. this.addChildren(dict.children);
  3399. }
  3400. this.renderTitle();
  3401. /*
  3402. var children = dict.children;
  3403. if(children === undefined){
  3404. this.data = $.extend(this.data, dict);
  3405. this.render();
  3406. return;
  3407. }
  3408. dict = $.extend({}, dict);
  3409. dict.children = undefined;
  3410. this.data = $.extend(this.data, dict);
  3411. this.removeChildren();
  3412. this.addChild(children);
  3413. */
  3414. },
  3415. /** Return the list of child nodes (undefined for unexpanded lazy nodes).
  3416. * @returns {FancytreeNode[] | undefined}
  3417. */
  3418. getChildren: function() {
  3419. if(this.hasChildren() === undefined){ // TODO: only required for lazy nodes?
  3420. return undefined; // Lazy node: unloaded, currently loading, or load error
  3421. }
  3422. return this.children;
  3423. },
  3424. /** Return the first child node or null.
  3425. * @returns {FancytreeNode | null}
  3426. */
  3427. getFirstChild: function() {
  3428. return this.children ? this.children[0] : null;
  3429. },
  3430. /** Return the 0-based child index.
  3431. * @returns {int}
  3432. */
  3433. getIndex: function() {
  3434. // return this.parent.children.indexOf(this);
  3435. return $.inArray(this, this.parent.children); // indexOf doesn't work in IE7
  3436. },
  3437. /** Return the hierarchical child index (1-based, e.g. '3.2.4').
  3438. * @param {string} [separator="."]
  3439. * @param {int} [digits=1]
  3440. * @returns {string}
  3441. */
  3442. getIndexHier: function(separator, digits) {
  3443. separator = separator || ".";
  3444. var s,
  3445. res = [];
  3446. $.each(this.getParentList(false, true), function(i, o){
  3447. s = "" + (o.getIndex() + 1);
  3448. if( digits ){
  3449. // prepend leading zeroes
  3450. s = ("0000000" + s).substr(-digits);
  3451. }
  3452. res.push(s);
  3453. });
  3454. return res.join(separator);
  3455. },
  3456. /** Return the parent keys separated by options.keyPathSeparator, e.g. "id_1/id_17/id_32".
  3457. * @param {boolean} [excludeSelf=false]
  3458. * @returns {string}
  3459. */
  3460. getKeyPath: function(excludeSelf) {
  3461. var path = [],
  3462. sep = this.tree.options.keyPathSeparator;
  3463. this.visitParents(function(n){
  3464. if(n.parent){
  3465. path.unshift(n.key);
  3466. }
  3467. }, !excludeSelf);
  3468. return sep + path.join(sep);
  3469. },
  3470. /** Return the last child of this node or null.
  3471. * @returns {FancytreeNode | null}
  3472. */
  3473. getLastChild: function() {
  3474. return this.children ? this.children[this.children.length - 1] : null;
  3475. },
  3476. /** Return node depth. 0: System root node, 1: visible top-level node, 2: first sub-level, ... .
  3477. * @returns {int}
  3478. */
  3479. getLevel: function() {
  3480. var level = 0,
  3481. dtn = this.parent;
  3482. while( dtn ) {
  3483. level++;
  3484. dtn = dtn.parent;
  3485. }
  3486. return level;
  3487. },
  3488. /** Return the successor node (under the same parent) or null.
  3489. * @returns {FancytreeNode | null}
  3490. */
  3491. getNextSibling: function() {
  3492. // TODO: use indexOf, if available: (not in IE6)
  3493. if( this.parent ){
  3494. var i, l,
  3495. ac = this.parent.children;
  3496. for(i=0, l=ac.length-1; i<l; i++){ // up to length-2, so next(last) = null
  3497. if( ac[i] === this ){
  3498. return ac[i+1];
  3499. }
  3500. }
  3501. }
  3502. return null;
  3503. },
  3504. /** Return the parent node (null for the system root node).
  3505. * @returns {FancytreeNode | null}
  3506. */
  3507. getParent: function() {
  3508. // TODO: return null for top-level nodes?
  3509. return this.parent;
  3510. },
  3511. /** Return an array of all parent nodes (top-down).
  3512. * @param {boolean} [includeRoot=false] Include the invisible system root node.
  3513. * @param {boolean} [includeSelf=false] Include the node itself.
  3514. * @returns {FancytreeNode[]}
  3515. */
  3516. getParentList: function(includeRoot, includeSelf) {
  3517. var l = [],
  3518. dtn = includeSelf ? this : this.parent;
  3519. while( dtn ) {
  3520. if( includeRoot || dtn.parent ){
  3521. l.unshift(dtn);
  3522. }
  3523. dtn = dtn.parent;
  3524. }
  3525. return l;
  3526. },
  3527. /** Return the predecessor node (under the same parent) or null.
  3528. * @returns {FancytreeNode | null}
  3529. */
  3530. getPrevSibling: function() {
  3531. if( this.parent ){
  3532. var i, l,
  3533. ac = this.parent.children;
  3534. for(i=1, l=ac.length; i<l; i++){ // start with 1, so prev(first) = null
  3535. if( ac[i] === this ){
  3536. return ac[i-1];
  3537. }
  3538. }
  3539. }
  3540. return null;
  3541. },
  3542. /**
  3543. * Return an array of selected descendant nodes.
  3544. * @param {boolean} [stopOnParents=false] only return the topmost selected
  3545. * node (useful with selectMode 3)
  3546. * @returns {FancytreeNode[]}
  3547. */
  3548. getSelectedNodes: function(stopOnParents) {
  3549. var nodeList = [];
  3550. this.visit(function(node){
  3551. if( node.selected ) {
  3552. nodeList.push(node);
  3553. if( stopOnParents === true ){
  3554. return "skip"; // stop processing this branch
  3555. }
  3556. }
  3557. });
  3558. return nodeList;
  3559. },
  3560. /** Return true if node has children. Return undefined if not sure, i.e. the node is lazy and not yet loaded).
  3561. * @returns {boolean | undefined}
  3562. */
  3563. hasChildren: function() {
  3564. if(this.lazy){
  3565. if(this.children == null ){
  3566. // null or undefined: Not yet loaded
  3567. return undefined;
  3568. }else if(this.children.length === 0){
  3569. // Loaded, but response was empty
  3570. return false;
  3571. }else if(this.children.length === 1 && this.children[0].isStatusNode() ){
  3572. // Currently loading or load error
  3573. return undefined;
  3574. }
  3575. return true;
  3576. }
  3577. return !!( this.children && this.children.length );
  3578. },
  3579. /** Return true if node has keyboard focus.
  3580. * @returns {boolean}
  3581. */
  3582. hasFocus: function() {
  3583. return (this.tree.hasFocus() && this.tree.focusNode === this);
  3584. },
  3585. /** Write to browser console if debugLevel >= 3 (prepending node info)
  3586. *
  3587. * @param {*} msg string or object or array of such
  3588. */
  3589. info: function(msg){
  3590. if( this.tree.options.debugLevel >= 3 ) {
  3591. Array.prototype.unshift.call(arguments, this.toString());
  3592. consoleApply("info", arguments);
  3593. }
  3594. },
  3595. /** Return true if node is active (see also FancytreeNode#isSelected).
  3596. * @returns {boolean}
  3597. */
  3598. isActive: function() {
  3599. return (this.tree.activeNode === this);
  3600. },
  3601. /** Return true if node is vertically below `otherNode`, i.e. rendered in a subsequent row.
  3602. * @param {FancytreeNode} otherNode
  3603. * @returns {boolean}
  3604. * @since 2.28
  3605. */
  3606. isBelowOf: function(otherNode) {
  3607. return (this.getIndexHier(".", 5) > otherNode.getIndexHier(".", 5));
  3608. },
  3609. /** Return true if node is a direct child of otherNode.
  3610. * @param {FancytreeNode} otherNode
  3611. * @returns {boolean}
  3612. */
  3613. isChildOf: function(otherNode) {
  3614. return (this.parent && this.parent === otherNode);
  3615. },
  3616. /** Return true, if node is a direct or indirect sub node of otherNode.
  3617. * @param {FancytreeNode} otherNode
  3618. * @returns {boolean}
  3619. */
  3620. isDescendantOf: function(otherNode) {
  3621. if(!otherNode || otherNode.tree !== this.tree){
  3622. return false;
  3623. }
  3624. var p = this.parent;
  3625. while( p ) {
  3626. if( p === otherNode ){
  3627. return true;
  3628. }
  3629. if( p === p.parent ) { $.error("Recursive parent link: " + p); }
  3630. p = p.parent;
  3631. }
  3632. return false;
  3633. },
  3634. /** Return true if node is expanded.
  3635. * @returns {boolean}
  3636. */
  3637. isExpanded: function() {
  3638. return !!this.expanded;
  3639. },
  3640. /** Return true if node is the first node of its parent's children.
  3641. * @returns {boolean}
  3642. */
  3643. isFirstSibling: function() {
  3644. var p = this.parent;
  3645. return !p || p.children[0] === this;
  3646. },
  3647. /** Return true if node is a folder, i.e. has the node.folder attribute set.
  3648. * @returns {boolean}
  3649. */
  3650. isFolder: function() {
  3651. return !!this.folder;
  3652. },
  3653. /** Return true if node is the last node of its parent's children.
  3654. * @returns {boolean}
  3655. */
  3656. isLastSibling: function() {
  3657. var p = this.parent;
  3658. return !p || p.children[p.children.length-1] === this;
  3659. },
  3660. /** Return true if node is lazy (even if data was already loaded)
  3661. * @returns {boolean}
  3662. */
  3663. isLazy: function() {
  3664. return !!this.lazy;
  3665. },
  3666. /** Return true if node is lazy and loaded. For non-lazy nodes always return true.
  3667. * @returns {boolean}
  3668. */
  3669. isLoaded: function() {
  3670. return !this.lazy || this.hasChildren() !== undefined; // Also checks if the only child is a status node
  3671. },
  3672. /** Return true if children are currently beeing loaded, i.e. a Ajax request is pending.
  3673. * @returns {boolean}
  3674. */
  3675. isLoading: function() {
  3676. return !!this._isLoading;
  3677. },
  3678. /*
  3679. * @deprecated since v2.4.0: Use isRootNode() instead
  3680. */
  3681. isRoot: function() {
  3682. return this.isRootNode();
  3683. },
  3684. /** Return true if node is partially selected (tri-state).
  3685. * @returns {boolean}
  3686. * @since 2.23
  3687. */
  3688. isPartsel: function() {
  3689. return !this.selected && !!this.partsel;
  3690. },
  3691. /** (experimental) Return true if this is partially loaded.
  3692. * @returns {boolean}
  3693. * @since 2.15
  3694. */
  3695. isPartload: function() {
  3696. return !!this.partload;
  3697. },
  3698. /** Return true if this is the (invisible) system root node.
  3699. * @returns {boolean}
  3700. * @since 2.4
  3701. */
  3702. isRootNode: function() {
  3703. return (this.tree.rootNode === this);
  3704. },
  3705. /** Return true if node is selected, i.e. has a checkmark set (see also FancytreeNode#isActive).
  3706. * @returns {boolean}
  3707. */
  3708. isSelected: function() {
  3709. return !!this.selected;
  3710. },
  3711. /** Return true if this node is a temporarily generated system node like
  3712. * 'loading', 'paging', or 'error' (node.statusNodeType contains the type).
  3713. * @returns {boolean}
  3714. */
  3715. isStatusNode: function() {
  3716. return !!this.statusNodeType;
  3717. },
  3718. /** Return true if this node is a status node of type 'paging'.
  3719. * @returns {boolean}
  3720. * @since 2.15
  3721. */
  3722. isPagingNode: function() {
  3723. return this.statusNodeType === "paging";
  3724. },
  3725. /** Return true if this a top level node, i.e. a direct child of the (invisible) system root node.
  3726. * @returns {boolean}
  3727. * @since 2.4
  3728. */
  3729. isTopLevel: function() {
  3730. return (this.tree.rootNode === this.parent);
  3731. },
  3732. /** Return true if node is lazy and not yet loaded. For non-lazy nodes always return false.
  3733. * @returns {boolean}
  3734. */
  3735. isUndefined: function() {
  3736. return this.hasChildren() === undefined; // also checks if the only child is a status node
  3737. },
  3738. /** Return true if all parent nodes are expanded. Note: this does not check
  3739. * whether the node is scrolled into the visible part of the screen.
  3740. * @returns {boolean}
  3741. */
  3742. isVisible: function() {
  3743. var i, l,
  3744. parents = this.getParentList(false, false);
  3745. for(i=0, l=parents.length; i<l; i++){
  3746. if( ! parents[i].expanded ){ return false; }
  3747. }
  3748. return true;
  3749. },
  3750. /** Deprecated.
  3751. * @deprecated since 2014-02-16: use load() instead.
  3752. */
  3753. lazyLoad: function(discard) {
  3754. this.warn("FancytreeNode.lazyLoad() is deprecated since 2014-02-16. Use .load() instead.");
  3755. return this.load(discard);
  3756. },
  3757. /**
  3758. * Load all children of a lazy node if neccessary. The <i>expanded</i> state is maintained.
  3759. * @param {boolean} [forceReload=false] Pass true to discard any existing nodes before. Otherwise this method does nothing if the node was already loaded.
  3760. * @returns {$.Promise}
  3761. */
  3762. load: function(forceReload) {
  3763. var res, source,
  3764. that = this,
  3765. wasExpanded = this.isExpanded();
  3766. _assert( this.isLazy(), "load() requires a lazy node" );
  3767. // _assert( forceReload || this.isUndefined(), "Pass forceReload=true to re-load a lazy node" );
  3768. if( !forceReload && !this.isUndefined() ) {
  3769. return _getResolvedPromise(this);
  3770. }
  3771. if( this.isLoaded() ){
  3772. this.resetLazy(); // also collapses
  3773. }
  3774. // This method is also called by setExpanded() and loadKeyPath(), so we
  3775. // have to avoid recursion.
  3776. source = this.tree._triggerNodeEvent("lazyLoad", this);
  3777. if( source === false ) { // #69
  3778. return _getResolvedPromise(this);
  3779. }
  3780. _assert(typeof source !== "boolean", "lazyLoad event must return source in data.result");
  3781. res = this.tree._callHook("nodeLoadChildren", this, source);
  3782. if( wasExpanded ) {
  3783. this.expanded = true;
  3784. res.always(function(){
  3785. that.render();
  3786. });
  3787. } else {
  3788. res.always(function(){
  3789. that.renderStatus(); // fix expander icon to 'loaded'
  3790. });
  3791. }
  3792. return res;
  3793. },
  3794. /** Expand all parents and optionally scroll into visible area as neccessary.
  3795. * Promise is resolved, when lazy loading and animations are done.
  3796. * @param {object} [opts] passed to `setExpanded()`.
  3797. * Defaults to {noAnimation: false, noEvents: false, scrollIntoView: true}
  3798. * @returns {$.Promise}
  3799. */
  3800. makeVisible: function(opts) {
  3801. var i,
  3802. that = this,
  3803. deferreds = [],
  3804. dfd = new $.Deferred(),
  3805. parents = this.getParentList(false, false),
  3806. len = parents.length,
  3807. effects = !(opts && opts.noAnimation === true),
  3808. scroll = !(opts && opts.scrollIntoView === false);
  3809. // Expand bottom-up, so only the top node is animated
  3810. for(i = len - 1; i >= 0; i--){
  3811. // that.debug("pushexpand" + parents[i]);
  3812. deferreds.push(parents[i].setExpanded(true, opts));
  3813. }
  3814. $.when.apply($, deferreds).done(function(){
  3815. // All expands have finished
  3816. // that.debug("expand DONE", scroll);
  3817. if( scroll ){
  3818. that.scrollIntoView(effects).done(function(){
  3819. // that.debug("scroll DONE");
  3820. dfd.resolve();
  3821. });
  3822. } else {
  3823. dfd.resolve();
  3824. }
  3825. });
  3826. return dfd.promise();
  3827. },
  3828. /** Move this node to targetNode.
  3829. * @param {FancytreeNode} targetNode
  3830. * @param {string} mode <pre>
  3831. * 'child': append this node as last child of targetNode.
  3832. * This is the default. To be compatble with the D'n'd
  3833. * hitMode, we also accept 'over'.
  3834. * 'firstChild': add this node as first child of targetNode.
  3835. * 'before': add this node as sibling before targetNode.
  3836. * 'after': add this node as sibling after targetNode.</pre>
  3837. * @param {function} [map] optional callback(FancytreeNode) to allow modifcations
  3838. */
  3839. moveTo: function(targetNode, mode, map) {
  3840. if(mode === undefined || mode === "over"){
  3841. mode = "child";
  3842. } else if ( mode === "firstChild" ) {
  3843. if( targetNode.children && targetNode.children.length ) {
  3844. mode = "before";
  3845. targetNode = targetNode.children[0];
  3846. } else {
  3847. mode = "child";
  3848. }
  3849. }
  3850. var pos,
  3851. prevParent = this.parent,
  3852. targetParent = (mode === "child") ? targetNode : targetNode.parent;
  3853. if(this === targetNode){
  3854. return;
  3855. }else if( !this.parent ){
  3856. $.error("Cannot move system root");
  3857. }else if( targetParent.isDescendantOf(this) ){
  3858. $.error("Cannot move a node to its own descendant");
  3859. }
  3860. if( targetParent !== prevParent ) {
  3861. prevParent.triggerModifyChild("remove", this);
  3862. }
  3863. // Unlink this node from current parent
  3864. if( this.parent.children.length === 1 ) {
  3865. if( this.parent === targetParent ){
  3866. return; // #258
  3867. }
  3868. this.parent.children = this.parent.lazy ? [] : null;
  3869. this.parent.expanded = false;
  3870. } else {
  3871. pos = $.inArray(this, this.parent.children);
  3872. _assert(pos >= 0, "invalid source parent");
  3873. this.parent.children.splice(pos, 1);
  3874. }
  3875. // Remove from source DOM parent
  3876. // if(this.parent.ul){
  3877. // this.parent.ul.removeChild(this.li);
  3878. // }
  3879. // Insert this node to target parent's child list
  3880. this.parent = targetParent;
  3881. if( targetParent.hasChildren() ) {
  3882. switch(mode) {
  3883. case "child":
  3884. // Append to existing target children
  3885. targetParent.children.push(this);
  3886. break;
  3887. case "before":
  3888. // Insert this node before target node
  3889. pos = $.inArray(targetNode, targetParent.children);
  3890. _assert(pos >= 0, "invalid target parent");
  3891. targetParent.children.splice(pos, 0, this);
  3892. break;
  3893. case "after":
  3894. // Insert this node after target node
  3895. pos = $.inArray(targetNode, targetParent.children);
  3896. _assert(pos >= 0, "invalid target parent");
  3897. targetParent.children.splice(pos+1, 0, this);
  3898. break;
  3899. default:
  3900. $.error("Invalid mode " + mode);
  3901. }
  3902. } else {
  3903. targetParent.children = [ this ];
  3904. }
  3905. // Parent has no <ul> tag yet:
  3906. // if( !targetParent.ul ) {
  3907. // // This is the parent's first child: create UL tag
  3908. // // (Hidden, because it will be
  3909. // targetParent.ul = document.createElement("ul");
  3910. // targetParent.ul.style.display = "none";
  3911. // targetParent.li.appendChild(targetParent.ul);
  3912. // }
  3913. // // Issue 319: Add to target DOM parent (only if node was already rendered(expanded))
  3914. // if(this.li){
  3915. // targetParent.ul.appendChild(this.li);
  3916. // }^
  3917. // Let caller modify the nodes
  3918. if( map ){
  3919. targetNode.visit(map, true);
  3920. }
  3921. if( targetParent === prevParent ) {
  3922. targetParent.triggerModifyChild("move", this);
  3923. } else {
  3924. // prevParent.triggerModifyChild("remove", this);
  3925. targetParent.triggerModifyChild("add", this);
  3926. }
  3927. // Handle cross-tree moves
  3928. if( this.tree !== targetNode.tree ) {
  3929. // Fix node.tree for all source nodes
  3930. // _assert(false, "Cross-tree move is not yet implemented.");
  3931. this.warn("Cross-tree moveTo is experimantal!");
  3932. this.visit(function(n){
  3933. // TODO: fix selection state and activation, ...
  3934. n.tree = targetNode.tree;
  3935. }, true);
  3936. }
  3937. // A collaposed node won't re-render children, so we have to remove it manually
  3938. // if( !targetParent.expanded ){
  3939. // prevParent.ul.removeChild(this.li);
  3940. // }
  3941. // Update HTML markup
  3942. if( !prevParent.isDescendantOf(targetParent)) {
  3943. prevParent.render();
  3944. }
  3945. if( !targetParent.isDescendantOf(prevParent) && targetParent !== prevParent) {
  3946. targetParent.render();
  3947. }
  3948. // TODO: fix selection state
  3949. // TODO: fix active state
  3950. /*
  3951. var tree = this.tree;
  3952. var opts = tree.options;
  3953. var pers = tree.persistence;
  3954. // Always expand, if it's below minExpandLevel
  3955. // tree.logDebug ("%s._addChildNode(%o), l=%o", this, ftnode, ftnode.getLevel());
  3956. if ( opts.minExpandLevel >= ftnode.getLevel() ) {
  3957. // tree.logDebug ("Force expand for %o", ftnode);
  3958. this.bExpanded = true;
  3959. }
  3960. // In multi-hier mode, update the parents selection state
  3961. // DT issue #82: only if not initializing, because the children may not exist yet
  3962. // if( !ftnode.data.isStatusNode() && opts.selectMode==3 && !isInitializing )
  3963. // ftnode._fixSelectionState();
  3964. // In multi-hier mode, update the parents selection state
  3965. if( ftnode.bSelected && opts.selectMode==3 ) {
  3966. var p = this;
  3967. while( p ) {
  3968. if( !p.hasSubSel )
  3969. p._setSubSel(true);
  3970. p = p.parent;
  3971. }
  3972. }
  3973. // render this node and the new child
  3974. if ( tree.bEnableUpdate )
  3975. this.render();
  3976. return ftnode;
  3977. */
  3978. },
  3979. /** Set focus relative to this node and optionally activate.
  3980. *
  3981. * @param {number} where The keyCode that would normally trigger this move,
  3982. * e.g. `$.ui.keyCode.LEFT` would collapse the node if it
  3983. * is expanded or move to the parent oterwise.
  3984. * @param {boolean} [activate=true]
  3985. * @returns {$.Promise}
  3986. */
  3987. navigate: function(where, activate) {
  3988. var i, parents, res,
  3989. handled = true,
  3990. KC = $.ui.keyCode,
  3991. sib = null;
  3992. // Navigate to node
  3993. function _goto(n){
  3994. if( n ){
  3995. // setFocus/setActive will scroll later (if autoScroll is specified)
  3996. try { n.makeVisible({scrollIntoView: false}); } catch(e) {} // #272
  3997. // Node may still be hidden by a filter
  3998. if( ! $(n.span).is(":visible") ) {
  3999. n.debug("Navigate: skipping hidden node");
  4000. n.navigate(where, activate);
  4001. return;
  4002. }
  4003. return activate === false ? n.setFocus() : n.setActive();
  4004. }
  4005. }
  4006. switch( where ) {
  4007. case KC.BACKSPACE:
  4008. if( this.parent && this.parent.parent ) {
  4009. res = _goto(this.parent);
  4010. }
  4011. break;
  4012. case KC.HOME:
  4013. this.tree.visit(function(n){ // goto first visible node
  4014. if( $(n.span).is(":visible") ) {
  4015. res = _goto(n);
  4016. return false;
  4017. }
  4018. });
  4019. break;
  4020. case KC.END:
  4021. this.tree.visit(function(n){ // goto last visible node
  4022. if( $(n.span).is(":visible") ) {
  4023. res = n;
  4024. }
  4025. });
  4026. if( res ) {
  4027. res = _goto(res);
  4028. }
  4029. break;
  4030. case KC.LEFT:
  4031. if( this.expanded ) {
  4032. this.setExpanded(false);
  4033. res = _goto(this);
  4034. } else if( this.parent && this.parent.parent ) {
  4035. res = _goto(this.parent);
  4036. }
  4037. break;
  4038. case KC.RIGHT:
  4039. if( !this.expanded && (this.children || this.lazy) ) {
  4040. this.setExpanded();
  4041. res = _goto(this);
  4042. } else if( this.children && this.children.length ) {
  4043. res = _goto(this.children[0]);
  4044. }
  4045. break;
  4046. case KC.UP:
  4047. sib = this.getPrevSibling();
  4048. // #359: skip hidden sibling nodes, preventing a _goto() recursion
  4049. while( sib && !$(sib.span).is(":visible") ) {
  4050. sib = sib.getPrevSibling();
  4051. }
  4052. while( sib && sib.expanded && sib.children && sib.children.length ) {
  4053. sib = sib.children[sib.children.length - 1];
  4054. }
  4055. if( !sib && this.parent && this.parent.parent ){
  4056. sib = this.parent;
  4057. }
  4058. res = _goto(sib);
  4059. break;
  4060. case KC.DOWN:
  4061. if( this.expanded && this.children && this.children.length ) {
  4062. sib = this.children[0];
  4063. } else {
  4064. parents = this.getParentList(false, true);
  4065. for(i=parents.length-1; i>=0; i--) {
  4066. sib = parents[i].getNextSibling();
  4067. // #359: skip hidden sibling nodes, preventing a _goto() recursion
  4068. while( sib && !$(sib.span).is(":visible") ) {
  4069. sib = sib.getNextSibling();
  4070. }
  4071. if( sib ){ break; }
  4072. }
  4073. }
  4074. res = _goto(sib);
  4075. break;
  4076. default:
  4077. handled = false;
  4078. }
  4079. return res || _getResolvedPromise();
  4080. },
  4081. /**
  4082. * Remove this node (not allowed for system root).
  4083. */
  4084. remove: function() {
  4085. return this.parent.removeChild(this);
  4086. },
  4087. /**
  4088. * Remove childNode from list of direct children.
  4089. * @param {FancytreeNode} childNode
  4090. */
  4091. removeChild: function(childNode) {
  4092. return this.tree._callHook("nodeRemoveChild", this, childNode);
  4093. },
  4094. /**
  4095. * Remove all child nodes and descendents. This converts the node into a leaf.<br>
  4096. * If this was a lazy node, it is still considered 'loaded'; call node.resetLazy()
  4097. * in order to trigger lazyLoad on next expand.
  4098. */
  4099. removeChildren: function() {
  4100. return this.tree._callHook("nodeRemoveChildren", this);
  4101. },
  4102. /**
  4103. * Remove class from node's span tag and .extraClasses.
  4104. *
  4105. * @param {string} className class name
  4106. *
  4107. * @since 2.17
  4108. */
  4109. removeClass: function(className){
  4110. return this.toggleClass(className, false);
  4111. },
  4112. /**
  4113. * This method renders and updates all HTML markup that is required
  4114. * to display this node in its current state.<br>
  4115. * Note:
  4116. * <ul>
  4117. * <li>It should only be neccessary to call this method after the node object
  4118. * was modified by direct access to its properties, because the common
  4119. * API methods (node.setTitle(), moveTo(), addChildren(), remove(), ...)
  4120. * already handle this.
  4121. * <li> {@link FancytreeNode#renderTitle} and {@link FancytreeNode#renderStatus}
  4122. * are implied. If changes are more local, calling only renderTitle() or
  4123. * renderStatus() may be sufficient and faster.
  4124. * </ul>
  4125. *
  4126. * @param {boolean} [force=false] re-render, even if html markup was already created
  4127. * @param {boolean} [deep=false] also render all descendants, even if parent is collapsed
  4128. */
  4129. render: function(force, deep) {
  4130. return this.tree._callHook("nodeRender", this, force, deep);
  4131. },
  4132. /** Create HTML markup for the node's outer &lt;span> (expander, checkbox, icon, and title).
  4133. * Implies {@link FancytreeNode#renderStatus}.
  4134. * @see Fancytree_Hooks#nodeRenderTitle
  4135. */
  4136. renderTitle: function() {
  4137. return this.tree._callHook("nodeRenderTitle", this);
  4138. },
  4139. /** Update element's CSS classes according to node state.
  4140. * @see Fancytree_Hooks#nodeRenderStatus
  4141. */
  4142. renderStatus: function() {
  4143. return this.tree._callHook("nodeRenderStatus", this);
  4144. },
  4145. /**
  4146. * (experimental) Replace this node with `source`.
  4147. * (Currently only available for paging nodes.)
  4148. * @param {NodeData[]} source List of child node definitions
  4149. * @since 2.15
  4150. */
  4151. replaceWith: function(source) {
  4152. var res,
  4153. parent = this.parent,
  4154. pos = $.inArray(this, parent.children),
  4155. that = this;
  4156. _assert( this.isPagingNode(), "replaceWith() currently requires a paging status node" );
  4157. res = this.tree._callHook("nodeLoadChildren", this, source);
  4158. res.done(function(data){
  4159. // New nodes are currently children of `this`.
  4160. var children = that.children;
  4161. // Prepend newly loaded child nodes to `this`
  4162. // Move new children after self
  4163. for( i=0; i<children.length; i++ ) {
  4164. children[i].parent = parent;
  4165. }
  4166. parent.children.splice.apply(parent.children, [pos + 1, 0].concat(children));
  4167. // Remove self
  4168. that.children = null;
  4169. that.remove();
  4170. // Redraw new nodes
  4171. parent.render();
  4172. // TODO: set node.partload = false if this was tha last paging node?
  4173. // parent.addPagingNode(false);
  4174. }).fail(function(){
  4175. that.setExpanded();
  4176. });
  4177. return res;
  4178. // $.error("Not implemented: replaceWith()");
  4179. },
  4180. /**
  4181. * Remove all children, collapse, and set the lazy-flag, so that the lazyLoad
  4182. * event is triggered on next expand.
  4183. */
  4184. resetLazy: function() {
  4185. this.removeChildren();
  4186. this.expanded = false;
  4187. this.lazy = true;
  4188. this.children = undefined;
  4189. this.renderStatus();
  4190. },
  4191. /** Schedule activity for delayed execution (cancel any pending request).
  4192. * scheduleAction('cancel') will only cancel a pending request (if any).
  4193. * @param {string} mode
  4194. * @param {number} ms
  4195. */
  4196. scheduleAction: function(mode, ms) {
  4197. if( this.tree.timer ) {
  4198. clearTimeout(this.tree.timer);
  4199. // this.tree.debug("clearTimeout(%o)", this.tree.timer);
  4200. }
  4201. this.tree.timer = null;
  4202. var self = this; // required for closures
  4203. switch (mode) {
  4204. case "cancel":
  4205. // Simply made sure that timer was cleared
  4206. break;
  4207. case "expand":
  4208. this.tree.timer = setTimeout(function(){
  4209. self.tree.debug("setTimeout: trigger expand");
  4210. self.setExpanded(true);
  4211. }, ms);
  4212. break;
  4213. case "activate":
  4214. this.tree.timer = setTimeout(function(){
  4215. self.tree.debug("setTimeout: trigger activate");
  4216. self.setActive(true);
  4217. }, ms);
  4218. break;
  4219. default:
  4220. $.error("Invalid mode " + mode);
  4221. }
  4222. // this.tree.debug("setTimeout(%s, %s): %s", mode, ms, this.tree.timer);
  4223. },
  4224. /**
  4225. *
  4226. * @param {boolean | PlainObject} [effects=false] animation options.
  4227. * @param {object} [options=null] {topNode: null, effects: ..., parent: ...} this node will remain visible in
  4228. * any case, even if `this` is outside the scroll pane.
  4229. * @returns {$.Promise}
  4230. */
  4231. scrollIntoView: function(effects, options) {
  4232. if( options !== undefined && _isNode(options) ) {
  4233. this.warn("scrollIntoView() with 'topNode' option is deprecated since 2014-05-08. Use 'options.topNode' instead.");
  4234. options = {topNode: options};
  4235. }
  4236. // this.$scrollParent = (this.options.scrollParent === "auto") ? $ul.scrollParent() : $(this.options.scrollParent);
  4237. // this.$scrollParent = this.$scrollParent.length ? this.$scrollParent || this.$container;
  4238. var topNodeY, nodeY, horzScrollbarHeight, containerOffsetTop,
  4239. opts = $.extend({
  4240. effects: (effects === true) ? {duration: 200, queue: false} : effects,
  4241. scrollOfs: this.tree.options.scrollOfs,
  4242. scrollParent: this.tree.options.scrollParent || this.tree.$container,
  4243. topNode: null
  4244. }, options),
  4245. dfd = new $.Deferred(),
  4246. that = this,
  4247. nodeHeight = $(this.span).height(),
  4248. $container = $(opts.scrollParent),
  4249. topOfs = opts.scrollOfs.top || 0,
  4250. bottomOfs = opts.scrollOfs.bottom || 0,
  4251. containerHeight = $container.height(),// - topOfs - bottomOfs,
  4252. scrollTop = $container.scrollTop(),
  4253. $animateTarget = $container,
  4254. isParentWindow = $container[0] === window,
  4255. topNode = opts.topNode || null,
  4256. newScrollTop = null;
  4257. // this.debug("scrollIntoView(), scrollTop=" + scrollTop, opts.scrollOfs);
  4258. // _assert($(this.span).is(":visible"), "scrollIntoView node is invisible"); // otherwise we cannot calc offsets
  4259. if( !$(this.span).is(":visible") ) {
  4260. // We cannot calc offsets for hidden elements
  4261. this.warn("scrollIntoView(): node is invisible.");
  4262. return _getResolvedPromise();
  4263. }
  4264. if( isParentWindow ) {
  4265. nodeY = $(this.span).offset().top;
  4266. topNodeY = (topNode && topNode.span) ? $(topNode.span).offset().top : 0;
  4267. $animateTarget = $("html,body");
  4268. } else {
  4269. _assert($container[0] !== document && $container[0] !== document.body,
  4270. "scrollParent should be a simple element or `window`, not document or body.");
  4271. containerOffsetTop = $container.offset().top,
  4272. nodeY = $(this.span).offset().top - containerOffsetTop + scrollTop; // relative to scroll parent
  4273. topNodeY = topNode ? $(topNode.span).offset().top - containerOffsetTop + scrollTop : 0;
  4274. horzScrollbarHeight = Math.max(0, ($container.innerHeight() - $container[0].clientHeight));
  4275. containerHeight -= horzScrollbarHeight;
  4276. }
  4277. // this.debug(" scrollIntoView(), nodeY=" + nodeY + ", containerHeight=" + containerHeight);
  4278. if( nodeY < (scrollTop + topOfs) ){
  4279. // Node is above visible container area
  4280. newScrollTop = nodeY - topOfs;
  4281. // this.debug(" scrollIntoView(), UPPER newScrollTop=" + newScrollTop);
  4282. }else if((nodeY + nodeHeight) > (scrollTop + containerHeight - bottomOfs)){
  4283. newScrollTop = nodeY + nodeHeight - containerHeight + bottomOfs;
  4284. // this.debug(" scrollIntoView(), LOWER newScrollTop=" + newScrollTop);
  4285. // If a topNode was passed, make sure that it is never scrolled
  4286. // outside the upper border
  4287. if(topNode){
  4288. _assert(topNode.isRootNode() || $(topNode.span).is(":visible"), "topNode must be visible");
  4289. if( topNodeY < newScrollTop ){
  4290. newScrollTop = topNodeY - topOfs;
  4291. // this.debug(" scrollIntoView(), TOP newScrollTop=" + newScrollTop);
  4292. }
  4293. }
  4294. }
  4295. if(newScrollTop !== null){
  4296. // this.debug(" scrollIntoView(), SET newScrollTop=" + newScrollTop);
  4297. if(opts.effects){
  4298. opts.effects.complete = function(){
  4299. dfd.resolveWith(that);
  4300. };
  4301. $animateTarget.stop(true).animate({
  4302. scrollTop: newScrollTop
  4303. }, opts.effects);
  4304. }else{
  4305. $animateTarget[0].scrollTop = newScrollTop;
  4306. dfd.resolveWith(this);
  4307. }
  4308. }else{
  4309. dfd.resolveWith(this);
  4310. }
  4311. return dfd.promise();
  4312. },
  4313. /**Activate this node.
  4314. * @param {boolean} [flag=true] pass false to deactivate
  4315. * @param {object} [opts] additional options. Defaults to {noEvents: false, noFocus: false}
  4316. * @returns {$.Promise}
  4317. */
  4318. setActive: function(flag, opts){
  4319. return this.tree._callHook("nodeSetActive", this, flag, opts);
  4320. },
  4321. /**Expand or collapse this node. Promise is resolved, when lazy loading and animations are done.
  4322. * @param {boolean} [flag=true] pass false to collapse
  4323. * @param {object} [opts] additional options. Defaults to {noAnimation: false, noEvents: false}
  4324. * @returns {$.Promise}
  4325. */
  4326. setExpanded: function(flag, opts){
  4327. return this.tree._callHook("nodeSetExpanded", this, flag, opts);
  4328. },
  4329. /**Set keyboard focus to this node.
  4330. * @param {boolean} [flag=true] pass false to blur
  4331. * @see Fancytree#setFocus
  4332. */
  4333. setFocus: function(flag){
  4334. return this.tree._callHook("nodeSetFocus", this, flag);
  4335. },
  4336. /**Select this node, i.e. check the checkbox.
  4337. * @param {boolean} [flag=true] pass false to deselect
  4338. * @param {object} [opts] additional options. Defaults to {noEvents: false, p
  4339. * propagateDown: null, propagateUp: null, callback: null }
  4340. */
  4341. setSelected: function(flag, opts){
  4342. return this.tree._callHook("nodeSetSelected", this, flag, opts);
  4343. },
  4344. /**Mark a lazy node as 'error', 'loading', 'nodata', or 'ok'.
  4345. * @param {string} status 'error'|'empty'|'ok'
  4346. * @param {string} [message]
  4347. * @param {string} [details]
  4348. */
  4349. setStatus: function(status, message, details){
  4350. return this.tree._callHook("nodeSetStatus", this, status, message, details);
  4351. },
  4352. /**Rename this node.
  4353. * @param {string} title
  4354. */
  4355. setTitle: function(title){
  4356. this.title = title;
  4357. this.renderTitle();
  4358. this.triggerModify("rename");
  4359. },
  4360. /**Sort child list by title.
  4361. * @param {function} [cmp] custom compare function(a, b) that returns -1, 0, or 1 (defaults to sort by title).
  4362. * @param {boolean} [deep=false] pass true to sort all descendant nodes
  4363. */
  4364. sortChildren: function(cmp, deep) {
  4365. var i,l,
  4366. cl = this.children;
  4367. if( !cl ){
  4368. return;
  4369. }
  4370. cmp = cmp || function(a, b) {
  4371. var x = a.title.toLowerCase(),
  4372. y = b.title.toLowerCase();
  4373. return x === y ? 0 : x > y ? 1 : -1;
  4374. };
  4375. cl.sort(cmp);
  4376. if( deep ){
  4377. for(i=0, l=cl.length; i<l; i++){
  4378. if( cl[i].children ){
  4379. cl[i].sortChildren(cmp, "$norender$");
  4380. }
  4381. }
  4382. }
  4383. if( deep !== "$norender$" ){
  4384. this.render();
  4385. }
  4386. this.triggerModifyChild("sort");
  4387. },
  4388. /** Convert node (or whole branch) into a plain object.
  4389. *
  4390. * The result is compatible with node.addChildren().
  4391. *
  4392. * @param {boolean} [recursive=false] include child nodes
  4393. * @param {function} [callback] callback(dict, node) is called for every node, in order to allow modifications
  4394. * @returns {NodeData}
  4395. */
  4396. toDict: function(recursive, callback) {
  4397. var i, l, node,
  4398. dict = {},
  4399. self = this;
  4400. $.each(NODE_ATTRS, function(i, a){
  4401. if(self[a] || self[a] === false){
  4402. dict[a] = self[a];
  4403. }
  4404. });
  4405. if(!$.isEmptyObject(this.data)){
  4406. dict.data = $.extend({}, this.data);
  4407. if($.isEmptyObject(dict.data)){
  4408. delete dict.data;
  4409. }
  4410. }
  4411. if( callback ){
  4412. callback(dict, self);
  4413. }
  4414. if( recursive ) {
  4415. if(this.hasChildren()){
  4416. dict.children = [];
  4417. for(i=0, l=this.children.length; i<l; i++ ){
  4418. node = this.children[i];
  4419. if( !node.isStatusNode() ){
  4420. dict.children.push(node.toDict(true, callback));
  4421. }
  4422. }
  4423. }else{
  4424. // dict.children = null;
  4425. }
  4426. }
  4427. return dict;
  4428. },
  4429. /**
  4430. * Set, clear, or toggle class of node's span tag and .extraClasses.
  4431. *
  4432. * @param {string} className class name (separate multiple classes by space)
  4433. * @param {boolean} [flag] true/false to add/remove class. If omitted, class is toggled.
  4434. * @returns {boolean} true if a class was added
  4435. *
  4436. * @since 2.17
  4437. */
  4438. toggleClass: function(value, flag){
  4439. var className, hasClass,
  4440. rnotwhite = ( /\S+/g ),
  4441. classNames = value.match( rnotwhite ) || [],
  4442. i = 0,
  4443. wasAdded = false,
  4444. statusElem = this[this.tree.statusClassPropName],
  4445. curClasses = (" " + (this.extraClasses || "") + " ");
  4446. // this.info("toggleClass('" + value + "', " + flag + ")", curClasses);
  4447. // Modify DOM element directly if it already exists
  4448. if( statusElem ) {
  4449. $(statusElem).toggleClass(value, flag);
  4450. }
  4451. // Modify node.extraClasses to make this change persistent
  4452. // Toggle if flag was not passed
  4453. while ( className = classNames[ i++ ] ) {
  4454. hasClass = curClasses.indexOf(" " + className + " ") >= 0;
  4455. flag = (flag === undefined) ? (!hasClass) : !!flag;
  4456. if ( flag ) {
  4457. if( !hasClass ) {
  4458. curClasses += className + " ";
  4459. wasAdded = true;
  4460. }
  4461. } else {
  4462. while ( curClasses.indexOf( " " + className + " " ) > -1 ) {
  4463. curClasses = curClasses.replace( " " + className + " ", " " );
  4464. }
  4465. }
  4466. }
  4467. this.extraClasses = $.trim(curClasses);
  4468. // this.info("-> toggleClass('" + value + "', " + flag + "): '" + this.extraClasses + "'");
  4469. return wasAdded;
  4470. },
  4471. /** Flip expanded status. */
  4472. toggleExpanded: function(){
  4473. return this.tree._callHook("nodeToggleExpanded", this);
  4474. },
  4475. /** Flip selection status. */
  4476. toggleSelected: function(){
  4477. return this.tree._callHook("nodeToggleSelected", this);
  4478. },
  4479. toString: function() {
  4480. return "FancytreeNode@" + this.key + "[title='" + this.title + "']";
  4481. // return "<FancytreeNode(#" + this.key + ", '" + this.title + "')>";
  4482. },
  4483. /**
  4484. * Trigger `modifyChild` event on a parent to signal that a child was modified.
  4485. * @param {string} operation Type of change: 'add', 'remove', 'rename', 'move', 'data', ...
  4486. * @param {FancytreeNode} [childNode]
  4487. * @param {object} [extra]
  4488. */
  4489. triggerModifyChild: function(operation, childNode, extra){
  4490. var data,
  4491. modifyChild = this.tree.options.modifyChild;
  4492. if ( modifyChild ){
  4493. if( childNode && childNode.parent !== this ) {
  4494. $.error("childNode " + childNode + " is not a child of " + this);
  4495. }
  4496. data = {
  4497. node: this,
  4498. tree: this.tree,
  4499. operation: operation,
  4500. childNode: childNode || null
  4501. };
  4502. if( extra ) {
  4503. $.extend(data, extra);
  4504. }
  4505. modifyChild({type: "modifyChild"}, data);
  4506. }
  4507. },
  4508. /**
  4509. * Trigger `modifyChild` event on node.parent(!).
  4510. * @param {string} operation Type of change: 'add', 'remove', 'rename', 'move', 'data', ...
  4511. * @param {object} [extra]
  4512. */
  4513. triggerModify: function(operation, extra){
  4514. this.parent.triggerModifyChild(operation, this, extra);
  4515. },
  4516. /** Call fn(node) for all child nodes in hierarchical order (depth-first).<br>
  4517. * Stop iteration, if fn() returns false. Skip current branch, if fn() returns "skip".<br>
  4518. * Return false if iteration was stopped.
  4519. *
  4520. * @param {function} fn the callback function.
  4521. * Return false to stop iteration, return "skip" to skip this node and
  4522. * its children only.
  4523. * @param {boolean} [includeSelf=false]
  4524. * @returns {boolean}
  4525. */
  4526. visit: function(fn, includeSelf) {
  4527. var i, l,
  4528. res = true,
  4529. children = this.children;
  4530. if( includeSelf === true ) {
  4531. res = fn(this);
  4532. if( res === false || res === "skip" ){
  4533. return res;
  4534. }
  4535. }
  4536. if(children){
  4537. for(i=0, l=children.length; i<l; i++){
  4538. res = children[i].visit(fn, true);
  4539. if( res === false ){
  4540. break;
  4541. }
  4542. }
  4543. }
  4544. return res;
  4545. },
  4546. /** Call fn(node) for all child nodes and recursively load lazy children.<br>
  4547. * <b>Note:</b> If you need this method, you probably should consider to review
  4548. * your architecture! Recursivley loading nodes is a perfect way for lazy
  4549. * programmers to flood the server with requests ;-)
  4550. *
  4551. * @param {function} [fn] optional callback function.
  4552. * Return false to stop iteration, return "skip" to skip this node and
  4553. * its children only.
  4554. * @param {boolean} [includeSelf=false]
  4555. * @returns {$.Promise}
  4556. * @since 2.4
  4557. */
  4558. visitAndLoad: function(fn, includeSelf, _recursion) {
  4559. var dfd, res, loaders,
  4560. node = this;
  4561. // node.debug("visitAndLoad");
  4562. if( fn && includeSelf === true ) {
  4563. res = fn(node);
  4564. if( res === false || res === "skip" ) {
  4565. return _recursion ? res : _getResolvedPromise();
  4566. }
  4567. }
  4568. if( !node.children && !node.lazy ) {
  4569. return _getResolvedPromise();
  4570. }
  4571. dfd = new $.Deferred();
  4572. loaders = [];
  4573. // node.debug("load()...");
  4574. node.load().done(function(){
  4575. // node.debug("load()... done.");
  4576. for(var i=0, l=node.children.length; i<l; i++){
  4577. res = node.children[i].visitAndLoad(fn, true, true);
  4578. if( res === false ) {
  4579. dfd.reject();
  4580. break;
  4581. } else if ( res !== "skip" ) {
  4582. loaders.push(res); // Add promise to the list
  4583. }
  4584. }
  4585. $.when.apply(this, loaders).then(function(){
  4586. dfd.resolve();
  4587. });
  4588. });
  4589. return dfd.promise();
  4590. },
  4591. /** Call fn(node) for all parent nodes, bottom-up, including invisible system root.<br>
  4592. * Stop iteration, if fn() returns false.<br>
  4593. * Return false if iteration was stopped.
  4594. *
  4595. * @param {function} fn the callback function.
  4596. * Return false to stop iteration, return "skip" to skip this node and children only.
  4597. * @param {boolean} [includeSelf=false]
  4598. * @returns {boolean}
  4599. */
  4600. visitParents: function(fn, includeSelf) {
  4601. // Visit parent nodes (bottom up)
  4602. if(includeSelf && fn(this) === false){
  4603. return false;
  4604. }
  4605. var p = this.parent;
  4606. while( p ) {
  4607. if(fn(p) === false){
  4608. return false;
  4609. }
  4610. p = p.parent;
  4611. }
  4612. return true;
  4613. },
  4614. /** Call fn(node) for all sibling nodes.<br>
  4615. * Stop iteration, if fn() returns false.<br>
  4616. * Return false if iteration was stopped.
  4617. *
  4618. * @param {function} fn the callback function.
  4619. * Return false to stop iteration.
  4620. * @param {boolean} [includeSelf=false]
  4621. * @returns {boolean}
  4622. */
  4623. visitSiblings: function(fn, includeSelf) {
  4624. var i, l, n,
  4625. ac = this.parent.children;
  4626. for (i=0, l=ac.length; i<l; i++) {
  4627. n = ac[i];
  4628. if ( includeSelf || n !== this ){
  4629. if( fn(n) === false ) {
  4630. return false;
  4631. }
  4632. }
  4633. }
  4634. return true;
  4635. },
  4636. /** Write warning to browser console if debugLevel >= 2 (prepending node info)
  4637. *
  4638. * @param {*} msg string or object or array of such
  4639. */
  4640. warn: function(msg){
  4641. if( this.tree.options.debugLevel >= 2 ) {
  4642. Array.prototype.unshift.call(arguments, this.toString());
  4643. consoleApply("warn", arguments);
  4644. }
  4645. }
  4646. };
  4647. /* *****************************************************************************
  4648. * Fancytree
  4649. */
  4650. /**
  4651. * Construct a new tree object.
  4652. *
  4653. * @class Fancytree
  4654. * @classdesc The controller behind a fancytree.
  4655. * This class also contains 'hook methods': see {@link Fancytree_Hooks}.
  4656. *
  4657. * @param {Widget} widget
  4658. *
  4659. * @property {string} _id Automatically generated unique tree instance ID, e.g. "1".
  4660. * @property {string} _ns Automatically generated unique tree namespace, e.g. ".fancytree-1".
  4661. * @property {FancytreeNode} activeNode Currently active node or null.
  4662. * @property {string} ariaPropName Property name of FancytreeNode that contains the element which will receive the aria attributes.
  4663. * Typically "li", but "tr" for table extension.
  4664. * @property {jQueryObject} $container Outer &lt;ul> element (or &lt;table> element for ext-table).
  4665. * @property {jQueryObject} $div A jQuery object containing the element used to instantiate the tree widget (`widget.element`)
  4666. * @property {object|array} columns Recommended place to store shared column meta data. @since 2.27
  4667. * @property {object} data Metadata, i.e. properties that may be passed to `source` in addition to a children array.
  4668. * @property {object} ext Hash of all active plugin instances.
  4669. * @property {FancytreeNode} focusNode Currently focused node or null.
  4670. * @property {FancytreeNode} lastSelectedNode Used to implement selectMode 1 (single select)
  4671. * @property {string} nodeContainerAttrName Property name of FancytreeNode that contains the outer element of single nodes.
  4672. * Typically "li", but "tr" for table extension.
  4673. * @property {FancytreeOptions} options Current options, i.e. default options + options passed to constructor.
  4674. * @property {FancytreeNode} rootNode Invisible system root node.
  4675. * @property {string} statusClassPropName Property name of FancytreeNode that contains the element which will receive the status classes.
  4676. * Typically "span", but "tr" for table extension.
  4677. * @property {object} types Map for shared type specific meta data, used with node.type attribute. @since 2.27
  4678. * @property {object} widget Base widget instance.
  4679. */
  4680. function Fancytree(widget) {
  4681. this.widget = widget;
  4682. this.$div = widget.element;
  4683. this.options = widget.options;
  4684. if( this.options ) {
  4685. if( $.isFunction(this.options.lazyload ) && !$.isFunction(this.options.lazyLoad) ) {
  4686. this.options.lazyLoad = function() {
  4687. FT.warn("The 'lazyload' event is deprecated since 2014-02-25. Use 'lazyLoad' (with uppercase L) instead.");
  4688. return widget.options.lazyload.apply(this, arguments);
  4689. };
  4690. }
  4691. if( $.isFunction(this.options.loaderror) ) {
  4692. $.error("The 'loaderror' event was renamed since 2014-07-03. Use 'loadError' (with uppercase E) instead.");
  4693. }
  4694. if( this.options.fx !== undefined ) {
  4695. FT.warn("The 'fx' option was replaced by 'toggleEffect' since 2014-11-30.");
  4696. }
  4697. if( this.options.removeNode !== undefined ) {
  4698. $.error("The 'removeNode' event was replaced by 'modifyChild' since 2.20 (2016-09-10).");
  4699. }
  4700. }
  4701. this.ext = {}; // Active extension instances
  4702. this.types = {};
  4703. this.columns = {};
  4704. // allow to init tree.data.foo from <div data-foo=''>
  4705. this.data = _getElementDataAsDict(this.$div);
  4706. // TODO: use widget.uuid instead?
  4707. this._id = $.ui.fancytree._nextId++;
  4708. // TODO: use widget.eventNamespace instead?
  4709. this._ns = ".fancytree-" + this._id; // append for namespaced events
  4710. this.activeNode = null;
  4711. this.focusNode = null;
  4712. this._hasFocus = null;
  4713. this._tempCache = {};
  4714. this._lastMousedownNode = null;
  4715. this._enableUpdate = true;
  4716. this.lastSelectedNode = null;
  4717. this.systemFocusElement = null;
  4718. this.lastQuicksearchTerm = "";
  4719. this.lastQuicksearchTime = 0;
  4720. this.statusClassPropName = "span";
  4721. this.ariaPropName = "li";
  4722. this.nodeContainerAttrName = "li";
  4723. // Remove previous markup if any
  4724. this.$div.find(">ul.fancytree-container").remove();
  4725. // Create a node without parent.
  4726. var fakeParent = { tree: this },
  4727. $ul;
  4728. this.rootNode = new FancytreeNode(fakeParent, {
  4729. title: "root",
  4730. key: "root_" + this._id,
  4731. children: null,
  4732. expanded: true
  4733. });
  4734. this.rootNode.parent = null;
  4735. // Create root markup
  4736. $ul = $("<ul>", {
  4737. "class": "ui-fancytree fancytree-container fancytree-plain"
  4738. }).appendTo(this.$div);
  4739. this.$container = $ul;
  4740. this.rootNode.ul = $ul[0];
  4741. if(this.options.debugLevel == null){
  4742. this.options.debugLevel = FT.debugLevel;
  4743. }
  4744. // // Add container to the TAB chain
  4745. // // See http://www.w3.org/TR/wai-aria-practices/#focus_activedescendant
  4746. // // #577: Allow to set tabindex to "0", "-1" and ""
  4747. // this.$container.attr("tabindex", this.options.tabindex);
  4748. // if( this.options.rtl ) {
  4749. // this.$container.attr("DIR", "RTL").addClass("fancytree-rtl");
  4750. // // }else{
  4751. // // this.$container.attr("DIR", null).removeClass("fancytree-rtl");
  4752. // }
  4753. // if(this.options.aria){
  4754. // this.$container.attr("role", "tree");
  4755. // if( this.options.selectMode !== 1 ) {
  4756. // this.$container.attr("aria-multiselectable", true);
  4757. // }
  4758. // }
  4759. }
  4760. Fancytree.prototype = /** @lends Fancytree# */{
  4761. /* Return a context object that can be re-used for _callHook().
  4762. * @param {Fancytree | FancytreeNode | EventData} obj
  4763. * @param {Event} originalEvent
  4764. * @param {Object} extra
  4765. * @returns {EventData}
  4766. */
  4767. _makeHookContext: function(obj, originalEvent, extra) {
  4768. var ctx, tree;
  4769. if(obj.node !== undefined){
  4770. // obj is already a context object
  4771. if(originalEvent && obj.originalEvent !== originalEvent){
  4772. $.error("invalid args");
  4773. }
  4774. ctx = obj;
  4775. }else if(obj.tree){
  4776. // obj is a FancytreeNode
  4777. tree = obj.tree;
  4778. ctx = { node: obj, tree: tree, widget: tree.widget, options: tree.widget.options, originalEvent: originalEvent,
  4779. typeInfo: tree.types[obj.type] || {}};
  4780. }else if(obj.widget){
  4781. // obj is a Fancytree
  4782. ctx = { node: null, tree: obj, widget: obj.widget, options: obj.widget.options, originalEvent: originalEvent };
  4783. }else{
  4784. $.error("invalid args");
  4785. }
  4786. if(extra){
  4787. $.extend(ctx, extra);
  4788. }
  4789. return ctx;
  4790. },
  4791. /* Trigger a hook function: funcName(ctx, [...]).
  4792. *
  4793. * @param {string} funcName
  4794. * @param {Fancytree|FancytreeNode|EventData} contextObject
  4795. * @param {any} [_extraArgs] optional additional arguments
  4796. * @returns {any}
  4797. */
  4798. _callHook: function(funcName, contextObject, _extraArgs) {
  4799. var ctx = this._makeHookContext(contextObject),
  4800. fn = this[funcName],
  4801. args = Array.prototype.slice.call(arguments, 2);
  4802. if(!$.isFunction(fn)){
  4803. $.error("_callHook('" + funcName + "') is not a function");
  4804. }
  4805. args.unshift(ctx);
  4806. // this.debug("_hook", funcName, ctx.node && ctx.node.toString() || ctx.tree.toString(), args);
  4807. return fn.apply(this, args);
  4808. },
  4809. _setExpiringValue: function(key, value, ms){
  4810. this._tempCache[key] = {value: value, expire: Date.now() + (+ms || 50)};
  4811. },
  4812. _getExpiringValue: function(key){
  4813. var entry = this._tempCache[key];
  4814. if( entry && entry.expire > Date.now() ) {
  4815. return entry.value;
  4816. }
  4817. delete this._tempCache[key];
  4818. return null;
  4819. },
  4820. /* Check if current extensions dependencies are met and throw an error if not.
  4821. *
  4822. * This method may be called inside the `treeInit` hook for custom extensions.
  4823. *
  4824. * @param {string} extension name of the required extension
  4825. * @param {boolean} [required=true] pass `false` if the extension is optional, but we want to check for order if it is present
  4826. * @param {boolean} [before] `true` if `name` must be included before this, `false` otherwise (use `null` if order doesn't matter)
  4827. * @param {string} [message] optional error message (defaults to a descriptve error message)
  4828. */
  4829. _requireExtension: function(name, required, before, message) {
  4830. before = !!before;
  4831. var thisName = this._local.name,
  4832. extList = this.options.extensions,
  4833. isBefore = $.inArray(name, extList) < $.inArray(thisName, extList),
  4834. isMissing = required && this.ext[name] == null,
  4835. badOrder = !isMissing && before != null && (before !== isBefore);
  4836. _assert(thisName && thisName !== name, "invalid or same name");
  4837. if( isMissing || badOrder ){
  4838. if( !message ){
  4839. if( isMissing || required ){
  4840. message = "'" + thisName + "' extension requires '" + name + "'";
  4841. if( badOrder ){
  4842. message += " to be registered " + (before ? "before" : "after") + " itself";
  4843. }
  4844. }else{
  4845. message = "If used together, `" + name + "` must be registered " + (before ? "before" : "after") + " `" + thisName + "`";
  4846. }
  4847. }
  4848. $.error(message);
  4849. return false;
  4850. }
  4851. return true;
  4852. },
  4853. /** Activate node with a given key and fire focus and activate events.
  4854. *
  4855. * A previously activated node will be deactivated.
  4856. * If activeVisible option is set, all parents will be expanded as necessary.
  4857. * Pass key = false, to deactivate the current node only.
  4858. * @param {string} key
  4859. * @returns {FancytreeNode} activated node (null, if not found)
  4860. */
  4861. activateKey: function(key) {
  4862. var node = this.getNodeByKey(key);
  4863. if(node){
  4864. node.setActive();
  4865. }else if(this.activeNode){
  4866. this.activeNode.setActive(false);
  4867. }
  4868. return node;
  4869. },
  4870. /** (experimental) Add child status nodes that indicate 'More...', ....
  4871. * @param {boolean|object} node optional node definition. Pass `false` to remove all paging nodes.
  4872. * @param {string} [mode='append'] 'child'|firstChild'
  4873. * @since 2.15
  4874. */
  4875. addPagingNode: function(node, mode){
  4876. return this.rootNode.addPagingNode(node, mode);
  4877. },
  4878. /** (experimental) Modify existing data model.
  4879. *
  4880. * @param {Array} patchList array of [key, NodePatch] arrays
  4881. * @returns {$.Promise} resolved, when all patches have been applied
  4882. * @see TreePatch
  4883. */
  4884. applyPatch: function(patchList) {
  4885. var dfd, i, p2, key, patch, node,
  4886. patchCount = patchList.length,
  4887. deferredList = [];
  4888. for(i=0; i<patchCount; i++){
  4889. p2 = patchList[i];
  4890. _assert(p2.length === 2, "patchList must be an array of length-2-arrays");
  4891. key = p2[0];
  4892. patch = p2[1];
  4893. node = (key === null) ? this.rootNode : this.getNodeByKey(key);
  4894. if(node){
  4895. dfd = new $.Deferred();
  4896. deferredList.push(dfd);
  4897. node.applyPatch(patch).always(_makeResolveFunc(dfd, node));
  4898. }else{
  4899. this.warn("could not find node with key '" + key + "'");
  4900. }
  4901. }
  4902. // Return a promise that is resolved, when ALL patches were applied
  4903. return $.when.apply($, deferredList).promise();
  4904. },
  4905. /* TODO: implement in dnd extension
  4906. cancelDrag: function() {
  4907. var dd = $.ui.ddmanager.current;
  4908. if(dd){
  4909. dd.cancel();
  4910. }
  4911. },
  4912. */
  4913. /** Remove all nodes.
  4914. * @since 2.14
  4915. */
  4916. clear: function(source) {
  4917. this._callHook("treeClear", this);
  4918. },
  4919. /** Return the number of nodes.
  4920. * @returns {integer}
  4921. */
  4922. count: function() {
  4923. return this.rootNode.countChildren();
  4924. },
  4925. /** Write to browser console if debugLevel >= 4 (prepending tree name)
  4926. *
  4927. * @param {*} msg string or object or array of such
  4928. */
  4929. debug: function(msg){
  4930. if( this.options.debugLevel >= 4 ) {
  4931. Array.prototype.unshift.call(arguments, this.toString());
  4932. consoleApply("log", arguments);
  4933. }
  4934. },
  4935. // TODO: disable()
  4936. // TODO: enable()
  4937. /** Temporarily suppress rendering to improve performance on bulk-updates.
  4938. *
  4939. * @param {boolean} flag
  4940. * @returns {boolean} previous status
  4941. * @since 2.19
  4942. */
  4943. enableUpdate: function(flag) {
  4944. flag = ( flag !== false );
  4945. /*jshint -W018 */ // Confusing use of '!'
  4946. if ( !!this._enableUpdate === !!flag ) {
  4947. return flag;
  4948. }
  4949. /*jshint +W018 */
  4950. this._enableUpdate = flag;
  4951. if ( flag ) {
  4952. this.debug("enableUpdate(true): redraw "); //, this._dirtyRoots);
  4953. this.render();
  4954. } else {
  4955. // this._dirtyRoots = null;
  4956. this.debug("enableUpdate(false)...");
  4957. }
  4958. return !flag; // return previous value
  4959. },
  4960. /**Find all nodes that matches condition.
  4961. *
  4962. * @param {string | function(node)} match title string to search for, or a
  4963. * callback function that returns `true` if a node is matched.
  4964. * @returns {FancytreeNode[]} array of nodes (may be empty)
  4965. * @see FancytreeNode#findAll
  4966. * @since 2.12
  4967. */
  4968. findAll: function(match) {
  4969. return this.rootNode.findAll(match);
  4970. },
  4971. /**Find first node that matches condition.
  4972. *
  4973. * @param {string | function(node)} match title string to search for, or a
  4974. * callback function that returns `true` if a node is matched.
  4975. * @returns {FancytreeNode} matching node or null
  4976. * @see FancytreeNode#findFirst
  4977. * @since 2.12
  4978. */
  4979. findFirst: function(match) {
  4980. return this.rootNode.findFirst(match);
  4981. },
  4982. /** Find the next visible node that starts with `match`, starting at `startNode`
  4983. * and wrap-around at the end.
  4984. *
  4985. * @param {string|function} match
  4986. * @param {FancytreeNode} [startNode] defaults to first node
  4987. * @returns {FancytreeNode} matching node or null
  4988. */
  4989. findNextNode: function(match, startNode, visibleOnly) {
  4990. match = (typeof match === "string") ? _makeNodeTitleStartMatcher(match) : match;
  4991. startNode = startNode || this.getFirstChild();
  4992. var stopNode = null,
  4993. parentChildren = startNode.parent.children,
  4994. matchingNode = null,
  4995. walkVisible = function(parent, idx, fn) {
  4996. var i, grandParent,
  4997. parentChildren = parent.children,
  4998. siblingCount = parentChildren.length,
  4999. node = parentChildren[idx];
  5000. // visit node itself
  5001. if( node && fn(node) === false ) {
  5002. return false;
  5003. }
  5004. // visit descendants
  5005. if( node && node.children && node.expanded ) {
  5006. if( walkVisible(node, 0, fn) === false ) {
  5007. return false;
  5008. }
  5009. }
  5010. // visit subsequent siblings
  5011. for( i = idx + 1; i < siblingCount; i++ ) {
  5012. if( walkVisible(parent, i, fn) === false ) {
  5013. return false;
  5014. }
  5015. }
  5016. // visit parent's subsequent siblings
  5017. grandParent = parent.parent;
  5018. if( grandParent ) {
  5019. return walkVisible(grandParent, grandParent.children.indexOf(parent) + 1, fn);
  5020. } else {
  5021. // wrap-around: restart with first node
  5022. return walkVisible(parent, 0, fn);
  5023. }
  5024. };
  5025. walkVisible(startNode.parent, parentChildren.indexOf(startNode), function(node){
  5026. // Stop iteration if we see the start node a second time
  5027. if( node === stopNode ) {
  5028. return false;
  5029. }
  5030. stopNode = stopNode || node;
  5031. // Ignore nodes hidden by a filter
  5032. if( ! $(node.span).is(":visible") ) {
  5033. node.debug("quicksearch: skipping hidden node");
  5034. return;
  5035. }
  5036. // Test if we found a match, but search for a second match if this
  5037. // was the currently active node
  5038. if( match(node) ) {
  5039. // node.debug("quicksearch match " + node.title, startNode);
  5040. matchingNode = node;
  5041. if( matchingNode !== startNode ) {
  5042. return false;
  5043. }
  5044. }
  5045. });
  5046. return matchingNode;
  5047. },
  5048. // TODO: fromDict
  5049. /**
  5050. * Generate INPUT elements that can be submitted with html forms.
  5051. *
  5052. * In selectMode 3 only the topmost selected nodes are considered, unless
  5053. * `opts.stopOnParents: false` is passed.
  5054. *
  5055. * @example
  5056. * // Generate input elements for active and selected nodes
  5057. * tree.generateFormElements();
  5058. * // Generate input elements selected nodes, using a custom `name` attribute
  5059. * tree.generateFormElements("cust_sel", false);
  5060. * // Generate input elements using a custom filter
  5061. * tree.generateFormElements(true, true, { filter: function(node) {
  5062. * return node.isSelected() && node.data.yes;
  5063. * }});
  5064. *
  5065. * @param {boolean | string} [selected=true] Pass false to disable, pass a string to override the field name (default: 'ft_ID[]')
  5066. * @param {boolean | string} [active=true] Pass false to disable, pass a string to override the field name (default: 'ft_ID_active')
  5067. * @param {object} [opts] default { filter: null, stopOnParents: true }
  5068. */
  5069. generateFormElements: function(selected, active, opts) {
  5070. opts = opts || {};
  5071. var nodeList,
  5072. selectedName = (typeof selected === "string") ? selected : "ft_" + this._id + "[]",
  5073. activeName = (typeof active === "string") ? active : "ft_" + this._id + "_active",
  5074. id = "fancytree_result_" + this._id,
  5075. $result = $("#" + id),
  5076. stopOnParents = this.options.selectMode === 3 && opts.stopOnParents !== false;
  5077. if($result.length){
  5078. $result.empty();
  5079. }else{
  5080. $result = $("<div>", {
  5081. id: id
  5082. }).hide().insertAfter(this.$container);
  5083. }
  5084. if(active !== false && this.activeNode){
  5085. $result.append($("<input>", {
  5086. type: "radio",
  5087. name: activeName,
  5088. value: this.activeNode.key,
  5089. checked: true
  5090. }));
  5091. }
  5092. function _appender( node ) {
  5093. $result.append($("<input>", {
  5094. type: "checkbox",
  5095. name: selectedName,
  5096. value: node.key,
  5097. checked: true
  5098. }));
  5099. }
  5100. if ( opts.filter ) {
  5101. this.visit(function(node) {
  5102. var res = opts.filter(node);
  5103. if( res === "skip" ) { return res; }
  5104. if ( res !== false ) {
  5105. _appender(node);
  5106. }
  5107. });
  5108. } else if ( selected !== false ) {
  5109. nodeList = this.getSelectedNodes(stopOnParents);
  5110. $.each(nodeList, function(idx, node) {
  5111. _appender(node);
  5112. });
  5113. }
  5114. },
  5115. /**
  5116. * Return the currently active node or null.
  5117. * @returns {FancytreeNode}
  5118. */
  5119. getActiveNode: function() {
  5120. return this.activeNode;
  5121. },
  5122. /** Return the first top level node if any (not the invisible root node).
  5123. * @returns {FancytreeNode | null}
  5124. */
  5125. getFirstChild: function() {
  5126. return this.rootNode.getFirstChild();
  5127. },
  5128. /**
  5129. * Return node that has keyboard focus or null.
  5130. * @returns {FancytreeNode}
  5131. */
  5132. getFocusNode: function() {
  5133. return this.focusNode;
  5134. },
  5135. /**
  5136. * Return node with a given key or null if not found.
  5137. *
  5138. * Not
  5139. * @param {string} key
  5140. * @param {FancytreeNode} [searchRoot] only search below this node
  5141. * @returns {FancytreeNode | null}
  5142. */
  5143. getNodeByKey: function(key, searchRoot) {
  5144. // Search the DOM by element ID (assuming this is faster than traversing all nodes).
  5145. var el, match;
  5146. // TODO: use tree.keyMap if available
  5147. // TODO: check opts.generateIds === true
  5148. if(!searchRoot){
  5149. el = document.getElementById(this.options.idPrefix + key);
  5150. if( el ){
  5151. return el.ftnode ? el.ftnode : null;
  5152. }
  5153. }
  5154. // Not found in the DOM, but still may be in an unrendered part of tree
  5155. searchRoot = searchRoot || this.rootNode;
  5156. match = null;
  5157. searchRoot.visit(function(node){
  5158. if(node.key === key) {
  5159. match = node;
  5160. return false; // Stop iteration
  5161. }
  5162. }, true);
  5163. return match;
  5164. },
  5165. /** Return the invisible system root node.
  5166. * @returns {FancytreeNode}
  5167. */
  5168. getRootNode: function() {
  5169. return this.rootNode;
  5170. },
  5171. /**
  5172. * Return an array of selected nodes.
  5173. * @param {boolean} [stopOnParents=false] only return the topmost selected
  5174. * node (useful with selectMode 3)
  5175. * @returns {FancytreeNode[]}
  5176. */
  5177. getSelectedNodes: function(stopOnParents) {
  5178. return this.rootNode.getSelectedNodes(stopOnParents);
  5179. },
  5180. /** Return true if the tree control has keyboard focus
  5181. * @returns {boolean}
  5182. */
  5183. hasFocus: function(){
  5184. return !!this._hasFocus;
  5185. },
  5186. /** Write to browser console if debugLevel >= 3 (prepending tree name)
  5187. * @param {*} msg string or object or array of such
  5188. */
  5189. info: function(msg){
  5190. if( this.options.debugLevel >= 3 ) {
  5191. Array.prototype.unshift.call(arguments, this.toString());
  5192. consoleApply("info", arguments);
  5193. }
  5194. },
  5195. /*
  5196. TODO: isInitializing: function() {
  5197. return ( this.phase=="init" || this.phase=="postInit" );
  5198. },
  5199. TODO: isReloading: function() {
  5200. return ( this.phase=="init" || this.phase=="postInit" ) && this.options.persist && this.persistence.cookiesFound;
  5201. },
  5202. TODO: isUserEvent: function() {
  5203. return ( this.phase=="userEvent" );
  5204. },
  5205. */
  5206. /**
  5207. * Make sure that a node with a given ID is loaded, by traversing - and
  5208. * loading - its parents. This method is meant for lazy hierarchies.
  5209. * A callback is executed for every node as we go.
  5210. * @example
  5211. * // Resolve using node.key:
  5212. * tree.loadKeyPath("/_3/_23/_26/_27", function(node, status){
  5213. * if(status === "loaded") {
  5214. * console.log("loaded intermediate node " + node);
  5215. * }else if(status === "ok") {
  5216. * node.activate();
  5217. * }
  5218. * });
  5219. * // Use deferred promise:
  5220. * tree.loadKeyPath("/_3/_23/_26/_27").progress(function(data){
  5221. * if(data.status === "loaded") {
  5222. * console.log("loaded intermediate node " + data.node);
  5223. * }else if(data.status === "ok") {
  5224. * node.activate();
  5225. * }
  5226. * }).done(function(){
  5227. * ...
  5228. * });
  5229. * // Custom path segment resolver:
  5230. * tree.loadKeyPath("/321/431/21/2", {
  5231. * matchKey: function(node, key){
  5232. * return node.data.refKey === key;
  5233. * },
  5234. * callback: function(node, status){
  5235. * if(status === "loaded") {
  5236. * console.log("loaded intermediate node " + node);
  5237. * }else if(status === "ok") {
  5238. * node.activate();
  5239. * }
  5240. * }
  5241. * });
  5242. * @param {string | string[]} keyPathList one or more key paths (e.g. '/3/2_1/7')
  5243. * @param {function | object} optsOrCallback callback(node, status) is called for every visited node ('loading', 'loaded', 'ok', 'error').
  5244. * Pass an object to define custom key matchers for the path segments: {callback: function, matchKey: function}.
  5245. * @returns {$.Promise}
  5246. */
  5247. loadKeyPath: function(keyPathList, optsOrCallback) {
  5248. var callback, i, path,
  5249. self = this,
  5250. dfd = new $.Deferred(),
  5251. parent = this.getRootNode(),
  5252. sep = this.options.keyPathSeparator,
  5253. pathSegList = [],
  5254. opts = $.extend({}, optsOrCallback);
  5255. // Prepare options
  5256. if( typeof optsOrCallback === "function" ) {
  5257. callback = optsOrCallback;
  5258. } else if ( optsOrCallback && optsOrCallback.callback ) {
  5259. callback = optsOrCallback.callback;
  5260. }
  5261. opts.callback = function(ctx, node, status){
  5262. if( callback ) {
  5263. callback.call(ctx, node, status);
  5264. }
  5265. dfd.notifyWith(ctx, [{node: node, status: status}]);
  5266. };
  5267. if( opts.matchKey == null ) {
  5268. opts.matchKey = function(node, key) { return node.key === key; };
  5269. }
  5270. // Convert array of path strings to array of segment arrays
  5271. if(!$.isArray(keyPathList)){
  5272. keyPathList = [keyPathList];
  5273. }
  5274. for(i=0; i<keyPathList.length; i++){
  5275. path = keyPathList[i];
  5276. // strip leading slash
  5277. if(path.charAt(0) === sep){
  5278. path = path.substr(1);
  5279. }
  5280. // segListMap[path] = { parent: parent, segList: path.split(sep) };
  5281. pathSegList.push(path.split(sep));
  5282. // targetList.push({ parent: parent, segList: path.split(sep)/* , path: path*/});
  5283. }
  5284. // The timeout forces async behavior always (even if nodes are all loaded)
  5285. // This way a potential progress() event will fire.
  5286. setTimeout(function(){
  5287. self._loadKeyPathImpl(dfd, opts, parent, pathSegList).done(function(){
  5288. dfd.resolve();
  5289. });
  5290. }, 0);
  5291. return dfd.promise();
  5292. },
  5293. /*
  5294. * Resolve a list of paths, relative to one parent node.
  5295. */
  5296. _loadKeyPathImpl: function(dfd, opts, parent, pathSegList) {
  5297. var deferredList, i, key, node, remainMap, tmpParent, segList, subDfd,
  5298. self = this;
  5299. function __findChild(parent, key){
  5300. // console.log("__findChild", key, parent);
  5301. var i, l,
  5302. cl = parent.children;
  5303. if( cl ) {
  5304. for(i=0, l=cl.length; i<l; i++){
  5305. if( opts.matchKey(cl[i], key)) { return cl[i]; }
  5306. }
  5307. }
  5308. return null;
  5309. }
  5310. // console.log("_loadKeyPathImpl, parent=", parent, ", pathSegList=", pathSegList);
  5311. // Pass 1:
  5312. // Handle all path segments for nodes that are already loaded.
  5313. // Collect distinct top-most lazy nodes in a map.
  5314. // Note that we can use node.key to de-dupe entries, even if a custom matcher would
  5315. // look for other node attributes.
  5316. // map[node.key] => {node: node, pathList: [list of remaining rest-paths]}
  5317. remainMap = {};
  5318. for(i=0; i<pathSegList.length; i++){
  5319. segList = pathSegList[i];
  5320. // target = targetList[i];
  5321. // Traverse and pop path segments (i.e. keys), until we hit a lazy, unloaded node
  5322. tmpParent = parent;
  5323. while(segList.length){
  5324. key = segList.shift();
  5325. node = __findChild(tmpParent, key);
  5326. if(!node){
  5327. this.warn("loadKeyPath: key not found: " + key + " (parent: " + tmpParent + ")");
  5328. opts.callback(this, key, "error");
  5329. break;
  5330. }else if(segList.length === 0){
  5331. opts.callback(this, node, "ok");
  5332. break;
  5333. }else if(!node.lazy || (node.hasChildren() !== undefined )){
  5334. opts.callback(this, node, "loaded");
  5335. tmpParent = node;
  5336. }else{
  5337. opts.callback(this, node, "loaded");
  5338. key = node.key; //target.segList.join(sep);
  5339. if(remainMap[key]){
  5340. remainMap[key].pathSegList.push(segList);
  5341. }else{
  5342. remainMap[key] = {parent: node, pathSegList: [segList]};
  5343. }
  5344. break;
  5345. }
  5346. }
  5347. }
  5348. // console.log("_loadKeyPathImpl AFTER pass 1, remainMap=", remainMap);
  5349. // Now load all lazy nodes and continue iteration for remaining paths
  5350. deferredList = [];
  5351. // Avoid jshint warning 'Don't make functions within a loop.':
  5352. function __lazyload(dfd, parent, pathSegList){
  5353. // console.log("__lazyload", parent, "pathSegList=", pathSegList);
  5354. opts.callback(self, parent, "loading");
  5355. parent.load().done(function(){
  5356. self._loadKeyPathImpl.call(self, dfd, opts, parent, pathSegList)
  5357. .always(_makeResolveFunc(dfd, self));
  5358. }).fail(function(errMsg){
  5359. self.warn("loadKeyPath: error loading lazy " + parent);
  5360. opts.callback(self, node, "error");
  5361. dfd.rejectWith(self);
  5362. });
  5363. }
  5364. // remainMap contains parent nodes, each with a list of relative sub-paths.
  5365. // We start loading all of them now, and pass the the list to each loader.
  5366. for(var nodeKey in remainMap){
  5367. var remain = remainMap[nodeKey];
  5368. // console.log("for(): remain=", remain, "remainMap=", remainMap);
  5369. // key = remain.segList.shift();
  5370. // node = __findChild(remain.parent, key);
  5371. // if (node == null) { // #576
  5372. // // Issue #576, refactored for v2.27:
  5373. // // The root cause was, that sometimes the wrong parent was used here
  5374. // // to find the next segment.
  5375. // // Falling back to getNodeByKey() was a hack that no longer works if a custom
  5376. // // matcher is used, because we cannot assume that a single segment-key is unique
  5377. // // throughout the tree.
  5378. // self.error("loadKeyPath: error loading child by key '" + key + "' (parent: " + target.parent + ")", target);
  5379. // // node = self.getNodeByKey(key);
  5380. // continue;
  5381. // }
  5382. subDfd = new $.Deferred();
  5383. deferredList.push(subDfd);
  5384. __lazyload(subDfd, remain.parent, remain.pathSegList);
  5385. }
  5386. // Return a promise that is resolved, when ALL paths were loaded
  5387. return $.when.apply($, deferredList).promise();
  5388. },
  5389. /** Re-fire beforeActivate, activate, and (optional) focus events.
  5390. * Calling this method in the `init` event, will activate the node that
  5391. * was marked 'active' in the source data, and optionally set the keyboard
  5392. * focus.
  5393. * @param [setFocus=false]
  5394. */
  5395. reactivate: function(setFocus) {
  5396. var res,
  5397. node = this.activeNode;
  5398. if( !node ) {
  5399. return _getResolvedPromise();
  5400. }
  5401. this.activeNode = null; // Force re-activating
  5402. res = node.setActive(true, {noFocus: true});
  5403. if( setFocus ){
  5404. node.setFocus();
  5405. }
  5406. return res;
  5407. },
  5408. /** Reload tree from source and return a promise.
  5409. * @param [source] optional new source (defaults to initial source data)
  5410. * @returns {$.Promise}
  5411. */
  5412. reload: function(source) {
  5413. this._callHook("treeClear", this);
  5414. return this._callHook("treeLoad", this, source);
  5415. },
  5416. /**Render tree (i.e. create DOM elements for all top-level nodes).
  5417. * @param {boolean} [force=false] create DOM elemnts, even if parent is collapsed
  5418. * @param {boolean} [deep=false]
  5419. */
  5420. render: function(force, deep) {
  5421. return this.rootNode.render(force, deep);
  5422. },
  5423. /**(De)select all nodes.
  5424. * @param {boolean} [flag=true]
  5425. * @since 2.28
  5426. */
  5427. selectAll: function(flag) {
  5428. this.visit(function(node){
  5429. node.setSelected(flag);
  5430. });
  5431. },
  5432. // TODO: selectKey: function(key, select)
  5433. // TODO: serializeArray: function(stopOnParents)
  5434. /**
  5435. * @param {boolean} [flag=true]
  5436. */
  5437. setFocus: function(flag) {
  5438. return this._callHook("treeSetFocus", this, flag);
  5439. },
  5440. /**
  5441. * Return all nodes as nested list of {@link NodeData}.
  5442. *
  5443. * @param {boolean} [includeRoot=false] Returns the hidden system root node (and its children)
  5444. * @param {function} [callback] callback(dict, node) is called for every node, in order to allow modifications
  5445. * @returns {Array | object}
  5446. * @see FancytreeNode#toDict
  5447. */
  5448. toDict: function(includeRoot, callback){
  5449. var res = this.rootNode.toDict(true, callback);
  5450. return includeRoot ? res : res.children;
  5451. },
  5452. /* Implicitly called for string conversions.
  5453. * @returns {string}
  5454. */
  5455. toString: function(){
  5456. return "Fancytree@" + this._id;
  5457. // return "<Fancytree(#" + this._id + ")>";
  5458. },
  5459. /* _trigger a widget event with additional node ctx.
  5460. * @see EventData
  5461. */
  5462. _triggerNodeEvent: function(type, node, originalEvent, extra) {
  5463. // this.debug("_trigger(" + type + "): '" + ctx.node.title + "'", ctx);
  5464. var ctx = this._makeHookContext(node, originalEvent, extra),
  5465. res = this.widget._trigger(type, originalEvent, ctx);
  5466. if(res !== false && ctx.result !== undefined){
  5467. return ctx.result;
  5468. }
  5469. return res;
  5470. },
  5471. /* _trigger a widget event with additional tree data. */
  5472. _triggerTreeEvent: function(type, originalEvent, extra) {
  5473. // this.debug("_trigger(" + type + ")", ctx);
  5474. var ctx = this._makeHookContext(this, originalEvent, extra),
  5475. res = this.widget._trigger(type, originalEvent, ctx);
  5476. if(res !== false && ctx.result !== undefined){
  5477. return ctx.result;
  5478. }
  5479. return res;
  5480. },
  5481. /** Call fn(node) for all nodes in hierarchical order (depth-first).
  5482. *
  5483. * @param {function} fn the callback function.
  5484. * Return false to stop iteration, return "skip" to skip this node and children only.
  5485. * @returns {boolean} false, if the iterator was stopped.
  5486. */
  5487. visit: function(fn) {
  5488. return this.rootNode.visit(fn, false);
  5489. },
  5490. /** Call fn(node) for all nodes in vertical order, top down (or bottom up).<br>
  5491. * Stop iteration, if fn() returns false.<br>
  5492. * Return false if iteration was stopped.
  5493. *
  5494. * @param {function} fn the callback function.
  5495. * Return false to stop iteration, return "skip" to skip this node and children only.
  5496. * @param {object} [options]
  5497. * Defaults:
  5498. * {start: First top node, reverse: false, includeSelf: true, includeHidden: false}
  5499. * @returns {boolean}
  5500. * @since 2.28
  5501. */
  5502. visitRows: function(fn, opts) {
  5503. if( opts && opts.reverse ) {
  5504. delete opts.reverse;
  5505. return this._visitRowsUp(fn, opts);
  5506. }
  5507. var i, nextIdx, parent, res, siblings,
  5508. siblingOfs = 0,
  5509. skipFirstNode = (opts.includeSelf === false),
  5510. includeHidden = !!opts.includeHidden,
  5511. node = opts.start || this.rootNode.children[0];
  5512. parent = node.parent;
  5513. while( parent ) {
  5514. // visit siblings
  5515. siblings = parent.children;
  5516. nextIdx = siblings.indexOf(node) + siblingOfs;
  5517. for( i=nextIdx; i<siblings.length; i++) {
  5518. node = siblings[i];
  5519. if( !skipFirstNode && fn(node) === false ) {
  5520. return false;
  5521. }
  5522. skipFirstNode = false;
  5523. // Dive into node's child nodes
  5524. if( node.children && node.children.length && (includeHidden || node.expanded) ) {
  5525. // Disable warning: Functions declared within loops referencing an outer
  5526. // scoped variable may lead to confusing semantics:
  5527. /*jshint -W083 */
  5528. res = node.visit(function(n) {
  5529. if( fn(n) === false ) {
  5530. return false;
  5531. }
  5532. if( !includeHidden && n.children && !n.expanded ) {
  5533. return "skip";
  5534. }
  5535. }, false);
  5536. /*jshint +W083 */
  5537. if( res === false ) {
  5538. return false;
  5539. }
  5540. }
  5541. }
  5542. // Visit parent nodes (bottom up)
  5543. node = parent;
  5544. parent = parent.parent;
  5545. siblingOfs = 1; //
  5546. }
  5547. return true;
  5548. },
  5549. /* Call fn(node) for all nodes in vertical order, bottom up.
  5550. */
  5551. _visitRowsUp: function(fn, opts) {
  5552. var children, idx, parent,
  5553. includeHidden = !!opts.includeHidden,
  5554. node = opts.start || this.rootNode.children[0];
  5555. while( true ) {
  5556. parent = node.parent;
  5557. children = parent.children;
  5558. if( children[0] === node ) {
  5559. // If this is already the first sibling, goto parent
  5560. node = parent;
  5561. children = parent.children;
  5562. } else {
  5563. // Otherwise, goto prev. sibling
  5564. idx = children.indexOf(node);
  5565. node = children[idx-1];
  5566. // If the prev. sibling has children, follow down to last descendant
  5567. while( (includeHidden || node.expanded) && node.children && node.children.length ) {
  5568. children = node.children;
  5569. parent = node;
  5570. node = children[children.length - 1];
  5571. }
  5572. }
  5573. // Skip invisible
  5574. if( !includeHidden && !$(node.span).is(":visible") ) {
  5575. continue;
  5576. }
  5577. if( fn(node) === false ) {
  5578. return false;
  5579. }
  5580. }
  5581. },
  5582. /** Write warning to browser console if debugLevel >= 2 (prepending tree info)
  5583. *
  5584. * @param {*} msg string or object or array of such
  5585. */
  5586. warn: function(msg){
  5587. if( this.options.debugLevel >= 2 ) {
  5588. Array.prototype.unshift.call(arguments, this.toString());
  5589. consoleApply("warn", arguments);
  5590. }
  5591. }
  5592. };
  5593. /**
  5594. * These additional methods of the {@link Fancytree} class are 'hook functions'
  5595. * that can be used and overloaded by extensions.
  5596. * (See <a href="https://github.com/mar10/fancytree/wiki/TutorialExtensions">writing extensions</a>.)
  5597. * @mixin Fancytree_Hooks
  5598. */
  5599. $.extend(Fancytree.prototype,
  5600. /** @lends Fancytree_Hooks# */
  5601. {
  5602. /** Default handling for mouse click events.
  5603. *
  5604. * @param {EventData} ctx
  5605. */
  5606. nodeClick: function(ctx) {
  5607. var activate, expand,
  5608. // event = ctx.originalEvent,
  5609. targetType = ctx.targetType,
  5610. node = ctx.node;
  5611. // this.debug("ftnode.onClick(" + event.type + "): ftnode:" + this + ", button:" + event.button + ", which: " + event.which, ctx);
  5612. // TODO: use switch
  5613. // TODO: make sure clicks on embedded <input> doesn't steal focus (see table sample)
  5614. if( targetType === "expander" ) {
  5615. if( node.isLoading() ) {
  5616. // #495: we probably got a click event while a lazy load is pending.
  5617. // The 'expanded' state is not yet set, so 'toggle' would expand
  5618. // and trigger lazyLoad again.
  5619. // It would be better to allow to collapse/expand the status node
  5620. // while loading (instead of ignoring), but that would require some
  5621. // more work.
  5622. node.debug("Got 2nd click while loading: ignored");
  5623. return;
  5624. }
  5625. // Clicking the expander icon always expands/collapses
  5626. this._callHook("nodeToggleExpanded", ctx);
  5627. } else if( targetType === "checkbox" ) {
  5628. // Clicking the checkbox always (de)selects
  5629. this._callHook("nodeToggleSelected", ctx);
  5630. if( ctx.options.focusOnSelect ) { // #358
  5631. this._callHook("nodeSetFocus", ctx, true);
  5632. }
  5633. } else {
  5634. // Honor `clickFolderMode` for
  5635. expand = false;
  5636. activate = true;
  5637. if( node.folder ) {
  5638. switch( ctx.options.clickFolderMode ) {
  5639. case 2: // expand only
  5640. expand = true;
  5641. activate = false;
  5642. break;
  5643. case 3: // expand and activate
  5644. activate = true;
  5645. expand = true; //!node.isExpanded();
  5646. break;
  5647. // else 1 or 4: just activate
  5648. }
  5649. }
  5650. if( activate ) {
  5651. this.nodeSetFocus(ctx);
  5652. this._callHook("nodeSetActive", ctx, true);
  5653. }
  5654. if( expand ) {
  5655. if(!activate){
  5656. // this._callHook("nodeSetFocus", ctx);
  5657. }
  5658. // this._callHook("nodeSetExpanded", ctx, true);
  5659. this._callHook("nodeToggleExpanded", ctx);
  5660. }
  5661. }
  5662. // Make sure that clicks stop, otherwise <a href='#'> jumps to the top
  5663. // if(event.target.localName === "a" && event.target.className === "fancytree-title"){
  5664. // event.preventDefault();
  5665. // }
  5666. // TODO: return promise?
  5667. },
  5668. /** Collapse all other children of same parent.
  5669. *
  5670. * @param {EventData} ctx
  5671. * @param {object} callOpts
  5672. */
  5673. nodeCollapseSiblings: function(ctx, callOpts) {
  5674. // TODO: return promise?
  5675. var ac, i, l,
  5676. node = ctx.node;
  5677. if( node.parent ){
  5678. ac = node.parent.children;
  5679. for (i=0, l=ac.length; i<l; i++) {
  5680. if ( ac[i] !== node && ac[i].expanded ){
  5681. this._callHook("nodeSetExpanded", ac[i], false, callOpts);
  5682. }
  5683. }
  5684. }
  5685. },
  5686. /** Default handling for mouse douleclick events.
  5687. * @param {EventData} ctx
  5688. */
  5689. nodeDblclick: function(ctx) {
  5690. // TODO: return promise?
  5691. if( ctx.targetType === "title" && ctx.options.clickFolderMode === 4) {
  5692. // this.nodeSetFocus(ctx);
  5693. // this._callHook("nodeSetActive", ctx, true);
  5694. this._callHook("nodeToggleExpanded", ctx);
  5695. }
  5696. // TODO: prevent text selection on dblclicks
  5697. if( ctx.targetType === "title" ) {
  5698. ctx.originalEvent.preventDefault();
  5699. }
  5700. },
  5701. /** Default handling for mouse keydown events.
  5702. *
  5703. * NOTE: this may be called with node == null if tree (but no node) has focus.
  5704. * @param {EventData} ctx
  5705. */
  5706. nodeKeydown: function(ctx) {
  5707. // TODO: return promise?
  5708. var matchNode, stamp, res, focusNode,
  5709. event = ctx.originalEvent,
  5710. node = ctx.node,
  5711. tree = ctx.tree,
  5712. opts = ctx.options,
  5713. which = event.which,
  5714. whichChar = String.fromCharCode(which),
  5715. clean = !(event.altKey || event.ctrlKey || event.metaKey || event.shiftKey),
  5716. $target = $(event.target),
  5717. handled = true,
  5718. activate = !(event.ctrlKey || !opts.autoActivate );
  5719. // (node || FT).debug("ftnode.nodeKeydown(" + event.type + "): ftnode:" + this + ", charCode:" + event.charCode + ", keyCode: " + event.keyCode + ", which: " + event.which);
  5720. // FT.debug("eventToString", which, '"' + String.fromCharCode(which) + '"', '"' + FT.eventToString(event) + '"');
  5721. // Set focus to active (or first node) if no other node has the focus yet
  5722. if( !node ){
  5723. focusNode = (this.getActiveNode() || this.getFirstChild());
  5724. if (focusNode){
  5725. focusNode.setFocus();
  5726. node = ctx.node = this.focusNode;
  5727. node.debug("Keydown force focus on active node");
  5728. }
  5729. }
  5730. if( opts.quicksearch && clean && /\w/.test(whichChar) &&
  5731. !SPECIAL_KEYCODES[which] && // #659
  5732. !$target.is(":input:enabled") ) {
  5733. // Allow to search for longer streaks if typed in quickly
  5734. stamp = Date.now();
  5735. if( stamp - tree.lastQuicksearchTime > 500 ) {
  5736. tree.lastQuicksearchTerm = "";
  5737. }
  5738. tree.lastQuicksearchTime = stamp;
  5739. tree.lastQuicksearchTerm += whichChar;
  5740. // tree.debug("quicksearch find", tree.lastQuicksearchTerm);
  5741. matchNode = tree.findNextNode(tree.lastQuicksearchTerm, tree.getActiveNode());
  5742. if( matchNode ) {
  5743. matchNode.setActive();
  5744. }
  5745. event.preventDefault();
  5746. return;
  5747. }
  5748. switch( FT.eventToString(event) ) {
  5749. case "+":
  5750. case "=": // 187: '+' @ Chrome, Safari
  5751. tree.nodeSetExpanded(ctx, true);
  5752. break;
  5753. case "-":
  5754. tree.nodeSetExpanded(ctx, false);
  5755. break;
  5756. case "space":
  5757. if( node.isPagingNode() ) {
  5758. tree._triggerNodeEvent("clickPaging", ctx, event);
  5759. } else if( FT.evalOption("checkbox", node, node, opts, false) ) { // #768
  5760. tree.nodeToggleSelected(ctx);
  5761. }else{
  5762. tree.nodeSetActive(ctx, true);
  5763. }
  5764. break;
  5765. case "return":
  5766. tree.nodeSetActive(ctx, true);
  5767. break;
  5768. case "home":
  5769. case "end":
  5770. case "backspace":
  5771. case "left":
  5772. case "right":
  5773. case "up":
  5774. case "down":
  5775. res = node.navigate(event.which, activate);
  5776. break;
  5777. default:
  5778. handled = false;
  5779. }
  5780. if(handled){
  5781. event.preventDefault();
  5782. }
  5783. },
  5784. // /** Default handling for mouse keypress events. */
  5785. // nodeKeypress: function(ctx) {
  5786. // var event = ctx.originalEvent;
  5787. // },
  5788. // /** Trigger lazyLoad event (async). */
  5789. // nodeLazyLoad: function(ctx) {
  5790. // var node = ctx.node;
  5791. // if(this._triggerNodeEvent())
  5792. // },
  5793. /** Load child nodes (async).
  5794. *
  5795. * @param {EventData} ctx
  5796. * @param {object[]|object|string|$.Promise|function} source
  5797. * @returns {$.Promise} The deferred will be resolved as soon as the (ajax)
  5798. * data was rendered.
  5799. */
  5800. nodeLoadChildren: function(ctx, source) {
  5801. var ajax, delay, dfd,
  5802. tree = ctx.tree,
  5803. node = ctx.node,
  5804. requestId = Date.now();
  5805. if($.isFunction(source)){
  5806. source = source.call(tree, {type: "source"}, ctx);
  5807. _assert(!$.isFunction(source), "source callback must not return another function");
  5808. }
  5809. if(source.url){
  5810. if( node._requestId ) {
  5811. node.warn("Recursive load request #" + requestId + " while #" + node._requestId + " is pending.");
  5812. // } else {
  5813. // node.debug("Send load request #" + requestId);
  5814. }
  5815. // `source` is an Ajax options object
  5816. ajax = $.extend({}, ctx.options.ajax, source);
  5817. node._requestId = requestId;
  5818. if(ajax.debugDelay){
  5819. // simulate a slow server
  5820. delay = ajax.debugDelay;
  5821. if($.isArray(delay)){ // random delay range [min..max]
  5822. delay = delay[0] + Math.random() * (delay[1] - delay[0]);
  5823. }
  5824. node.warn("nodeLoadChildren waiting debugDelay " + Math.round(delay) + " ms ...");
  5825. ajax.debugDelay = false;
  5826. dfd = $.Deferred(function (dfd) {
  5827. setTimeout(function () {
  5828. $.ajax(ajax)
  5829. .done(function () { dfd.resolveWith(this, arguments); })
  5830. .fail(function () { dfd.rejectWith(this, arguments); });
  5831. }, delay);
  5832. });
  5833. }else{
  5834. dfd = $.ajax(ajax);
  5835. }
  5836. // Defer the deferred: we want to be able to reject, even if ajax
  5837. // resolved ok.
  5838. source = new $.Deferred();
  5839. dfd.done(function (data, textStatus, jqXHR) {
  5840. var errorObj, res;
  5841. if((this.dataType === "json" || this.dataType === "jsonp") && typeof data === "string"){
  5842. $.error("Ajax request returned a string (did you get the JSON dataType wrong?).");
  5843. }
  5844. if( node._requestId && node._requestId > requestId ) {
  5845. // The expected request time stamp is later than `requestId`
  5846. // (which was kept as as closure variable to this handler function)
  5847. // node.warn("Ignored load response for obsolete request #" + requestId + " (expected #" + node._requestId + ")");
  5848. source.rejectWith(this, [RECURSIVE_REQUEST_ERROR]);
  5849. return;
  5850. // } else {
  5851. // node.debug("Response returned for load request #" + requestId);
  5852. }
  5853. // postProcess is similar to the standard ajax dataFilter hook,
  5854. // but it is also called for JSONP
  5855. if( ctx.options.postProcess ){
  5856. try {
  5857. res = tree._triggerNodeEvent("postProcess", ctx, ctx.originalEvent, {
  5858. response: data, error: null, dataType: this.dataType
  5859. });
  5860. } catch(e) {
  5861. res = { error: e, message: "" + e, details: "postProcess failed"};
  5862. }
  5863. if( res.error ) {
  5864. errorObj = $.isPlainObject(res.error) ? res.error : {message: res.error};
  5865. errorObj = tree._makeHookContext(node, null, errorObj);
  5866. source.rejectWith(this, [errorObj]);
  5867. return;
  5868. }
  5869. data = $.isArray(res) ? res : data;
  5870. } else if (data && data.hasOwnProperty("d") && ctx.options.enableAspx ) {
  5871. // Process ASPX WebMethod JSON object inside "d" property
  5872. data = (typeof data.d === "string") ? $.parseJSON(data.d) : data.d;
  5873. }
  5874. source.resolveWith(this, [data]);
  5875. }).fail(function (jqXHR, textStatus, errorThrown) {
  5876. var errorObj = tree._makeHookContext(node, null, {
  5877. error: jqXHR,
  5878. args: Array.prototype.slice.call(arguments),
  5879. message: errorThrown,
  5880. details: jqXHR.status + ": " + errorThrown
  5881. });
  5882. source.rejectWith(this, [errorObj]);
  5883. });
  5884. }
  5885. // #383: accept and convert ECMAScript 6 Promise
  5886. if( $.isFunction(source.then) && $.isFunction(source["catch"]) ) {
  5887. dfd = source;
  5888. source = new $.Deferred();
  5889. dfd.then(function(value){
  5890. source.resolve(value);
  5891. }, function(reason){
  5892. source.reject(reason);
  5893. });
  5894. }
  5895. if($.isFunction(source.promise)){
  5896. // `source` is a deferred, i.e. ajax request
  5897. // _assert(!node.isLoading(), "recursive load");
  5898. tree.nodeSetStatus(ctx, "loading");
  5899. source.done(function (children) {
  5900. tree.nodeSetStatus(ctx, "ok");
  5901. node._requestId = null;
  5902. }).fail(function(error){
  5903. var ctxErr;
  5904. if ( error === RECURSIVE_REQUEST_ERROR ) {
  5905. node.warn("Ignored response for obsolete load request #" + requestId + " (expected #" + node._requestId + ")");
  5906. return;
  5907. } else if (error.node && error.error && error.message) {
  5908. // error is already a context object
  5909. ctxErr = error;
  5910. } else {
  5911. ctxErr = tree._makeHookContext(node, null, {
  5912. error: error, // it can be jqXHR or any custom error
  5913. args: Array.prototype.slice.call(arguments),
  5914. message: error ? (error.message || error.toString()) : ""
  5915. });
  5916. if( ctxErr.message === "[object Object]" ) {
  5917. ctxErr.message = "";
  5918. }
  5919. }
  5920. node.warn("Load children failed (" + ctxErr.message + ")", ctxErr);
  5921. if( tree._triggerNodeEvent("loadError", ctxErr, null) !== false ) {
  5922. tree.nodeSetStatus(ctx, "error", ctxErr.message, ctxErr.details);
  5923. }
  5924. });
  5925. } else {
  5926. if( ctx.options.postProcess ){
  5927. // #792: Call postProcess for non-deferred source
  5928. var res = tree._triggerNodeEvent("postProcess", ctx, ctx.originalEvent, {
  5929. response: source, error: null, dataType: typeof source
  5930. });
  5931. source = $.isArray(res) ? res : source;
  5932. }
  5933. }
  5934. // $.when(source) resolves also for non-deferreds
  5935. return $.when(source).done(function(children){
  5936. var metaData;
  5937. if( $.isPlainObject(children) ){
  5938. // We got {foo: 'abc', children: [...]}
  5939. // Copy extra properties to tree.data.foo
  5940. _assert(node.isRootNode(), "source may only be an object for root nodes (expecting an array of child objects otherwise)");
  5941. _assert($.isArray(children.children), "if an object is passed as source, it must contain a 'children' array (all other properties are added to 'tree.data')");
  5942. metaData = children;
  5943. children = children.children;
  5944. delete metaData.children;
  5945. // Copy some attributes to tree.data
  5946. $.each(TREE_ATTRS, function(i, attr) {
  5947. if( metaData[attr] !== undefined ){
  5948. tree[attr] = metaData[attr];
  5949. delete metaData[attr];
  5950. }
  5951. });
  5952. // Copy all other attributes to tree.data.NAME
  5953. $.extend(tree.data, metaData);
  5954. }
  5955. _assert($.isArray(children), "expected array of children");
  5956. node._setChildren(children);
  5957. // trigger fancytreeloadchildren
  5958. tree._triggerNodeEvent("loadChildren", node);
  5959. });
  5960. },
  5961. /** [Not Implemented] */
  5962. nodeLoadKeyPath: function(ctx, keyPathList) {
  5963. // TODO: implement and improve
  5964. // http://code.google.com/p/dynatree/issues/detail?id=222
  5965. },
  5966. /**
  5967. * Remove a single direct child of ctx.node.
  5968. * @param {EventData} ctx
  5969. * @param {FancytreeNode} childNode dircect child of ctx.node
  5970. */
  5971. nodeRemoveChild: function(ctx, childNode) {
  5972. var idx,
  5973. node = ctx.node,
  5974. // opts = ctx.options,
  5975. subCtx = $.extend({}, ctx, {node: childNode}),
  5976. children = node.children;
  5977. // FT.debug("nodeRemoveChild()", node.toString(), childNode.toString());
  5978. if( children.length === 1 ) {
  5979. _assert(childNode === children[0], "invalid single child");
  5980. return this.nodeRemoveChildren(ctx);
  5981. }
  5982. if( this.activeNode && (childNode === this.activeNode || this.activeNode.isDescendantOf(childNode))){
  5983. this.activeNode.setActive(false); // TODO: don't fire events
  5984. }
  5985. if( this.focusNode && (childNode === this.focusNode || this.focusNode.isDescendantOf(childNode))){
  5986. this.focusNode = null;
  5987. }
  5988. // TODO: persist must take care to clear select and expand cookies
  5989. this.nodeRemoveMarkup(subCtx);
  5990. this.nodeRemoveChildren(subCtx);
  5991. idx = $.inArray(childNode, children);
  5992. _assert(idx >= 0, "invalid child");
  5993. // Notify listeners
  5994. node.triggerModifyChild("remove", childNode);
  5995. // Unlink to support GC
  5996. childNode.visit(function(n){
  5997. n.parent = null;
  5998. }, true);
  5999. this._callHook("treeRegisterNode", this, false, childNode);
  6000. // remove from child list
  6001. children.splice(idx, 1);
  6002. },
  6003. /**Remove HTML markup for all descendents of ctx.node.
  6004. * @param {EventData} ctx
  6005. */
  6006. nodeRemoveChildMarkup: function(ctx) {
  6007. var node = ctx.node;
  6008. // FT.debug("nodeRemoveChildMarkup()", node.toString());
  6009. // TODO: Unlink attr.ftnode to support GC
  6010. if(node.ul){
  6011. if( node.isRootNode() ) {
  6012. $(node.ul).empty();
  6013. } else {
  6014. $(node.ul).remove();
  6015. node.ul = null;
  6016. }
  6017. node.visit(function(n){
  6018. n.li = n.ul = null;
  6019. });
  6020. }
  6021. },
  6022. /**Remove all descendants of ctx.node.
  6023. * @param {EventData} ctx
  6024. */
  6025. nodeRemoveChildren: function(ctx) {
  6026. var subCtx,
  6027. tree = ctx.tree,
  6028. node = ctx.node,
  6029. children = node.children;
  6030. // opts = ctx.options;
  6031. // FT.debug("nodeRemoveChildren()", node.toString());
  6032. if(!children){
  6033. return;
  6034. }
  6035. if( this.activeNode && this.activeNode.isDescendantOf(node)){
  6036. this.activeNode.setActive(false); // TODO: don't fire events
  6037. }
  6038. if( this.focusNode && this.focusNode.isDescendantOf(node)){
  6039. this.focusNode = null;
  6040. }
  6041. // TODO: persist must take care to clear select and expand cookies
  6042. this.nodeRemoveChildMarkup(ctx);
  6043. // Unlink children to support GC
  6044. // TODO: also delete this.children (not possible using visit())
  6045. subCtx = $.extend({}, ctx);
  6046. node.triggerModifyChild("remove", null);
  6047. node.visit(function(n){
  6048. n.parent = null;
  6049. tree._callHook("treeRegisterNode", tree, false, n);
  6050. });
  6051. if( node.lazy ){
  6052. // 'undefined' would be interpreted as 'not yet loaded' for lazy nodes
  6053. node.children = [];
  6054. } else{
  6055. node.children = null;
  6056. }
  6057. if( !node.isRootNode() ) {
  6058. node.expanded = false; // #449, #459
  6059. }
  6060. this.nodeRenderStatus(ctx);
  6061. },
  6062. /**Remove HTML markup for ctx.node and all its descendents.
  6063. * @param {EventData} ctx
  6064. */
  6065. nodeRemoveMarkup: function(ctx) {
  6066. var node = ctx.node;
  6067. // FT.debug("nodeRemoveMarkup()", node.toString());
  6068. // TODO: Unlink attr.ftnode to support GC
  6069. if(node.li){
  6070. $(node.li).remove();
  6071. node.li = null;
  6072. }
  6073. this.nodeRemoveChildMarkup(ctx);
  6074. },
  6075. /**
  6076. * Create `&lt;li>&lt;span>..&lt;/span> .. &lt;/li>` tags for this node.
  6077. *
  6078. * This method takes care that all HTML markup is created that is required
  6079. * to display this node in its current state.
  6080. *
  6081. * Call this method to create new nodes, or after the strucuture
  6082. * was changed (e.g. after moving this node or adding/removing children)
  6083. * nodeRenderTitle() and nodeRenderStatus() are implied.
  6084. *
  6085. * &lt;code>
  6086. * &lt;li id='KEY' ftnode=NODE>
  6087. * &lt;span class='fancytree-node fancytree-expanded fancytree-has-children fancytree-lastsib fancytree-exp-el fancytree-ico-e'>
  6088. * &lt;span class="fancytree-expander">&lt;/span>
  6089. * &lt;span class="fancytree-checkbox">&lt;/span> // only present in checkbox mode
  6090. * &lt;span class="fancytree-icon">&lt;/span>
  6091. * &lt;a href="#" class="fancytree-title"> Node 1 &lt;/a>
  6092. * &lt;/span>
  6093. * &lt;ul> // only present if node has children
  6094. * &lt;li id='KEY' ftnode=NODE> child1 ... &lt;/li>
  6095. * &lt;li id='KEY' ftnode=NODE> child2 ... &lt;/li>
  6096. * &lt;/ul>
  6097. * &lt;/li>
  6098. * &lt;/code>
  6099. *
  6100. * @param {EventData} ctx
  6101. * @param {boolean} [force=false] re-render, even if html markup was already created
  6102. * @param {boolean} [deep=false] also render all descendants, even if parent is collapsed
  6103. * @param {boolean} [collapsed=false] force root node to be collapsed, so we can apply animated expand later
  6104. */
  6105. nodeRender: function(ctx, force, deep, collapsed, _recursive) {
  6106. /* This method must take care of all cases where the current data mode
  6107. * (i.e. node hierarchy) does not match the current markup.
  6108. *
  6109. * - node was not yet rendered:
  6110. * create markup
  6111. * - node was rendered: exit fast
  6112. * - children have been added
  6113. * - children have been removed
  6114. */
  6115. var childLI, childNode1, childNode2, i, l, next, subCtx,
  6116. node = ctx.node,
  6117. tree = ctx.tree,
  6118. opts = ctx.options,
  6119. aria = opts.aria,
  6120. firstTime = false,
  6121. parent = node.parent,
  6122. isRootNode = !parent,
  6123. children = node.children,
  6124. successorLi = null;
  6125. // FT.debug("nodeRender(" + !!force + ", " + !!deep + ")", node.toString());
  6126. if( tree._enableUpdate === false ) {
  6127. // tree.debug("no render", tree._enableUpdate);
  6128. return;
  6129. }
  6130. if( ! isRootNode && ! parent.ul ) {
  6131. // Calling node.collapse on a deep, unrendered node
  6132. return;
  6133. }
  6134. _assert(isRootNode || parent.ul, "parent UL must exist");
  6135. // Render the node
  6136. if( !isRootNode ){
  6137. // Discard markup on force-mode, or if it is not linked to parent <ul>
  6138. if(node.li && (force || (node.li.parentNode !== node.parent.ul) ) ){
  6139. if( node.li.parentNode === node.parent.ul ){
  6140. // #486: store following node, so we can insert the new markup there later
  6141. successorLi = node.li.nextSibling;
  6142. }else{
  6143. // May happen, when a top-level node was dropped over another
  6144. this.debug("Unlinking " + node + " (must be child of " + node.parent + ")");
  6145. }
  6146. // this.debug("nodeRemoveMarkup...");
  6147. this.nodeRemoveMarkup(ctx);
  6148. }
  6149. // Create <li><span /> </li>
  6150. // node.debug("render...");
  6151. if( !node.li ) {
  6152. // node.debug("render... really");
  6153. firstTime = true;
  6154. node.li = document.createElement("li");
  6155. node.li.ftnode = node;
  6156. if( node.key && opts.generateIds ){
  6157. node.li.id = opts.idPrefix + node.key;
  6158. }
  6159. node.span = document.createElement("span");
  6160. node.span.className = "fancytree-node";
  6161. if( aria && !node.tr ) {
  6162. $(node.li).attr("role", "treeitem");
  6163. }
  6164. node.li.appendChild(node.span);
  6165. // Create inner HTML for the <span> (expander, checkbox, icon, and title)
  6166. this.nodeRenderTitle(ctx);
  6167. // Allow tweaking and binding, after node was created for the first time
  6168. if ( opts.createNode ){
  6169. opts.createNode.call(tree, {type: "createNode"}, ctx);
  6170. }
  6171. }else{
  6172. // this.nodeRenderTitle(ctx);
  6173. this.nodeRenderStatus(ctx);
  6174. }
  6175. // Allow tweaking after node state was rendered
  6176. if ( opts.renderNode ){
  6177. opts.renderNode.call(tree, {type: "renderNode"}, ctx);
  6178. }
  6179. }
  6180. // Visit child nodes
  6181. if( children ){
  6182. if( isRootNode || node.expanded || deep === true ) {
  6183. // Create a UL to hold the children
  6184. if( !node.ul ){
  6185. node.ul = document.createElement("ul");
  6186. if((collapsed === true && !_recursive) || !node.expanded){
  6187. // hide top UL, so we can use an animation to show it later
  6188. node.ul.style.display = "none";
  6189. }
  6190. if(aria){
  6191. $(node.ul).attr("role", "group");
  6192. }
  6193. if ( node.li ) { // issue #67
  6194. node.li.appendChild(node.ul);
  6195. } else {
  6196. node.tree.$div.append(node.ul);
  6197. }
  6198. }
  6199. // Add child markup
  6200. for(i=0, l=children.length; i<l; i++) {
  6201. subCtx = $.extend({}, ctx, {node: children[i]});
  6202. this.nodeRender(subCtx, force, deep, false, true);
  6203. }
  6204. // Remove <li> if nodes have moved to another parent
  6205. childLI = node.ul.firstChild;
  6206. while( childLI ){
  6207. childNode2 = childLI.ftnode;
  6208. if( childNode2 && childNode2.parent !== node ) {
  6209. node.debug("_fixParent: remove missing " + childNode2, childLI);
  6210. next = childLI.nextSibling;
  6211. childLI.parentNode.removeChild(childLI);
  6212. childLI = next;
  6213. }else{
  6214. childLI = childLI.nextSibling;
  6215. }
  6216. }
  6217. // Make sure, that <li> order matches node.children order.
  6218. childLI = node.ul.firstChild;
  6219. for(i=0, l=children.length-1; i<l; i++) {
  6220. childNode1 = children[i];
  6221. childNode2 = childLI.ftnode;
  6222. if( childNode1 !== childNode2 ) {
  6223. // node.debug("_fixOrder: mismatch at index " + i + ": " + childNode1 + " != " + childNode2);
  6224. node.ul.insertBefore(childNode1.li, childNode2.li);
  6225. } else {
  6226. childLI = childLI.nextSibling;
  6227. }
  6228. }
  6229. }
  6230. }else{
  6231. // No children: remove markup if any
  6232. if( node.ul ){
  6233. // alert("remove child markup for " + node);
  6234. this.warn("remove child markup for " + node);
  6235. this.nodeRemoveChildMarkup(ctx);
  6236. }
  6237. }
  6238. if( !isRootNode ){
  6239. // Update element classes according to node state
  6240. // this.nodeRenderStatus(ctx);
  6241. // Finally add the whole structure to the DOM, so the browser can render
  6242. if( firstTime ){
  6243. // #486: successorLi is set, if we re-rendered (i.e. discarded)
  6244. // existing markup, which we want to insert at the same position.
  6245. // (null is equivalent to append)
  6246. // parent.ul.appendChild(node.li);
  6247. parent.ul.insertBefore(node.li, successorLi);
  6248. }
  6249. }
  6250. },
  6251. /** Create HTML inside the node's outer &lt;span> (i.e. expander, checkbox,
  6252. * icon, and title).
  6253. *
  6254. * nodeRenderStatus() is implied.
  6255. * @param {EventData} ctx
  6256. * @param {string} [title] optinal new title
  6257. */
  6258. nodeRenderTitle: function(ctx, title) {
  6259. // set node connector images, links and text
  6260. var checkbox, className, icon, nodeTitle, role, tabindex, tooltip, iconTooltip,
  6261. node = ctx.node,
  6262. tree = ctx.tree,
  6263. opts = ctx.options,
  6264. aria = opts.aria,
  6265. level = node.getLevel(),
  6266. ares = [];
  6267. if(title !== undefined){
  6268. node.title = title;
  6269. }
  6270. if ( !node.span || tree._enableUpdate === false ) {
  6271. // Silently bail out if node was not rendered yet, assuming
  6272. // node.render() will be called as the node becomes visible
  6273. return;
  6274. }
  6275. // Connector (expanded, expandable or simple)
  6276. role = (aria && node.hasChildren() !== false) ? " role='button'" : "";
  6277. if( level < opts.minExpandLevel ) {
  6278. if( !node.lazy ) {
  6279. node.expanded = true;
  6280. }
  6281. if(level > 1){
  6282. ares.push("<span " + role + " class='fancytree-expander fancytree-expander-fixed'></span>");
  6283. }
  6284. // .. else (i.e. for root level) skip expander/connector alltogether
  6285. } else {
  6286. ares.push("<span " + role + " class='fancytree-expander'></span>");
  6287. }
  6288. // Checkbox mode
  6289. checkbox = FT.evalOption("checkbox", node, node, opts, false);
  6290. if( checkbox && !node.isStatusNode() ) {
  6291. role = aria ? " role='checkbox'" : "";
  6292. className = "fancytree-checkbox";
  6293. if( checkbox === "radio" || (node.parent && node.parent.radiogroup) ) {
  6294. className += " fancytree-radio";
  6295. }
  6296. ares.push("<span " + role + " class='" + className + "'></span>");
  6297. }
  6298. // Folder or doctype icon
  6299. if( node.data.iconClass !== undefined ) { // 2015-11-16
  6300. // Handle / warn about backward compatibility
  6301. if( node.icon ) {
  6302. $.error("'iconClass' node option is deprecated since v2.14.0: use 'icon' only instead");
  6303. } else {
  6304. node.warn("'iconClass' node option is deprecated since v2.14.0: use 'icon' instead");
  6305. node.icon = node.data.iconClass;
  6306. }
  6307. }
  6308. // If opts.icon is a callback and returns something other than undefined, use that
  6309. // else if node.icon is a boolean or string, use that
  6310. // else if opts.icon is a boolean or string, use that
  6311. // else show standard icon (which may be different for folders or documents)
  6312. icon = FT.evalOption("icon", node, node, opts, true);
  6313. // if( typeof icon !== "boolean" ) {
  6314. // // icon is defined, but not true/false: must be a string
  6315. // icon = "" + icon;
  6316. // }
  6317. if( icon !== false ) {
  6318. role = aria ? " role='presentation'" : "";
  6319. iconTooltip = FT.evalOption("iconTooltip", node, node, opts, null);
  6320. iconTooltip = iconTooltip ? " title='" + _escapeTooltip(iconTooltip) + "'" : "";
  6321. if ( typeof icon === "string" ) {
  6322. if( TEST_IMG.test(icon) ) {
  6323. // node.icon is an image url. Prepend imagePath
  6324. icon = (icon.charAt(0) === "/") ? icon : ((opts.imagePath || "") + icon);
  6325. ares.push("<img src='" + icon + "' class='fancytree-icon'" + iconTooltip + " alt='' />");
  6326. } else {
  6327. ares.push("<span " + role + " class='fancytree-custom-icon " + icon + "'" + iconTooltip + "></span>");
  6328. }
  6329. } else if ( icon.text ) {
  6330. ares.push("<span " + role + " class='fancytree-custom-icon " +
  6331. (icon.addClass || "") + "'" + iconTooltip + ">" + FT.escapeHtml(icon.text) + "</span>");
  6332. } else if ( icon.html ) {
  6333. ares.push("<span " + role + " class='fancytree-custom-icon " +
  6334. (icon.addClass || "") + "'" + iconTooltip + ">" + icon.html + "</span>");
  6335. } else {
  6336. // standard icon: theme css will take care of this
  6337. ares.push("<span " + role + " class='fancytree-icon'" + iconTooltip + "></span>");
  6338. }
  6339. }
  6340. // Node title
  6341. nodeTitle = "";
  6342. if ( opts.renderTitle ){
  6343. nodeTitle = opts.renderTitle.call(tree, {type: "renderTitle"}, ctx) || "";
  6344. }
  6345. if ( !nodeTitle ) {
  6346. tooltip = FT.evalOption("tooltip", node, node, opts, null);
  6347. if( tooltip === true ) {
  6348. tooltip = node.title;
  6349. }
  6350. // if( node.tooltip ) {
  6351. // tooltip = node.tooltip;
  6352. // } else if ( opts.tooltip ) {
  6353. // tooltip = opts.tooltip === true ? node.title : opts.tooltip.call(tree, node);
  6354. // }
  6355. tooltip = tooltip ? " title='" + _escapeTooltip(tooltip) + "'" : "";
  6356. tabindex = opts.titlesTabbable ? " tabindex='0'" : "";
  6357. nodeTitle = "<span class='fancytree-title'" +
  6358. tooltip + tabindex + ">" +
  6359. (opts.escapeTitles ? FT.escapeHtml(node.title) : node.title) +
  6360. "</span>";
  6361. }
  6362. ares.push(nodeTitle);
  6363. // Note: this will trigger focusout, if node had the focus
  6364. //$(node.span).html(ares.join("")); // it will cleanup the jQuery data currently associated with SPAN (if any), but it executes more slowly
  6365. node.span.innerHTML = ares.join("");
  6366. // Update CSS classes
  6367. this.nodeRenderStatus(ctx);
  6368. if ( opts.enhanceTitle ){
  6369. ctx.$title = $(">span.fancytree-title", node.span);
  6370. nodeTitle = opts.enhanceTitle.call(tree, {type: "enhanceTitle"}, ctx) || "";
  6371. }
  6372. },
  6373. /** Update element classes according to node state.
  6374. * @param {EventData} ctx
  6375. */
  6376. nodeRenderStatus: function(ctx) {
  6377. // Set classes for current status
  6378. var $ariaElem,
  6379. node = ctx.node,
  6380. tree = ctx.tree,
  6381. opts = ctx.options,
  6382. // nodeContainer = node[tree.nodeContainerAttrName],
  6383. hasChildren = node.hasChildren(),
  6384. isLastSib = node.isLastSibling(),
  6385. aria = opts.aria,
  6386. cn = opts._classNames,
  6387. cnList = [],
  6388. statusElem = node[tree.statusClassPropName];
  6389. if( !statusElem || tree._enableUpdate === false ){
  6390. // if this function is called for an unrendered node, ignore it (will be updated on nect render anyway)
  6391. return;
  6392. }
  6393. if( aria ) {
  6394. $ariaElem = $(node.tr || node.li);
  6395. }
  6396. // Build a list of class names that we will add to the node <span>
  6397. cnList.push(cn.node);
  6398. if( tree.activeNode === node ){
  6399. cnList.push(cn.active);
  6400. // $(">span.fancytree-title", statusElem).attr("tabindex", "0");
  6401. // tree.$container.removeAttr("tabindex");
  6402. // }else{
  6403. // $(">span.fancytree-title", statusElem).removeAttr("tabindex");
  6404. // tree.$container.attr("tabindex", "0");
  6405. }
  6406. if( tree.focusNode === node ){
  6407. cnList.push(cn.focused);
  6408. }
  6409. if( node.expanded ){
  6410. cnList.push(cn.expanded);
  6411. }
  6412. if( aria ){
  6413. if (hasChildren !== false) {
  6414. $ariaElem.attr("aria-expanded", Boolean(node.expanded));
  6415. }
  6416. else {
  6417. $ariaElem.removeAttr("aria-expanded");
  6418. }
  6419. }
  6420. if( node.folder ){
  6421. cnList.push(cn.folder);
  6422. }
  6423. if( hasChildren !== false ){
  6424. cnList.push(cn.hasChildren);
  6425. }
  6426. // TODO: required?
  6427. if( isLastSib ){
  6428. cnList.push(cn.lastsib);
  6429. }
  6430. if( node.lazy && node.children == null ){
  6431. cnList.push(cn.lazy);
  6432. }
  6433. if( node.partload ){
  6434. cnList.push(cn.partload);
  6435. }
  6436. if( node.partsel ){
  6437. cnList.push(cn.partsel);
  6438. }
  6439. if( FT.evalOption("unselectable", node, node, opts, false) ){
  6440. cnList.push(cn.unselectable);
  6441. }
  6442. if( node._isLoading ){
  6443. cnList.push(cn.loading);
  6444. }
  6445. if( node._error ){
  6446. cnList.push(cn.error);
  6447. }
  6448. if( node.statusNodeType ) {
  6449. cnList.push(cn.statusNodePrefix + node.statusNodeType);
  6450. }
  6451. if( node.selected ){
  6452. cnList.push(cn.selected);
  6453. if(aria){
  6454. $ariaElem.attr("aria-selected", true);
  6455. }
  6456. }else if(aria){
  6457. $ariaElem.attr("aria-selected", false);
  6458. }
  6459. if( node.extraClasses ){
  6460. cnList.push(node.extraClasses);
  6461. }
  6462. // IE6 doesn't correctly evaluate multiple class names,
  6463. // so we create combined class names that can be used in the CSS
  6464. if( hasChildren === false ){
  6465. cnList.push(cn.combinedExpanderPrefix + "n" +
  6466. (isLastSib ? "l" : "")
  6467. );
  6468. }else{
  6469. cnList.push(cn.combinedExpanderPrefix +
  6470. (node.expanded ? "e" : "c") +
  6471. (node.lazy && node.children == null ? "d" : "") +
  6472. (isLastSib ? "l" : "")
  6473. );
  6474. }
  6475. cnList.push(cn.combinedIconPrefix +
  6476. (node.expanded ? "e" : "c") +
  6477. (node.folder ? "f" : "")
  6478. );
  6479. // node.span.className = cnList.join(" ");
  6480. statusElem.className = cnList.join(" ");
  6481. // TODO: we should not set this in the <span> tag also, if we set it here:
  6482. // Maybe most (all) of the classes should be set in LI instead of SPAN?
  6483. if(node.li){
  6484. // #719: we have to consider that there may be already other classes:
  6485. $(node.li).toggleClass(cn.lastsib, isLastSib);
  6486. }
  6487. },
  6488. /** Activate node.
  6489. * flag defaults to true.
  6490. * If flag is true, the node is activated (must be a synchronous operation)
  6491. * If flag is false, the node is deactivated (must be a synchronous operation)
  6492. * @param {EventData} ctx
  6493. * @param {boolean} [flag=true]
  6494. * @param {object} [opts] additional options. Defaults to {noEvents: false, noFocus: false}
  6495. * @returns {$.Promise}
  6496. */
  6497. nodeSetActive: function(ctx, flag, callOpts) {
  6498. // Handle user click / [space] / [enter], according to clickFolderMode.
  6499. callOpts = callOpts || {};
  6500. var subCtx,
  6501. node = ctx.node,
  6502. tree = ctx.tree,
  6503. opts = ctx.options,
  6504. noEvents = (callOpts.noEvents === true),
  6505. noFocus = (callOpts.noFocus === true),
  6506. isActive = (node === tree.activeNode);
  6507. // flag defaults to true
  6508. flag = (flag !== false);
  6509. // node.debug("nodeSetActive", flag);
  6510. if(isActive === flag){
  6511. // Nothing to do
  6512. return _getResolvedPromise(node);
  6513. }else if(flag && !noEvents && this._triggerNodeEvent("beforeActivate", node, ctx.originalEvent) === false ){
  6514. // Callback returned false
  6515. return _getRejectedPromise(node, ["rejected"]);
  6516. }
  6517. if(flag){
  6518. if(tree.activeNode){
  6519. _assert(tree.activeNode !== node, "node was active (inconsistency)");
  6520. subCtx = $.extend({}, ctx, {node: tree.activeNode});
  6521. tree.nodeSetActive(subCtx, false);
  6522. _assert(tree.activeNode === null, "deactivate was out of sync?");
  6523. }
  6524. if(opts.activeVisible){
  6525. // If no focus is set (noFocus: true) and there is no focused node, this node is made visible.
  6526. node.makeVisible({scrollIntoView: noFocus && tree.focusNode == null});
  6527. }
  6528. tree.activeNode = node;
  6529. tree.nodeRenderStatus(ctx);
  6530. if( !noFocus ) {
  6531. tree.nodeSetFocus(ctx);
  6532. }
  6533. if( !noEvents ) {
  6534. tree._triggerNodeEvent("activate", node, ctx.originalEvent);
  6535. }
  6536. }else{
  6537. _assert(tree.activeNode === node, "node was not active (inconsistency)");
  6538. tree.activeNode = null;
  6539. this.nodeRenderStatus(ctx);
  6540. if( !noEvents ) {
  6541. ctx.tree._triggerNodeEvent("deactivate", node, ctx.originalEvent);
  6542. }
  6543. }
  6544. return _getResolvedPromise(node);
  6545. },
  6546. /** Expand or collapse node, return Deferred.promise.
  6547. *
  6548. * @param {EventData} ctx
  6549. * @param {boolean} [flag=true]
  6550. * @param {object} [opts] additional options. Defaults to {noAnimation: false, noEvents: false}
  6551. * @returns {$.Promise} The deferred will be resolved as soon as the (lazy)
  6552. * data was retrieved, rendered, and the expand animation finshed.
  6553. */
  6554. nodeSetExpanded: function(ctx, flag, callOpts) {
  6555. callOpts = callOpts || {};
  6556. var _afterLoad, dfd, i, l, parents, prevAC,
  6557. node = ctx.node,
  6558. tree = ctx.tree,
  6559. opts = ctx.options,
  6560. noAnimation = (callOpts.noAnimation === true),
  6561. noEvents = (callOpts.noEvents === true);
  6562. // flag defaults to true
  6563. flag = (flag !== false);
  6564. // node.debug("nodeSetExpanded(" + flag + ")");
  6565. if((node.expanded && flag) || (!node.expanded && !flag)){
  6566. // Nothing to do
  6567. // node.debug("nodeSetExpanded(" + flag + "): nothing to do");
  6568. return _getResolvedPromise(node);
  6569. }else if(flag && !node.lazy && !node.hasChildren() ){
  6570. // Prevent expanding of empty nodes
  6571. // return _getRejectedPromise(node, ["empty"]);
  6572. return _getResolvedPromise(node);
  6573. }else if( !flag && node.getLevel() < opts.minExpandLevel ) {
  6574. // Prevent collapsing locked levels
  6575. return _getRejectedPromise(node, ["locked"]);
  6576. }else if ( !noEvents && this._triggerNodeEvent("beforeExpand", node, ctx.originalEvent) === false ){
  6577. // Callback returned false
  6578. return _getRejectedPromise(node, ["rejected"]);
  6579. }
  6580. // If this node inside a collpased node, no animation and scrolling is needed
  6581. if( !noAnimation && !node.isVisible() ) {
  6582. noAnimation = callOpts.noAnimation = true;
  6583. }
  6584. dfd = new $.Deferred();
  6585. // Auto-collapse mode: collapse all siblings
  6586. if( flag && !node.expanded && opts.autoCollapse ) {
  6587. parents = node.getParentList(false, true);
  6588. prevAC = opts.autoCollapse;
  6589. try{
  6590. opts.autoCollapse = false;
  6591. for(i=0, l=parents.length; i<l; i++){
  6592. // TODO: should return promise?
  6593. this._callHook("nodeCollapseSiblings", parents[i], callOpts);
  6594. }
  6595. }finally{
  6596. opts.autoCollapse = prevAC;
  6597. }
  6598. }
  6599. // Trigger expand/collapse after expanding
  6600. dfd.done(function(){
  6601. var lastChild = node.getLastChild();
  6602. if( flag && opts.autoScroll && !noAnimation && lastChild ) {
  6603. // Scroll down to last child, but keep current node visible
  6604. lastChild.scrollIntoView(true, {topNode: node}).always(function(){
  6605. if( !noEvents ) {
  6606. ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
  6607. }
  6608. });
  6609. } else {
  6610. if( !noEvents ) {
  6611. ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
  6612. }
  6613. }
  6614. });
  6615. // vvv Code below is executed after loading finished:
  6616. _afterLoad = function(callback){
  6617. var cn = opts._classNames,
  6618. isVisible, isExpanded,
  6619. effect = opts.toggleEffect;
  6620. node.expanded = flag;
  6621. // Create required markup, but make sure the top UL is hidden, so we
  6622. // can animate later
  6623. tree._callHook("nodeRender", ctx, false, false, true);
  6624. // Hide children, if node is collapsed
  6625. if( node.ul ) {
  6626. isVisible = (node.ul.style.display !== "none");
  6627. isExpanded = !!node.expanded;
  6628. if ( isVisible === isExpanded ) {
  6629. node.warn("nodeSetExpanded: UL.style.display already set");
  6630. } else if ( !effect || noAnimation ) {
  6631. node.ul.style.display = ( node.expanded || !parent ) ? "" : "none";
  6632. } else {
  6633. // The UI toggle() effect works with the ext-wide extension,
  6634. // while jQuery.animate() has problems when the title span
  6635. // has positon: absolute.
  6636. // Since jQuery UI 1.12, the blind effect requires the parent
  6637. // element to have 'position: relative'.
  6638. // See #716, #717
  6639. $(node.li).addClass(cn.animating); // #717
  6640. // node.info("fancytree-animating start: " + node.li.className);
  6641. $(node.ul)
  6642. .addClass(cn.animating) // # 716
  6643. .toggle(effect.effect, effect.options, effect.duration, function(){
  6644. // node.info("fancytree-animating end: " + node.li.className);
  6645. $(this).removeClass(cn.animating); // #716
  6646. $(node.li).removeClass(cn.animating); // #717
  6647. callback();
  6648. });
  6649. return;
  6650. }
  6651. }
  6652. callback();
  6653. };
  6654. // ^^^ Code above is executed after loading finshed.
  6655. // Load lazy nodes, if any. Then continue with _afterLoad()
  6656. if(flag && node.lazy && node.hasChildren() === undefined){
  6657. // node.debug("nodeSetExpanded: load start...");
  6658. node.load().done(function(){
  6659. // node.debug("nodeSetExpanded: load done");
  6660. if(dfd.notifyWith){ // requires jQuery 1.6+
  6661. dfd.notifyWith(node, ["loaded"]);
  6662. }
  6663. _afterLoad(function () { dfd.resolveWith(node); });
  6664. }).fail(function(errMsg){
  6665. _afterLoad(function () { dfd.rejectWith(node, ["load failed (" + errMsg + ")"]); });
  6666. });
  6667. /*
  6668. var source = tree._triggerNodeEvent("lazyLoad", node, ctx.originalEvent);
  6669. _assert(typeof source !== "boolean", "lazyLoad event must return source in data.result");
  6670. node.debug("nodeSetExpanded: load start...");
  6671. this._callHook("nodeLoadChildren", ctx, source).done(function(){
  6672. node.debug("nodeSetExpanded: load done");
  6673. if(dfd.notifyWith){ // requires jQuery 1.6+
  6674. dfd.notifyWith(node, ["loaded"]);
  6675. }
  6676. _afterLoad.call(tree);
  6677. }).fail(function(errMsg){
  6678. dfd.rejectWith(node, ["load failed (" + errMsg + ")"]);
  6679. });
  6680. */
  6681. }else{
  6682. _afterLoad(function () { dfd.resolveWith(node); });
  6683. }
  6684. // node.debug("nodeSetExpanded: returns");
  6685. return dfd.promise();
  6686. },
  6687. /** Focus or blur this node.
  6688. * @param {EventData} ctx
  6689. * @param {boolean} [flag=true]
  6690. */
  6691. nodeSetFocus: function(ctx, flag) {
  6692. // ctx.node.debug("nodeSetFocus(" + flag + ")");
  6693. var ctx2,
  6694. tree = ctx.tree,
  6695. node = ctx.node,
  6696. opts = tree.options,
  6697. // et = ctx.originalEvent && ctx.originalEvent.type,
  6698. isInput = ctx.originalEvent ? $(ctx.originalEvent.target).is(":input") : false;
  6699. flag = (flag !== false);
  6700. // (node || tree).debug("nodeSetFocus(" + flag + "), event: " + et + ", isInput: "+ isInput);
  6701. // Blur previous node if any
  6702. if(tree.focusNode){
  6703. if(tree.focusNode === node && flag){
  6704. // node.debug("nodeSetFocus(" + flag + "): nothing to do");
  6705. return;
  6706. }
  6707. ctx2 = $.extend({}, ctx, {node: tree.focusNode});
  6708. tree.focusNode = null;
  6709. this._triggerNodeEvent("blur", ctx2);
  6710. this._callHook("nodeRenderStatus", ctx2);
  6711. }
  6712. // Set focus to container and node
  6713. if(flag){
  6714. if( !this.hasFocus() ){
  6715. node.debug("nodeSetFocus: forcing container focus");
  6716. this._callHook("treeSetFocus", ctx, true, {calledByNode: true});
  6717. }
  6718. node.makeVisible({scrollIntoView: false});
  6719. tree.focusNode = node;
  6720. if( opts.titlesTabbable ) {
  6721. if( !isInput ) { // #621
  6722. $(node.span).find(".fancytree-title").focus();
  6723. }
  6724. } else {
  6725. // We cannot set KB focus to a node, so use the tree container
  6726. // #563, #570: IE scrolls on every call to .focus(), if the container
  6727. // is partially outside the viewport. So do it only, when absolutely
  6728. // neccessary:
  6729. if( $(document.activeElement).closest(".fancytree-container").length === 0 ) {
  6730. $(tree.$container).focus();
  6731. }
  6732. }
  6733. if( opts.aria ){
  6734. // Set active descendant to node's span ID (create one, if needed)
  6735. $(tree.$container).attr("aria-activedescendant",
  6736. $( node.tr || node.li ).uniqueId().attr("id"));
  6737. // "ftal_" + opts.idPrefix + node.key);
  6738. }
  6739. // $(node.span).find(".fancytree-title").focus();
  6740. this._triggerNodeEvent("focus", ctx);
  6741. // if( opts.autoActivate ){
  6742. // tree.nodeSetActive(ctx, true);
  6743. // }
  6744. if( opts.autoScroll ){
  6745. node.scrollIntoView();
  6746. }
  6747. this._callHook("nodeRenderStatus", ctx);
  6748. }
  6749. },
  6750. /** (De)Select node, return new status (sync).
  6751. *
  6752. * @param {EventData} ctx
  6753. * @param {boolean} [flag=true]
  6754. * @param {object} [opts] additional options. Defaults to {noEvents: false,
  6755. * propagateDown: null, propagateUp: null,
  6756. * callback: null,
  6757. * }
  6758. * @returns {boolean} previous status
  6759. */
  6760. nodeSetSelected: function(ctx, flag, callOpts) {
  6761. callOpts = callOpts || {};
  6762. var node = ctx.node,
  6763. tree = ctx.tree,
  6764. opts = ctx.options,
  6765. noEvents = (callOpts.noEvents === true),
  6766. parent = node.parent;
  6767. // flag defaults to true
  6768. flag = (flag !== false);
  6769. // node.debug("nodeSetSelected(" + flag + ")", ctx);
  6770. // Cannot (de)select unselectable nodes directly (only by propagation or
  6771. // by setting the `.selected` property)
  6772. if( FT.evalOption("unselectable", node, node, opts, false) ){
  6773. return;
  6774. }
  6775. // Remember the user's intent, in case down -> up propagation prevents
  6776. // applying it to node.selected
  6777. node._lastSelectIntent = flag;
  6778. // Nothing to do?
  6779. /*jshint -W018 */ // Confusing use of '!'
  6780. if( !!node.selected === flag ){
  6781. if( opts.selectMode === 3 && node.partsel && !flag ){
  6782. // If propagation prevented selecting this node last time, we still
  6783. // want to allow to apply setSelected(false) now
  6784. }else{
  6785. return flag;
  6786. }
  6787. }
  6788. /*jshint +W018 */
  6789. if( !noEvents &&
  6790. this._triggerNodeEvent("beforeSelect", node, ctx.originalEvent) === false ) {
  6791. return !!node.selected;
  6792. }
  6793. if(flag && opts.selectMode === 1){
  6794. // single selection mode (we don't uncheck all tree nodes, for performance reasons)
  6795. if(tree.lastSelectedNode){
  6796. tree.lastSelectedNode.setSelected(false);
  6797. }
  6798. node.selected = flag;
  6799. }else if(opts.selectMode === 3 && parent && !parent.radiogroup && !node.radiogroup){
  6800. // multi-hierarchical selection mode
  6801. node.selected = flag;
  6802. node.fixSelection3AfterClick(callOpts);
  6803. }else if(parent && parent.radiogroup){
  6804. node.visitSiblings(function(n){
  6805. n._changeSelectStatusAttrs(flag && n === node);
  6806. }, true);
  6807. }else{
  6808. // default: selectMode: 2, multi selection mode
  6809. node.selected = flag;
  6810. }
  6811. this.nodeRenderStatus(ctx);
  6812. tree.lastSelectedNode = flag ? node : null;
  6813. if( !noEvents ) {
  6814. tree._triggerNodeEvent("select", ctx);
  6815. }
  6816. },
  6817. /** Show node status (ok, loading, error, nodata) using styles and a dummy child node.
  6818. *
  6819. * @param {EventData} ctx
  6820. * @param status
  6821. * @param message
  6822. * @param details
  6823. * @since 2.3
  6824. */
  6825. nodeSetStatus: function(ctx, status, message, details) {
  6826. var node = ctx.node,
  6827. tree = ctx.tree;
  6828. function _clearStatusNode() {
  6829. // Remove dedicated dummy node, if any
  6830. var firstChild = ( node.children ? node.children[0] : null );
  6831. if ( firstChild && firstChild.isStatusNode() ) {
  6832. try{
  6833. // I've seen exceptions here with loadKeyPath...
  6834. if(node.ul){
  6835. node.ul.removeChild(firstChild.li);
  6836. firstChild.li = null; // avoid leaks (DT issue 215)
  6837. }
  6838. }catch(e){}
  6839. if( node.children.length === 1 ){
  6840. node.children = [];
  6841. }else{
  6842. node.children.shift();
  6843. }
  6844. }
  6845. }
  6846. function _setStatusNode(data, type) {
  6847. // Create/modify the dedicated dummy node for 'loading...' or
  6848. // 'error!' status. (only called for direct child of the invisible
  6849. // system root)
  6850. var firstChild = ( node.children ? node.children[0] : null );
  6851. if ( firstChild && firstChild.isStatusNode() ) {
  6852. $.extend(firstChild, data);
  6853. firstChild.statusNodeType = type;
  6854. tree._callHook("nodeRenderTitle", firstChild);
  6855. } else {
  6856. node._setChildren([data]);
  6857. node.children[0].statusNodeType = type;
  6858. tree.render();
  6859. }
  6860. return node.children[0];
  6861. }
  6862. switch( status ){
  6863. case "ok":
  6864. _clearStatusNode();
  6865. node._isLoading = false;
  6866. node._error = null;
  6867. node.renderStatus();
  6868. break;
  6869. case "loading":
  6870. if( !node.parent ) {
  6871. _setStatusNode({
  6872. title: tree.options.strings.loading + (message ? " (" + message + ")" : ""),
  6873. // icon: true, // needed for 'loding' icon
  6874. checkbox: false,
  6875. tooltip: details
  6876. }, status);
  6877. }
  6878. node._isLoading = true;
  6879. node._error = null;
  6880. node.renderStatus();
  6881. break;
  6882. case "error":
  6883. _setStatusNode({
  6884. title: tree.options.strings.loadError + (message ? " (" + message + ")" : ""),
  6885. // icon: false,
  6886. checkbox: false,
  6887. tooltip: details
  6888. }, status);
  6889. node._isLoading = false;
  6890. node._error = { message: message, details: details };
  6891. node.renderStatus();
  6892. break;
  6893. case "nodata":
  6894. _setStatusNode({
  6895. title: tree.options.strings.noData,
  6896. // icon: false,
  6897. checkbox: false,
  6898. tooltip: details
  6899. }, status);
  6900. node._isLoading = false;
  6901. node._error = null;
  6902. node.renderStatus();
  6903. break;
  6904. default:
  6905. $.error("invalid node status " + status);
  6906. }
  6907. },
  6908. /**
  6909. *
  6910. * @param {EventData} ctx
  6911. */
  6912. nodeToggleExpanded: function(ctx) {
  6913. return this.nodeSetExpanded(ctx, !ctx.node.expanded);
  6914. },
  6915. /**
  6916. * @param {EventData} ctx
  6917. */
  6918. nodeToggleSelected: function(ctx) {
  6919. var node = ctx.node,
  6920. flag = !node.selected;
  6921. // In selectMode: 3 this node may be unselected+partsel, even if
  6922. // setSelected(true) was called before, due to `unselectable` children.
  6923. // In this case, we now toggle as `setSelected(false)`
  6924. if( node.partsel && !node.selected && node._lastSelectIntent === true ) {
  6925. flag = false;
  6926. node.selected = true; // so it is not considered 'nothing to do'
  6927. }
  6928. node._lastSelectIntent = flag;
  6929. return this.nodeSetSelected(ctx, flag);
  6930. },
  6931. /** Remove all nodes.
  6932. * @param {EventData} ctx
  6933. */
  6934. treeClear: function(ctx) {
  6935. var tree = ctx.tree;
  6936. tree.activeNode = null;
  6937. tree.focusNode = null;
  6938. tree.$div.find(">ul.fancytree-container").empty();
  6939. // TODO: call destructors and remove reference loops
  6940. tree.rootNode.children = null;
  6941. },
  6942. /** Widget was created (called only once, even it re-initialized).
  6943. * @param {EventData} ctx
  6944. */
  6945. treeCreate: function(ctx) {
  6946. },
  6947. /** Widget was destroyed.
  6948. * @param {EventData} ctx
  6949. */
  6950. treeDestroy: function(ctx) {
  6951. this.$div.find(">ul.fancytree-container").remove();
  6952. this.$source && this.$source.removeClass("fancytree-helper-hidden");
  6953. },
  6954. /** Widget was (re-)initialized.
  6955. * @param {EventData} ctx
  6956. */
  6957. treeInit: function(ctx) {
  6958. var tree = ctx.tree,
  6959. opts = tree.options;
  6960. //this.debug("Fancytree.treeInit()");
  6961. // Add container to the TAB chain
  6962. // See http://www.w3.org/TR/wai-aria-practices/#focus_activedescendant
  6963. // #577: Allow to set tabindex to "0", "-1" and ""
  6964. tree.$container.attr("tabindex", opts.tabindex);
  6965. // Copy some attributes to tree.data
  6966. $.each(TREE_ATTRS, function(i, attr) {
  6967. if( opts[attr] !== undefined ){
  6968. tree.info("Move option " + attr + " to tree");
  6969. tree[attr] = opts[attr];
  6970. delete opts[attr];
  6971. }
  6972. });
  6973. if( opts.rtl ) {
  6974. tree.$container.attr("DIR", "RTL").addClass("fancytree-rtl");
  6975. }else{
  6976. tree.$container.removeAttr("DIR").removeClass("fancytree-rtl");
  6977. }
  6978. if( opts.aria ){
  6979. tree.$container.attr("role", "tree");
  6980. if( opts.selectMode !== 1 ) {
  6981. tree.$container.attr("aria-multiselectable", true);
  6982. }
  6983. }
  6984. this.treeLoad(ctx);
  6985. },
  6986. /** Parse Fancytree from source, as configured in the options.
  6987. * @param {EventData} ctx
  6988. * @param {object} [source] optional new source (use last data otherwise)
  6989. */
  6990. treeLoad: function(ctx, source) {
  6991. var metaData, type, $ul,
  6992. tree = ctx.tree,
  6993. $container = ctx.widget.element,
  6994. dfd,
  6995. // calling context for root node
  6996. rootCtx = $.extend({}, ctx, {node: this.rootNode});
  6997. if(tree.rootNode.children){
  6998. this.treeClear(ctx);
  6999. }
  7000. source = source || this.options.source;
  7001. if(!source){
  7002. type = $container.data("type") || "html";
  7003. switch(type){
  7004. case "html":
  7005. $ul = $container.find(">ul:first");
  7006. $ul.addClass("ui-fancytree-source fancytree-helper-hidden");
  7007. source = $.ui.fancytree.parseHtml($ul);
  7008. // allow to init tree.data.foo from <ul data-foo=''>
  7009. this.data = $.extend(this.data, _getElementDataAsDict($ul));
  7010. break;
  7011. case "json":
  7012. source = $.parseJSON($container.text());
  7013. // $container already contains the <ul>, but we remove the plain (json) text
  7014. // $container.empty();
  7015. $container.contents().filter(function(){
  7016. return (this.nodeType === 3);
  7017. }).remove();
  7018. if( $.isPlainObject(source) ){
  7019. // We got {foo: 'abc', children: [...]}
  7020. _assert($.isArray(source.children), "if an object is passed as source, it must contain a 'children' array (all other properties are added to 'tree.data')");
  7021. metaData = source;
  7022. source = source.children;
  7023. delete metaData.children;
  7024. // Copy some attributes to tree.data
  7025. $.each(TREE_ATTRS, function(i, attr) {
  7026. if( metaData[attr] !== undefined ){
  7027. tree[attr] = metaData[attr];
  7028. delete metaData[attr];
  7029. }
  7030. });
  7031. // Copy extra properties to tree.data.foo
  7032. $.extend(tree.data, metaData);
  7033. }
  7034. break;
  7035. default:
  7036. $.error("Invalid data-type: " + type);
  7037. }
  7038. }else if(typeof source === "string"){
  7039. // TODO: source is an element ID
  7040. $.error("Not implemented");
  7041. }
  7042. // Trigger fancytreeinit after nodes have been loaded
  7043. dfd = this.nodeLoadChildren(rootCtx, source).done(function(){
  7044. tree.render();
  7045. if( ctx.options.selectMode === 3 ){
  7046. tree.rootNode.fixSelection3FromEndNodes();
  7047. }
  7048. if( tree.activeNode && tree.options.activeVisible ) {
  7049. tree.activeNode.makeVisible();
  7050. }
  7051. tree._triggerTreeEvent("init", null, { status: true });
  7052. }).fail(function(){
  7053. tree.render();
  7054. tree._triggerTreeEvent("init", null, { status: false });
  7055. });
  7056. return dfd;
  7057. },
  7058. /** Node was inserted into or removed from the tree.
  7059. * @param {EventData} ctx
  7060. * @param {boolean} add
  7061. * @param {FancytreeNode} node
  7062. */
  7063. treeRegisterNode: function(ctx, add, node) {
  7064. },
  7065. /** Widget got focus.
  7066. * @param {EventData} ctx
  7067. * @param {boolean} [flag=true]
  7068. */
  7069. treeSetFocus: function(ctx, flag, callOpts) {
  7070. var targetNode;
  7071. flag = (flag !== false);
  7072. // this.debug("treeSetFocus(" + flag + "), callOpts: ", callOpts, this.hasFocus());
  7073. // this.debug(" focusNode: " + this.focusNode);
  7074. // this.debug(" activeNode: " + this.activeNode);
  7075. if( flag !== this.hasFocus() ){
  7076. this._hasFocus = flag;
  7077. if( !flag && this.focusNode ) {
  7078. // Node also looses focus if widget blurs
  7079. this.focusNode.setFocus(false);
  7080. } else if ( flag && (!callOpts || !callOpts.calledByNode) ) {
  7081. $(this.$container).focus();
  7082. }
  7083. this.$container.toggleClass("fancytree-treefocus", flag);
  7084. this._triggerTreeEvent(flag ? "focusTree" : "blurTree");
  7085. if( flag && !this.activeNode ) {
  7086. // #712: Use last mousedowned node ('click' event fires after focusin)
  7087. targetNode = this._lastMousedownNode || this.getFirstChild();
  7088. targetNode && targetNode.setFocus();
  7089. }
  7090. }
  7091. },
  7092. /** Widget option was set using `$().fancytree("option", "foo", "bar")`.
  7093. * @param {EventData} ctx
  7094. * @param {string} key option name
  7095. * @param {any} value option value
  7096. */
  7097. treeSetOption: function(ctx, key, value) {
  7098. var tree = ctx.tree,
  7099. callDefault = true,
  7100. callCreate = false,
  7101. callRender = false;
  7102. switch( key ) {
  7103. case "aria":
  7104. case "checkbox":
  7105. case "icon":
  7106. case "minExpandLevel":
  7107. case "tabindex":
  7108. // tree._callHook("treeCreate", tree);
  7109. callCreate = true;
  7110. callRender = true;
  7111. break;
  7112. case "escapeTitles":
  7113. case "tooltip":
  7114. callRender = true;
  7115. break;
  7116. case "rtl":
  7117. if( value === false ) {
  7118. tree.$container.removeAttr("DIR").removeClass("fancytree-rtl");
  7119. }else{
  7120. tree.$container.attr("DIR", "RTL").addClass("fancytree-rtl");
  7121. }
  7122. callRender = true;
  7123. break;
  7124. case "source":
  7125. callDefault = false;
  7126. tree._callHook("treeLoad", tree, value);
  7127. callRender = true;
  7128. break;
  7129. }
  7130. tree.debug("set option " + key + "=" + value + " <" + typeof(value) + ">");
  7131. if(callDefault){
  7132. if( this.widget._super ) {
  7133. // jQuery UI 1.9+
  7134. this.widget._super.call( this.widget, key, value );
  7135. } else {
  7136. // jQuery UI <= 1.8, we have to manually invoke the _setOption method from the base widget
  7137. $.Widget.prototype._setOption.call(this.widget, key, value);
  7138. }
  7139. }
  7140. if(callCreate){
  7141. tree._callHook("treeCreate", tree);
  7142. }
  7143. if(callRender){
  7144. tree.render(true, false); // force, not-deep
  7145. }
  7146. }
  7147. });
  7148. /* ******************************************************************************
  7149. * jQuery UI widget boilerplate
  7150. */
  7151. /**
  7152. * The plugin (derrived from <a href=" http://api.jqueryui.com/jQuery.widget/">jQuery.Widget</a>).<br>
  7153. * This constructor is not called directly. Use `$(selector).fancytree({})`
  7154. * to initialize the plugin instead.<br>
  7155. * <pre class="sh_javascript sunlight-highlight-javascript">// Access widget methods and members:
  7156. * var tree = $("#tree").fancytree("getTree");
  7157. * var node = $("#tree").fancytree("getActiveNode", "1234");
  7158. * </pre>
  7159. *
  7160. * @mixin Fancytree_Widget
  7161. */
  7162. $.widget("ui.fancytree",
  7163. /** @lends Fancytree_Widget# */
  7164. {
  7165. /**These options will be used as defaults
  7166. * @type {FancytreeOptions}
  7167. */
  7168. options:
  7169. {
  7170. activeVisible: true,
  7171. ajax: {
  7172. type: "GET",
  7173. cache: false, // false: Append random '_' argument to the request url to prevent caching.
  7174. // timeout: 0, // >0: Make sure we get an ajax error if server is unreachable
  7175. dataType: "json" // Expect json format and pass json object to callbacks.
  7176. }, //
  7177. aria: true,
  7178. autoActivate: true,
  7179. autoCollapse: false,
  7180. autoScroll: false,
  7181. checkbox: false,
  7182. clickFolderMode: 4,
  7183. debugLevel: null, // 0..4 (null: use global setting $.ui.fancytree.debugInfo)
  7184. disabled: false, // TODO: required anymore?
  7185. enableAspx: true,
  7186. escapeTitles: false,
  7187. extensions: [],
  7188. // fx: { height: "toggle", duration: 200 },
  7189. // toggleEffect: { effect: "drop", options: {direction: "left"}, duration: 200 },
  7190. // toggleEffect: { effect: "slide", options: {direction: "up"}, duration: 200 },
  7191. toggleEffect: { effect: "blind", options: {direction: "vertical", scale: "box"}, duration: 200 },
  7192. generateIds: false,
  7193. icon: true,
  7194. idPrefix: "ft_",
  7195. focusOnSelect: false,
  7196. keyboard: true,
  7197. keyPathSeparator: "/",
  7198. minExpandLevel: 1,
  7199. quicksearch: false,
  7200. rtl: false,
  7201. scrollOfs: {top: 0, bottom: 0},
  7202. scrollParent: null,
  7203. selectMode: 2,
  7204. strings: {
  7205. loading: "Loading...", // &#8230; would be escaped when escapeTitles is true
  7206. loadError: "Load error!",
  7207. moreData: "More...",
  7208. noData: "No data."
  7209. },
  7210. tabindex: "0",
  7211. titlesTabbable: false,
  7212. tooltip: false,
  7213. _classNames: {
  7214. node: "fancytree-node",
  7215. folder: "fancytree-folder",
  7216. animating: "fancytree-animating",
  7217. combinedExpanderPrefix: "fancytree-exp-",
  7218. combinedIconPrefix: "fancytree-ico-",
  7219. hasChildren: "fancytree-has-children",
  7220. active: "fancytree-active",
  7221. selected: "fancytree-selected",
  7222. expanded: "fancytree-expanded",
  7223. lazy: "fancytree-lazy",
  7224. focused: "fancytree-focused",
  7225. partload: "fancytree-partload",
  7226. partsel: "fancytree-partsel",
  7227. radio: "fancytree-radio",
  7228. // radiogroup: "fancytree-radiogroup",
  7229. unselectable: "fancytree-unselectable",
  7230. lastsib: "fancytree-lastsib",
  7231. loading: "fancytree-loading",
  7232. error: "fancytree-error",
  7233. statusNodePrefix: "fancytree-statusnode-"
  7234. },
  7235. // events
  7236. lazyLoad: null,
  7237. postProcess: null
  7238. },
  7239. /* Set up the widget, Called on first $().fancytree() */
  7240. _create: function() {
  7241. this.tree = new Fancytree(this);
  7242. this.$source = this.source || this.element.data("type") === "json" ? this.element
  7243. : this.element.find(">ul:first");
  7244. // Subclass Fancytree instance with all enabled extensions
  7245. var extension, extName, i,
  7246. opts = this.options,
  7247. extensions = opts.extensions,
  7248. base = this.tree;
  7249. for(i=0; i<extensions.length; i++){
  7250. extName = extensions[i];
  7251. extension = $.ui.fancytree._extensions[extName];
  7252. if(!extension){
  7253. $.error("Could not apply extension '" + extName + "' (it is not registered, did you forget to include it?)");
  7254. }
  7255. // Add extension options as tree.options.EXTENSION
  7256. // _assert(!this.tree.options[extName], "Extension name must not exist as option name: " + extName);
  7257. this.tree.options[extName] = $.extend(true, {}, extension.options, this.tree.options[extName]);
  7258. // Add a namespace tree.ext.EXTENSION, to hold instance data
  7259. _assert(this.tree.ext[extName] === undefined, "Extension name must not exist as Fancytree.ext attribute: '" + extName + "'");
  7260. // this.tree[extName] = extension;
  7261. this.tree.ext[extName] = {};
  7262. // Subclass Fancytree methods using proxies.
  7263. _subclassObject(this.tree, base, extension, extName);
  7264. // current extension becomes base for the next extension
  7265. base = extension;
  7266. }
  7267. //
  7268. if( opts.icons !== undefined ) { // 2015-11-16
  7269. if( opts.icon !== true ) {
  7270. $.error("'icons' tree option is deprecated since v2.14.0: use 'icon' only instead");
  7271. } else {
  7272. this.tree.warn("'icons' tree option is deprecated since v2.14.0: use 'icon' instead");
  7273. opts.icon = opts.icons;
  7274. }
  7275. }
  7276. if( opts.iconClass !== undefined ) { // 2015-11-16
  7277. if( opts.icon ) {
  7278. $.error("'iconClass' tree option is deprecated since v2.14.0: use 'icon' only instead");
  7279. } else {
  7280. this.tree.warn("'iconClass' tree option is deprecated since v2.14.0: use 'icon' instead");
  7281. opts.icon = opts.iconClass;
  7282. }
  7283. }
  7284. if( opts.tabbable !== undefined ) { // 2016-04-04
  7285. opts.tabindex = opts.tabbable ? "0" : "-1";
  7286. this.tree.warn("'tabbable' tree option is deprecated since v2.17.0: use 'tabindex='" + opts.tabindex + "' instead");
  7287. }
  7288. //
  7289. this.tree._callHook("treeCreate", this.tree);
  7290. // Note: 'fancytreecreate' event is fired by widget base class
  7291. // this.tree._triggerTreeEvent("create");
  7292. },
  7293. /* Called on every $().fancytree() */
  7294. _init: function() {
  7295. this.tree._callHook("treeInit", this.tree);
  7296. // TODO: currently we call bind after treeInit, because treeInit
  7297. // might change tree.$container.
  7298. // It would be better, to move event binding into hooks altogether
  7299. this._bind();
  7300. },
  7301. /* Use the _setOption method to respond to changes to options */
  7302. _setOption: function(key, value) {
  7303. return this.tree._callHook("treeSetOption", this.tree, key, value);
  7304. },
  7305. /** Use the destroy method to clean up any modifications your widget has made to the DOM */
  7306. destroy: function() {
  7307. this._unbind();
  7308. this.tree._callHook("treeDestroy", this.tree);
  7309. // In jQuery UI 1.8, you must invoke the destroy method from the base widget
  7310. $.Widget.prototype.destroy.call(this);
  7311. // TODO: delete tree and nodes to make garbage collect easier?
  7312. // TODO: In jQuery UI 1.9 and above, you would define _destroy instead of destroy and not call the base method
  7313. },
  7314. // -------------------------------------------------------------------------
  7315. /* Remove all event handlers for our namespace */
  7316. _unbind: function() {
  7317. var ns = this.tree._ns;
  7318. this.element.off(ns);
  7319. this.tree.$container.off(ns);
  7320. $(document).off(ns);
  7321. },
  7322. /* Add mouse and kyboard handlers to the container */
  7323. _bind: function() {
  7324. var that = this,
  7325. opts = this.options,
  7326. tree = this.tree,
  7327. ns = tree._ns
  7328. // selstartEvent = ( $.support.selectstart ? "selectstart" : "mousedown" )
  7329. ;
  7330. // Remove all previuous handlers for this tree
  7331. this._unbind();
  7332. //alert("keydown" + ns + "foc=" + tree.hasFocus() + tree.$container);
  7333. // tree.debug("bind events; container: ", tree.$container);
  7334. tree.$container.on("focusin" + ns + " focusout" + ns, function(event){
  7335. var node = FT.getNode(event),
  7336. flag = (event.type === "focusin");
  7337. if( !flag && node && $(event.target).is("a") ) {
  7338. // #764
  7339. node.debug("Ignored focusout on embedded <a> element.");
  7340. return;
  7341. }
  7342. // tree.treeOnFocusInOut.call(tree, event);
  7343. // tree.debug("Tree container got event " + event.type, node, event, FT.getEventTarget(event));
  7344. if( flag ) {
  7345. if( tree._getExpiringValue("focusin") ) {
  7346. // #789: IE 11 may send duplicate focusin events
  7347. FT.info("Ignored double focusin.");
  7348. return;
  7349. }
  7350. tree._setExpiringValue("focusin", true, 50);
  7351. if( !node ) {
  7352. // #789: IE 11 may send focusin before mousdown(?)
  7353. node = tree._getExpiringValue("mouseDownNode");
  7354. if( node ) { FT.info("Reconstruct mouse target for focusin from recent event."); }
  7355. }
  7356. }
  7357. if(node){
  7358. // For example clicking into an <input> that is part of a node
  7359. tree._callHook("nodeSetFocus", tree._makeHookContext(node, event), flag);
  7360. }else{
  7361. if( tree.tbody && $(event.target).parents("table.fancytree-container > thead").length ) {
  7362. // #767: ignore events in the table's header
  7363. tree.debug("Ignore focus event outside table body.", event);
  7364. } else {
  7365. tree._callHook("treeSetFocus", tree, flag);
  7366. }
  7367. }
  7368. }).on("selectstart" + ns, "span.fancytree-title", function(event){
  7369. // prevent mouse-drags to select text ranges
  7370. // tree.debug("<span title> got event " + event.type);
  7371. event.preventDefault();
  7372. }).on("keydown" + ns, function(event){
  7373. // TODO: also bind keyup and keypress
  7374. // tree.debug("got event " + event.type + ", hasFocus:" + tree.hasFocus());
  7375. // if(opts.disabled || opts.keyboard === false || !tree.hasFocus() ){
  7376. if(opts.disabled || opts.keyboard === false ){
  7377. return true;
  7378. }
  7379. var res,
  7380. node = tree.focusNode, // node may be null
  7381. ctx = tree._makeHookContext(node || tree, event),
  7382. prevPhase = tree.phase;
  7383. try {
  7384. tree.phase = "userEvent";
  7385. // If a 'fancytreekeydown' handler returns false, skip the default
  7386. // handling (implemented by tree.nodeKeydown()).
  7387. if(node){
  7388. res = tree._triggerNodeEvent("keydown", node, event);
  7389. }else{
  7390. res = tree._triggerTreeEvent("keydown", event);
  7391. }
  7392. if ( res === "preventNav" ){
  7393. res = true; // prevent keyboard navigation, but don't prevent default handling of embedded input controls
  7394. } else if ( res !== false ){
  7395. res = tree._callHook("nodeKeydown", ctx);
  7396. }
  7397. return res;
  7398. } finally {
  7399. tree.phase = prevPhase;
  7400. }
  7401. }).on("mousedown" + ns, function(event){
  7402. var et = FT.getEventTarget(event);
  7403. // that.tree.debug("event(" + event.type + "): node: ", et.node);
  7404. // #712: Store the clicked node, so we can use it when we get a focusin event
  7405. // ('click' event fires after focusin)
  7406. // tree.debug("event(" + event.type + "): node: ", et.node);
  7407. tree._lastMousedownNode = et ? et.node : null;
  7408. // #789: Store the node also for a short period, so we can use it
  7409. // in a *resulting* focusin event
  7410. tree._setExpiringValue("mouseDownNode", tree._lastMousedownNode);
  7411. }).on("click" + ns + " dblclick" + ns, function(event){
  7412. if(opts.disabled){
  7413. return true;
  7414. }
  7415. var ctx,
  7416. et = FT.getEventTarget(event),
  7417. node = et.node,
  7418. tree = that.tree,
  7419. prevPhase = tree.phase;
  7420. // that.tree.debug("event(" + event.type + "): node: ", node);
  7421. if( !node ){
  7422. return true; // Allow bubbling of other events
  7423. }
  7424. ctx = tree._makeHookContext(node, event);
  7425. // that.tree.debug("event(" + event.type + "): node: ", node);
  7426. try {
  7427. tree.phase = "userEvent";
  7428. switch(event.type) {
  7429. case "click":
  7430. ctx.targetType = et.type;
  7431. if( node.isPagingNode() ) {
  7432. return tree._triggerNodeEvent("clickPaging", ctx, event) === true;
  7433. }
  7434. return ( tree._triggerNodeEvent("click", ctx, event) === false ) ? false : tree._callHook("nodeClick", ctx);
  7435. case "dblclick":
  7436. ctx.targetType = et.type;
  7437. return ( tree._triggerNodeEvent("dblclick", ctx, event) === false ) ? false : tree._callHook("nodeDblclick", ctx);
  7438. }
  7439. } finally {
  7440. tree.phase = prevPhase;
  7441. }
  7442. });
  7443. },
  7444. /** Return the active node or null.
  7445. * @returns {FancytreeNode}
  7446. */
  7447. getActiveNode: function() {
  7448. return this.tree.activeNode;
  7449. },
  7450. /** Return the matching node or null.
  7451. * @param {string} key
  7452. * @returns {FancytreeNode}
  7453. */
  7454. getNodeByKey: function(key) {
  7455. return this.tree.getNodeByKey(key);
  7456. },
  7457. /** Return the invisible system root node.
  7458. * @returns {FancytreeNode}
  7459. */
  7460. getRootNode: function() {
  7461. return this.tree.rootNode;
  7462. },
  7463. /** Return the current tree instance.
  7464. * @returns {Fancytree}
  7465. */
  7466. getTree: function() {
  7467. return this.tree;
  7468. }
  7469. });
  7470. // $.ui.fancytree was created by the widget factory. Create a local shortcut:
  7471. FT = $.ui.fancytree;
  7472. /**
  7473. * Static members in the `$.ui.fancytree` namespace.<br>
  7474. * <br>
  7475. * <pre class="sh_javascript sunlight-highlight-javascript">// Access static members:
  7476. * var node = $.ui.fancytree.getNode(element);
  7477. * alert($.ui.fancytree.version);
  7478. * </pre>
  7479. *
  7480. * @mixin Fancytree_Static
  7481. */
  7482. $.extend($.ui.fancytree,
  7483. /** @lends Fancytree_Static# */
  7484. {
  7485. /** @type {string} */
  7486. version: "2.28.1", // Set to semver by 'grunt release'
  7487. /** @type {string} */
  7488. buildType: "production", // Set to 'production' by 'grunt build'
  7489. /** @type {int} */
  7490. debugLevel: 3, // Set to 3 by 'grunt build'
  7491. // Used by $.ui.fancytree.debug() and as default for tree.options.debugLevel
  7492. _nextId: 1,
  7493. _nextNodeKey: 1,
  7494. _extensions: {},
  7495. // focusTree: null,
  7496. /** Expose class object as $.ui.fancytree._FancytreeClass */
  7497. _FancytreeClass: Fancytree,
  7498. /** Expose class object as $.ui.fancytree._FancytreeNodeClass */
  7499. _FancytreeNodeClass: FancytreeNode,
  7500. /* Feature checks to provide backwards compatibility */
  7501. jquerySupports: {
  7502. // http://jqueryui.com/upgrade-guide/1.9/#deprecated-offset-option-merged-into-my-and-at
  7503. positionMyOfs: isVersionAtLeast($.ui.version, 1, 9)
  7504. },
  7505. /** Throw an error if condition fails (debug method).
  7506. * @param {boolean} cond
  7507. * @param {string} msg
  7508. */
  7509. assert: function(cond, msg){
  7510. return _assert(cond, msg);
  7511. },
  7512. /** Create a new Fancytree instance on a target element.
  7513. *
  7514. * @param {Element | jQueryObject | string} el Target DOM element or selector
  7515. * @param {FancytreeOptions} [opts] Fancytree options
  7516. * @returns {Fancytree} new tree instance
  7517. * @example
  7518. * var tree = $.ui.fancytree.createTree("#tree", {
  7519. * source: {url: "my/webservice"}
  7520. * }); // Create tree for this matching element
  7521. *
  7522. * @since 2.25
  7523. */
  7524. createTree: function(el, opts){
  7525. var tree = $(el).fancytree(opts).fancytree("getTree");
  7526. return tree;
  7527. },
  7528. /** Return a function that executes *fn* at most every *timeout* ms.
  7529. * @param {integer} timeout
  7530. * @param {function} fn
  7531. * @param {boolean} [invokeAsap=false]
  7532. * @param {any} [ctx]
  7533. */
  7534. debounce: function(timeout, fn, invokeAsap, ctx) {
  7535. var timer;
  7536. if(arguments.length === 3 && typeof invokeAsap !== "boolean") {
  7537. ctx = invokeAsap;
  7538. invokeAsap = false;
  7539. }
  7540. return function() {
  7541. var args = arguments;
  7542. ctx = ctx || this;
  7543. invokeAsap && !timer && fn.apply(ctx, args);
  7544. clearTimeout(timer);
  7545. timer = setTimeout(function() {
  7546. invokeAsap || fn.apply(ctx, args);
  7547. timer = null;
  7548. }, timeout);
  7549. };
  7550. },
  7551. /** Write message to console if debugLevel >= 4
  7552. * @param {string} msg
  7553. */
  7554. debug: function(msg){
  7555. /*jshint expr:true */
  7556. ($.ui.fancytree.debugLevel >= 4) && consoleApply("log", arguments);
  7557. },
  7558. /** Write error message to console if debugLevel >= 1.
  7559. * @param {string} msg
  7560. */
  7561. error: function(msg){
  7562. ($.ui.fancytree.debugLevel >= 1) && consoleApply("error", arguments);
  7563. },
  7564. /** Convert &lt;, &gt;, &amp;, &quot;, &#39;, &#x2F; to the equivalent entities.
  7565. *
  7566. * @param {string} s
  7567. * @returns {string}
  7568. */
  7569. escapeHtml: function(s){
  7570. return ("" + s).replace(REX_HTML, function(s) {
  7571. return ENTITY_MAP[s];
  7572. });
  7573. },
  7574. /** Make jQuery.position() arguments backwards compatible, i.e. if
  7575. * jQuery UI version <= 1.8, convert
  7576. * { my: "left+3 center", at: "left bottom", of: $target }
  7577. * to
  7578. * { my: "left center", at: "left bottom", of: $target, offset: "3 0" }
  7579. *
  7580. * See http://jqueryui.com/upgrade-guide/1.9/#deprecated-offset-option-merged-into-my-and-at
  7581. * and http://jsfiddle.net/mar10/6xtu9a4e/
  7582. *
  7583. * @param {object} opts
  7584. * @returns {object} the (potentially modified) original opts hash object
  7585. */
  7586. fixPositionOptions: function(opts) {
  7587. if( opts.offset || ("" + opts.my + opts.at ).indexOf("%") >= 0 ) {
  7588. $.error("expected new position syntax (but '%' is not supported)");
  7589. }
  7590. if( ! $.ui.fancytree.jquerySupports.positionMyOfs ) {
  7591. var // parse 'left+3 center' into ['left+3 center', 'left', '+3', 'center', undefined]
  7592. myParts = /(\w+)([+-]?\d+)?\s+(\w+)([+-]?\d+)?/.exec(opts.my),
  7593. atParts = /(\w+)([+-]?\d+)?\s+(\w+)([+-]?\d+)?/.exec(opts.at),
  7594. // convert to numbers
  7595. dx = (myParts[2] ? (+myParts[2]) : 0) + (atParts[2] ? (+atParts[2]) : 0),
  7596. dy = (myParts[4] ? (+myParts[4]) : 0) + (atParts[4] ? (+atParts[4]) : 0);
  7597. opts = $.extend({}, opts, { // make a copy and overwrite
  7598. my: myParts[1] + " " + myParts[3],
  7599. at: atParts[1] + " " + atParts[3]
  7600. });
  7601. if( dx || dy ) {
  7602. opts.offset = "" + dx + " " + dy;
  7603. }
  7604. }
  7605. return opts;
  7606. },
  7607. /** Return a {node: FancytreeNode, type: TYPE} object for a mouse event.
  7608. *
  7609. * @param {Event} event Mouse event, e.g. click, ...
  7610. * @returns {object} Return a {node: FancytreeNode, type: TYPE} object
  7611. * TYPE: 'title' | 'prefix' | 'expander' | 'checkbox' | 'icon' | undefined
  7612. */
  7613. getEventTarget: function(event){
  7614. var $target,
  7615. tcn = event && event.target ? event.target.className : "",
  7616. res = {node: this.getNode(event.target), type: undefined};
  7617. // We use a fast version of $(res.node).hasClass()
  7618. // See http://jsperf.com/test-for-classname/2
  7619. if( /\bfancytree-title\b/.test(tcn) ){
  7620. res.type = "title";
  7621. }else if( /\bfancytree-expander\b/.test(tcn) ){
  7622. res.type = (res.node.hasChildren() === false ? "prefix" : "expander");
  7623. // }else if( /\bfancytree-checkbox\b/.test(tcn) || /\bfancytree-radio\b/.test(tcn) ){
  7624. }else if( /\bfancytree-checkbox\b/.test(tcn) ){
  7625. res.type = "checkbox";
  7626. }else if( /\bfancytree(-custom)?-icon\b/.test(tcn) ){
  7627. res.type = "icon";
  7628. }else if( /\bfancytree-node\b/.test(tcn) ){
  7629. // Somewhere near the title
  7630. res.type = "title";
  7631. }else if( event && event.target ) {
  7632. $target = $(event.target);
  7633. if( $target.is("ul[role=group]") ) {
  7634. // #nnn: Clicking right to a node may hit the surrounding UL
  7635. FT.info("Ignoring click on outer UL.");
  7636. res.node = null;
  7637. }else if( $target.closest(".fancytree-title").length ) {
  7638. // #228: clicking an embedded element inside a title
  7639. res.type = "title";
  7640. }else if( $target.closest(".fancytree-checkbox").length ) {
  7641. // E.g. <svg> inside checkbox span
  7642. res.type = "checkbox";
  7643. }else if( $target.closest(".fancytree-expander").length ) {
  7644. res.type = "expander";
  7645. }
  7646. }
  7647. return res;
  7648. },
  7649. /** Return a string describing the affected node region for a mouse event.
  7650. *
  7651. * @param {Event} event Mouse event, e.g. click, mousemove, ...
  7652. * @returns {string} 'title' | 'prefix' | 'expander' | 'checkbox' | 'icon' | undefined
  7653. */
  7654. getEventTargetType: function(event){
  7655. return this.getEventTarget(event).type;
  7656. },
  7657. /** Return a FancytreeNode instance from element, event, or jQuery object.
  7658. *
  7659. * @param {Element | jQueryObject | Event} el
  7660. * @returns {FancytreeNode} matching node or null
  7661. */
  7662. getNode: function(el){
  7663. if(el instanceof FancytreeNode){
  7664. return el; // el already was a FancytreeNode
  7665. }else if( el instanceof $ ){
  7666. el = el[0]; // el was a jQuery object: use the DOM element
  7667. }else if(el.originalEvent !== undefined){
  7668. el = el.target; // el was an Event
  7669. }
  7670. while( el ) {
  7671. if(el.ftnode) {
  7672. return el.ftnode;
  7673. }
  7674. el = el.parentNode;
  7675. }
  7676. return null;
  7677. },
  7678. /** Return a Fancytree instance, from element, index, event, or jQueryObject.
  7679. *
  7680. * @param {Element | jQueryObject | Event | integer | string} [el]
  7681. * @returns {Fancytree} matching tree or null
  7682. * @example
  7683. * $.ui.fancytree.getTree(); // Get first Fancytree instance on page
  7684. * $.ui.fancytree.getTree(1); // Get second Fancytree instance on page
  7685. * $.ui.fancytree.getTree("#tree"); // Get tree for this matching element
  7686. *
  7687. * @since 2.13
  7688. */
  7689. getTree: function(el){
  7690. var widget;
  7691. if( el instanceof Fancytree ) {
  7692. return el; // el already was a Fancytree
  7693. }
  7694. if( el === undefined ) {
  7695. el = 0; // get first tree
  7696. }
  7697. if( typeof el === "number" ) {
  7698. el = $(".fancytree-container").eq(el); // el was an integer: return nth instance
  7699. } else if( typeof el === "string" ) {
  7700. el = $(el).eq(0); // el was a selector: use first match
  7701. } else if( el.selector !== undefined ) {
  7702. el = el.eq(0); // el was a jQuery object: use the first DOM element
  7703. } else if( el.originalEvent !== undefined ) {
  7704. el = $(el.target); // el was an Event
  7705. }
  7706. el = el.closest(":ui-fancytree");
  7707. widget = el.data("ui-fancytree") || el.data("fancytree"); // the latter is required by jQuery <= 1.8
  7708. return widget ? widget.tree : null;
  7709. },
  7710. /** Return an option value that has a default, but may be overridden by a
  7711. * callback or a node instance attribute.
  7712. *
  7713. * Evaluation sequence:<br>
  7714. *
  7715. * If tree.options.<optionName> is a callback that returns something, use that.<br>
  7716. * Else if node.<optionName> is defined, use that.<br>
  7717. * Else if tree.options.<optionName> is a value, use that.<br>
  7718. * Else use `defaultValue`.
  7719. *
  7720. * @param {string} optionName name of the option property (on node and tree)
  7721. * @param {FancytreeNode} node passed to the callback
  7722. * @param {object} nodeObject where to look for the local option property, e.g. `node` or `node.data`
  7723. * @param {object} treeOption where to look for the tree option, e.g. `tree.options` or `tree.options.dnd5`
  7724. * @param {any} [defaultValue]
  7725. * @returns {any}
  7726. *
  7727. * @example
  7728. * // Check for node.foo, tree,options.foo(), and tree.options.foo:
  7729. * $.ui.fancytree.evalOption("foo", node, node, tree.options);
  7730. * // Check for node.data.bar, tree,options.qux.bar(), and tree.options.qux.bar:
  7731. * $.ui.fancytree.evalOption("bar", node, node.data, tree.options.qux);
  7732. *
  7733. * @since 2.22
  7734. */
  7735. evalOption: function(optionName, node, nodeObject, treeOptions, defaultValue) {
  7736. var ctx, res,
  7737. tree = node.tree,
  7738. treeOpt = treeOptions[optionName],
  7739. nodeOpt = nodeObject[optionName];
  7740. if( $.isFunction(treeOpt) ) {
  7741. ctx = {
  7742. node: node, tree: tree, widget: tree.widget, options: tree.widget.options,
  7743. typeInfo: tree.types[node.type] || {}
  7744. };
  7745. res = treeOpt.call(tree, {type: optionName}, ctx);
  7746. if( res == null ) {
  7747. res = nodeOpt;
  7748. }
  7749. } else {
  7750. res = (nodeOpt != null) ? nodeOpt : treeOpt;
  7751. }
  7752. if( res == null ) {
  7753. res = defaultValue; // no option set at all: return default
  7754. }
  7755. return res;
  7756. },
  7757. /** Set expander, checkbox, or node icon, supporting string and object format.
  7758. *
  7759. * @param {Element | jQueryObject} span
  7760. * @param {string} baseClass
  7761. * @param {string | object} icon
  7762. * @since 2.27
  7763. */
  7764. setSpanIcon: function( span, baseClass, icon ) {
  7765. var $span = $( span );
  7766. if( typeof icon === "string" ) {
  7767. $span.attr( "class", baseClass + " " + icon );
  7768. } else { // support object syntax: { text: ligature, addClasse: classname }
  7769. if( icon.text ) {
  7770. $span.text( "" + icon.text );
  7771. } else if ( icon.html ) {
  7772. span.innerHTML = icon.html;
  7773. }
  7774. $span.attr( "class", baseClass + " " + ( icon.addClass || "" ) );
  7775. }
  7776. },
  7777. /** Convert a keydown or mouse event to a canonical string like 'ctrl+a',
  7778. * 'ctrl+shift+f2', 'shift+leftdblclick'.
  7779. *
  7780. * This is especially handy for switch-statements in event handlers.
  7781. *
  7782. * @param {event}
  7783. * @returns {string}
  7784. *
  7785. * @example
  7786. switch( $.ui.fancytree.eventToString(event) ) {
  7787. case "-":
  7788. tree.nodeSetExpanded(ctx, false);
  7789. break;
  7790. case "shift+return":
  7791. tree.nodeSetActive(ctx, true);
  7792. break;
  7793. case "down":
  7794. res = node.navigate(event.which, activate);
  7795. break;
  7796. default:
  7797. handled = false;
  7798. }
  7799. if( handled ){
  7800. event.preventDefault();
  7801. }
  7802. */
  7803. eventToString: function(event) {
  7804. // Poor-man's hotkeys. See here for a complete implementation:
  7805. // https://github.com/jeresig/jquery.hotkeys
  7806. var which = event.which,
  7807. et = event.type,
  7808. s = [];
  7809. if( event.altKey ) { s.push("alt"); }
  7810. if( event.ctrlKey ) { s.push("ctrl"); }
  7811. if( event.metaKey ) { s.push("meta"); }
  7812. if( event.shiftKey ) { s.push("shift"); }
  7813. if( et === "click" || et === "dblclick" ) {
  7814. s.push(MOUSE_BUTTONS[event.button] + et);
  7815. } else {
  7816. if( !IGNORE_KEYCODES[which] ) {
  7817. s.push( SPECIAL_KEYCODES[which] || String.fromCharCode(which).toLowerCase() );
  7818. }
  7819. }
  7820. return s.join("+");
  7821. },
  7822. /** Write message to console if debugLevel >= 3
  7823. * @param {string} msg
  7824. */
  7825. info: function(msg){
  7826. /*jshint expr:true */
  7827. ($.ui.fancytree.debugLevel >= 3) && consoleApply("info", arguments);
  7828. },
  7829. /* @deprecated: use eventToString(event) instead.
  7830. */
  7831. keyEventToString: function(event) {
  7832. this.warn("keyEventToString() is deprecated: use eventToString()");
  7833. return this.eventToString(event);
  7834. },
  7835. /** Return a wrapped handler method, that provides `this.super`.
  7836. *
  7837. * @example
  7838. // Implement `opts.createNode` event to add the 'draggable' attribute
  7839. $.ui.fancytree.overrideMethod(ctx.options, "createNode", function(event, data) {
  7840. // Default processing if any
  7841. this._super.apply(this, arguments);
  7842. // Add 'draggable' attribute
  7843. data.node.span.draggable = true;
  7844. });
  7845. *
  7846. * @param {object} instance
  7847. * @param {string} methodName
  7848. * @param {function} handler
  7849. */
  7850. overrideMethod: function(instance, methodName, handler){
  7851. var prevSuper,
  7852. _super = instance[methodName] || $.noop;
  7853. // context = context || this;
  7854. instance[methodName] = function() {
  7855. try {
  7856. prevSuper = this._super;
  7857. this._super = _super;
  7858. return handler.apply(this, arguments);
  7859. } finally {
  7860. this._super = prevSuper;
  7861. }
  7862. };
  7863. },
  7864. /**
  7865. * Parse tree data from HTML <ul> markup
  7866. *
  7867. * @param {jQueryObject} $ul
  7868. * @returns {NodeData[]}
  7869. */
  7870. parseHtml: function($ul) {
  7871. // TODO: understand this:
  7872. /*jshint validthis:true */
  7873. var classes, className, extraClasses, i, iPos, l, tmp, tmp2,
  7874. $children = $ul.find(">li"),
  7875. children = [];
  7876. $children.each(function() {
  7877. var allData, lowerCaseAttr,
  7878. $li = $(this),
  7879. $liSpan = $li.find(">span:first", this),
  7880. $liA = $liSpan.length ? null : $li.find(">a:first"),
  7881. d = { tooltip: null, data: {} };
  7882. if( $liSpan.length ) {
  7883. d.title = $liSpan.html();
  7884. } else if( $liA && $liA.length ) {
  7885. // If a <li><a> tag is specified, use it literally and extract href/target.
  7886. d.title = $liA.html();
  7887. d.data.href = $liA.attr("href");
  7888. d.data.target = $liA.attr("target");
  7889. d.tooltip = $liA.attr("title");
  7890. } else {
  7891. // If only a <li> tag is specified, use the trimmed string up to
  7892. // the next child <ul> tag.
  7893. d.title = $li.html();
  7894. iPos = d.title.search(/<ul/i);
  7895. if( iPos >= 0 ){
  7896. d.title = d.title.substring(0, iPos);
  7897. }
  7898. }
  7899. d.title = $.trim(d.title);
  7900. // Make sure all fields exist
  7901. for(i=0, l=CLASS_ATTRS.length; i<l; i++){
  7902. d[CLASS_ATTRS[i]] = undefined;
  7903. }
  7904. // Initialize to `true`, if class is set and collect extraClasses
  7905. classes = this.className.split(" ");
  7906. extraClasses = [];
  7907. for(i=0, l=classes.length; i<l; i++){
  7908. className = classes[i];
  7909. if(CLASS_ATTR_MAP[className]){
  7910. d[className] = true;
  7911. }else{
  7912. extraClasses.push(className);
  7913. }
  7914. }
  7915. d.extraClasses = extraClasses.join(" ");
  7916. // Parse node options from ID, title and class attributes
  7917. tmp = $li.attr("title");
  7918. if( tmp ){
  7919. d.tooltip = tmp; // overrides <a title='...'>
  7920. }
  7921. tmp = $li.attr("id");
  7922. if( tmp ){
  7923. d.key = tmp;
  7924. }
  7925. // Translate hideCheckbox -> checkbox:false
  7926. if( $li.attr("hideCheckbox") ){
  7927. d.checkbox = false;
  7928. }
  7929. // Add <li data-NAME='...'> as node.data.NAME
  7930. allData = _getElementDataAsDict($li);
  7931. if( allData && !$.isEmptyObject(allData) ) {
  7932. // #507: convert data-hidecheckbox (lower case) to hideCheckbox
  7933. for( lowerCaseAttr in NODE_ATTR_LOWERCASE_MAP ) {
  7934. if( allData.hasOwnProperty(lowerCaseAttr) ) {
  7935. allData[NODE_ATTR_LOWERCASE_MAP[lowerCaseAttr]] = allData[lowerCaseAttr];
  7936. delete allData[lowerCaseAttr];
  7937. }
  7938. }
  7939. // #56: Allow to set special node.attributes from data-...
  7940. for(i=0, l=NODE_ATTRS.length; i<l; i++){
  7941. tmp = NODE_ATTRS[i];
  7942. tmp2 = allData[tmp];
  7943. if( tmp2 != null ) {
  7944. delete allData[tmp];
  7945. d[tmp] = tmp2;
  7946. }
  7947. }
  7948. // All other data-... goes to node.data...
  7949. $.extend(d.data, allData);
  7950. }
  7951. // Recursive reading of child nodes, if LI tag contains an UL tag
  7952. $ul = $li.find(">ul:first");
  7953. if( $ul.length ) {
  7954. d.children = $.ui.fancytree.parseHtml($ul);
  7955. }else{
  7956. d.children = d.lazy ? undefined : null;
  7957. }
  7958. children.push(d);
  7959. // FT.debug("parse ", d, children);
  7960. });
  7961. return children;
  7962. },
  7963. /** Add Fancytree extension definition to the list of globally available extensions.
  7964. *
  7965. * @param {object} definition
  7966. */
  7967. registerExtension: function(definition){
  7968. _assert(definition.name != null, "extensions must have a `name` property.");
  7969. _assert(definition.version != null, "extensions must have a `version` property.");
  7970. $.ui.fancytree._extensions[definition.name] = definition;
  7971. },
  7972. /** Inverse of escapeHtml().
  7973. *
  7974. * @param {string} s
  7975. * @returns {string}
  7976. */
  7977. unescapeHtml: function(s){
  7978. var e = document.createElement("div");
  7979. e.innerHTML = s;
  7980. return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
  7981. },
  7982. /** Write warning message to console if debugLevel >= 2.
  7983. * @param {string} msg
  7984. */
  7985. warn: function(msg){
  7986. ($.ui.fancytree.debugLevel >= 2) && consoleApply("warn", arguments);
  7987. }
  7988. });
  7989. // Value returned by `require('jquery.fancytree')`
  7990. return $.ui.fancytree;
  7991. })); // End of closure
  7992. /*! Extension 'jquery.fancytree.childcounter.js' */// Extending Fancytree
  7993. // ===================
  7994. //
  7995. // See also the [live demo](http://wwwendt.de/tech/fancytree/demo/sample-ext-childcounter.html) of this code.
  7996. //
  7997. // Every extension should have a comment header containing some information
  7998. // about the author, copyright and licensing. Also a pointer to the latest
  7999. // source code.
  8000. // Prefix with `/*!` so the comment is not removed by the minifier.
  8001. /*!
  8002. * jquery.fancytree.childcounter.js
  8003. *
  8004. * Add a child counter bubble to tree nodes.
  8005. * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
  8006. *
  8007. * Copyright (c) 2008-2018, Martin Wendt (http://wwWendt.de)
  8008. *
  8009. * Released under the MIT license
  8010. * https://github.com/mar10/fancytree/wiki/LicenseInfo
  8011. *
  8012. * @version 2.28.1
  8013. * @date 2018-03-19T06:47:37Z
  8014. */
  8015. // To keep the global namespace clean, we wrap everything in a closure.
  8016. // The UMD wrapper pattern defines the dependencies on jQuery and the
  8017. // Fancytree core module, and makes sure that we can use the `require()`
  8018. // syntax with package loaders.
  8019. ;(function( factory ) {
  8020. if ( typeof define === "function" && define.amd ) {
  8021. // AMD. Register as an anonymous module.
  8022. define( [ "jquery", "./jquery.fancytree" ], factory );
  8023. } else if ( typeof module === "object" && module.exports ) {
  8024. // Node/CommonJS
  8025. require("./jquery.fancytree");
  8026. module.exports = factory(require("jquery"));
  8027. } else {
  8028. // Browser globals
  8029. factory( jQuery );
  8030. }
  8031. }( function( $ ) {
  8032. // Consider to use [strict mode](http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/)
  8033. "use strict";
  8034. // The [coding guidelines](http://contribute.jquery.org/style-guide/js/)
  8035. // require jshint compliance.
  8036. // But for this sample, we want to allow unused variables for demonstration purpose.
  8037. /*jshint unused:false */
  8038. // Adding methods
  8039. // --------------
  8040. // New member functions can be added to the `Fancytree` class.
  8041. // This function will be available for every tree instance:
  8042. //
  8043. // var tree = $("#tree").fancytree("getTree");
  8044. // tree.countSelected(false);
  8045. $.ui.fancytree._FancytreeClass.prototype.countSelected = function(topOnly){
  8046. var tree = this,
  8047. treeOptions = tree.options;
  8048. return tree.getSelectedNodes(topOnly).length;
  8049. };
  8050. // The `FancytreeNode` class can also be easily extended. This would be called
  8051. // like
  8052. // node.updateCounters();
  8053. //
  8054. // It is also good practice to add a docstring comment.
  8055. /**
  8056. * [ext-childcounter] Update counter badges for `node` and its parents.
  8057. * May be called in the `loadChildren` event, to update parents of lazy loaded
  8058. * nodes.
  8059. * @alias FancytreeNode#updateCounters
  8060. * @requires jquery.fancytree.childcounters.js
  8061. */
  8062. $.ui.fancytree._FancytreeNodeClass.prototype.updateCounters = function(){
  8063. var node = this,
  8064. $badge = $("span.fancytree-childcounter", node.span),
  8065. extOpts = node.tree.options.childcounter,
  8066. count = node.countChildren(extOpts.deep);
  8067. node.data.childCounter = count;
  8068. if( (count || !extOpts.hideZeros) && (!node.isExpanded() || !extOpts.hideExpanded) ) {
  8069. if( !$badge.length ) {
  8070. $badge = $("<span class='fancytree-childcounter'/>").appendTo($("span.fancytree-icon", node.span));
  8071. }
  8072. $badge.text(count);
  8073. } else {
  8074. $badge.remove();
  8075. }
  8076. if( extOpts.deep && !node.isTopLevel() && !node.isRoot() ) {
  8077. node.parent.updateCounters();
  8078. }
  8079. };
  8080. // Finally, we can extend the widget API and create functions that are called
  8081. // like so:
  8082. //
  8083. // $("#tree").fancytree("widgetMethod1", "abc");
  8084. $.ui.fancytree.prototype.widgetMethod1 = function(arg1){
  8085. var tree = this.tree;
  8086. return arg1;
  8087. };
  8088. // Register a Fancytree extension
  8089. // ------------------------------
  8090. // A full blown extension, extension is available for all trees and can be
  8091. // enabled like so (see also the [live demo](http://wwwendt.de/tech/fancytree/demo/sample-ext-childcounter.html)):
  8092. //
  8093. // <script src="../src/jquery.fancytree.js"></script>
  8094. // <script src="../src/jquery.fancytree.childcounter.js"></script>
  8095. // ...
  8096. //
  8097. // $("#tree").fancytree({
  8098. // extensions: ["childcounter"],
  8099. // childcounter: {
  8100. // hideExpanded: true
  8101. // },
  8102. // ...
  8103. // });
  8104. //
  8105. /* 'childcounter' extension */
  8106. $.ui.fancytree.registerExtension({
  8107. // Every extension must be registered by a unique name.
  8108. name: "childcounter",
  8109. // Version information should be compliant with [semver](http://semver.org)
  8110. version: "2.28.1",
  8111. // Extension specific options and their defaults.
  8112. // This options will be available as `tree.options.childcounter.hideExpanded`
  8113. options: {
  8114. deep: true,
  8115. hideZeros: true,
  8116. hideExpanded: false
  8117. },
  8118. // Attributes other than `options` (or functions) can be defined here, and
  8119. // will be added to the tree.ext.EXTNAME namespace, in this case `tree.ext.childcounter.foo`.
  8120. // They can also be accessed as `this._local.foo` from within the extension
  8121. // methods.
  8122. foo: 42,
  8123. // Local functions are prefixed with an underscore '_'.
  8124. // Callable as `this._local._appendCounter()`.
  8125. _appendCounter: function(bar){
  8126. var tree = this;
  8127. },
  8128. // **Override virtual methods for this extension.**
  8129. //
  8130. // Fancytree implements a number of 'hook methods', prefixed by 'node...' or 'tree...'.
  8131. // with a `ctx` argument (see [EventData](http://www.wwwendt.de/tech/fancytree/doc/jsdoc/global.html#EventData)
  8132. // for details) and an extended calling context:<br>
  8133. // `this` : the Fancytree instance<br>
  8134. // `this._local`: the namespace that contains extension attributes and private methods (same as this.ext.EXTNAME)<br>
  8135. // `this._super`: the virtual function that was overridden (member of previous extension or Fancytree)
  8136. //
  8137. // See also the [complete list of available hook functions](http://www.wwwendt.de/tech/fancytree/doc/jsdoc/Fancytree_Hooks.html).
  8138. /* Init */
  8139. // `treeInit` is triggered when a tree is initalized. We can set up classes or
  8140. // bind event handlers here...
  8141. treeInit: function(ctx){
  8142. var tree = this, // same as ctx.tree,
  8143. opts = ctx.options,
  8144. extOpts = ctx.options.childcounter;
  8145. // Optionally check for dependencies with other extensions
  8146. /* this._requireExtension("glyph", false, false); */
  8147. // Call the base implementation
  8148. this._superApply(arguments);
  8149. // Add a class to the tree container
  8150. this.$container.addClass("fancytree-ext-childcounter");
  8151. },
  8152. // Destroy this tree instance (we only call the default implementation, so
  8153. // this method could as well be omitted).
  8154. treeDestroy: function(ctx){
  8155. this._superApply(arguments);
  8156. },
  8157. // Overload the `renderTitle` hook, to append a counter badge
  8158. nodeRenderTitle: function(ctx, title) {
  8159. var node = ctx.node,
  8160. extOpts = ctx.options.childcounter,
  8161. count = (node.data.childCounter == null) ? node.countChildren(extOpts.deep) : +node.data.childCounter;
  8162. // Let the base implementation render the title
  8163. // We use `_super()` instead of `_superApply()` here, since it is a little bit
  8164. // more performant when called often
  8165. this._super(ctx, title);
  8166. // Append a counter badge
  8167. if( (count || ! extOpts.hideZeros) && (!node.isExpanded() || !extOpts.hideExpanded) ){
  8168. $("span.fancytree-icon", node.span).append($("<span class='fancytree-childcounter'/>").text(count));
  8169. }
  8170. },
  8171. // Overload the `setExpanded` hook, so the counters are updated
  8172. nodeSetExpanded: function(ctx, flag, callOpts) {
  8173. var tree = ctx.tree,
  8174. node = ctx.node;
  8175. // Let the base implementation expand/collapse the node, then redraw the title
  8176. // after the animation has finished
  8177. return this._superApply(arguments).always(function(){
  8178. tree.nodeRenderTitle(ctx);
  8179. });
  8180. }
  8181. // End of extension definition
  8182. });
  8183. // Value returned by `require('jquery.fancytree..')`
  8184. return $.ui.fancytree;
  8185. })); // End of closure
  8186. /*! Extension 'jquery.fancytree.clones.js' *//*!
  8187. *
  8188. * jquery.fancytree.clones.js
  8189. * Support faster lookup of nodes by key and shared ref-ids.
  8190. * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
  8191. *
  8192. * Copyright (c) 2008-2018, Martin Wendt (http://wwWendt.de)
  8193. *
  8194. * Released under the MIT license
  8195. * https://github.com/mar10/fancytree/wiki/LicenseInfo
  8196. *
  8197. * @version 2.28.1
  8198. * @date 2018-03-19T06:47:37Z
  8199. */
  8200. ;(function( factory ) {
  8201. if ( typeof define === "function" && define.amd ) {
  8202. // AMD. Register as an anonymous module.
  8203. define( [ "jquery", "./jquery.fancytree" ], factory );
  8204. } else if ( typeof module === "object" && module.exports ) {
  8205. // Node/CommonJS
  8206. require("./jquery.fancytree");
  8207. module.exports = factory(require("jquery"));
  8208. } else {
  8209. // Browser globals
  8210. factory( jQuery );
  8211. }
  8212. }( function( $ ) {
  8213. "use strict";
  8214. /*******************************************************************************
  8215. * Private functions and variables
  8216. */
  8217. function _assert(cond, msg){
  8218. // TODO: see qunit.js extractStacktrace()
  8219. if(!cond){
  8220. msg = msg ? ": " + msg : "";
  8221. $.error("Assertion failed" + msg);
  8222. }
  8223. }
  8224. /* Return first occurrence of member from array. */
  8225. function _removeArrayMember(arr, elem) {
  8226. // TODO: use Array.indexOf for IE >= 9
  8227. var i;
  8228. for (i = arr.length - 1; i >= 0; i--) {
  8229. if (arr[i] === elem) {
  8230. arr.splice(i, 1);
  8231. return true;
  8232. }
  8233. }
  8234. return false;
  8235. }
  8236. // /**
  8237. // * Calculate a 32 bit FNV-1a hash
  8238. // * Found here: https://gist.github.com/vaiorabbit/5657561
  8239. // * Ref.: http://isthe.com/chongo/tech/comp/fnv/
  8240. // *
  8241. // * @param {string} str the input value
  8242. // * @param {boolean} [asString=false] set to true to return the hash value as
  8243. // * 8-digit hex string instead of an integer
  8244. // * @param {integer} [seed] optionally pass the hash of the previous chunk
  8245. // * @returns {integer | string}
  8246. // */
  8247. // function hashFnv32a(str, asString, seed) {
  8248. // /*jshint bitwise:false */
  8249. // var i, l,
  8250. // hval = (seed === undefined) ? 0x811c9dc5 : seed;
  8251. // for (i = 0, l = str.length; i < l; i++) {
  8252. // hval ^= str.charCodeAt(i);
  8253. // hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
  8254. // }
  8255. // if( asString ){
  8256. // // Convert to 8 digit hex string
  8257. // return ("0000000" + (hval >>> 0).toString(16)).substr(-8);
  8258. // }
  8259. // return hval >>> 0;
  8260. // }
  8261. /**
  8262. * JS Implementation of MurmurHash3 (r136) (as of May 20, 2011)
  8263. *
  8264. * @author <a href="mailto:[email protected]">Gary Court</a>
  8265. * @see http://github.com/garycourt/murmurhash-js
  8266. * @author <a href="mailto:[email protected]">Austin Appleby</a>
  8267. * @see http://sites.google.com/site/murmurhash/
  8268. *
  8269. * @param {string} key ASCII only
  8270. * @param {boolean} [asString=false]
  8271. * @param {number} seed Positive integer only
  8272. * @return {number} 32-bit positive integer hash
  8273. */
  8274. function hashMurmur3(key, asString, seed) {
  8275. /*jshint bitwise:false */
  8276. var h1b, k1,
  8277. remainder = key.length & 3,
  8278. bytes = key.length - remainder,
  8279. h1 = seed,
  8280. c1 = 0xcc9e2d51,
  8281. c2 = 0x1b873593,
  8282. i = 0;
  8283. while (i < bytes) {
  8284. k1 =
  8285. ((key.charCodeAt(i) & 0xff)) |
  8286. ((key.charCodeAt(++i) & 0xff) << 8) |
  8287. ((key.charCodeAt(++i) & 0xff) << 16) |
  8288. ((key.charCodeAt(++i) & 0xff) << 24);
  8289. ++i;
  8290. k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff;
  8291. k1 = (k1 << 15) | (k1 >>> 17);
  8292. k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff;
  8293. h1 ^= k1;
  8294. h1 = (h1 << 13) | (h1 >>> 19);
  8295. h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff;
  8296. h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16));
  8297. }
  8298. k1 = 0;
  8299. switch (remainder) {
  8300. /*jshint -W086:true */
  8301. case 3: k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
  8302. case 2: k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
  8303. case 1: k1 ^= (key.charCodeAt(i) & 0xff);
  8304. k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
  8305. k1 = (k1 << 15) | (k1 >>> 17);
  8306. k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
  8307. h1 ^= k1;
  8308. }
  8309. h1 ^= key.length;
  8310. h1 ^= h1 >>> 16;
  8311. h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
  8312. h1 ^= h1 >>> 13;
  8313. h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff;
  8314. h1 ^= h1 >>> 16;
  8315. if( asString ){
  8316. // Convert to 8 digit hex string
  8317. return ("0000000" + (h1 >>> 0).toString(16)).substr(-8);
  8318. }
  8319. return h1 >>> 0;
  8320. }
  8321. // console.info(hashMurmur3("costarring"));
  8322. // console.info(hashMurmur3("costarring", true));
  8323. // console.info(hashMurmur3("liquid"));
  8324. // console.info(hashMurmur3("liquid", true));
  8325. /*
  8326. * Return a unique key for node by calculationg the hash of the parents refKey-list
  8327. */
  8328. function calcUniqueKey(node) {
  8329. var key,
  8330. path = $.map(node.getParentList(false, true), function(e){ return e.refKey || e.key; });
  8331. path = path.join("/");
  8332. key = "id_" + hashMurmur3(path, true);
  8333. // node.debug(path + " -> " + key);
  8334. return key;
  8335. }
  8336. /**
  8337. * [ext-clones] Return a list of clone-nodes or null.
  8338. * @param {boolean} [includeSelf=false]
  8339. * @returns {FancytreeNode[] | null}
  8340. *
  8341. * @alias FancytreeNode#getCloneList
  8342. * @requires jquery.fancytree.clones.js
  8343. */
  8344. $.ui.fancytree._FancytreeNodeClass.prototype.getCloneList = function(includeSelf){
  8345. var key,
  8346. tree = this.tree,
  8347. refList = tree.refMap[this.refKey] || null,
  8348. keyMap = tree.keyMap;
  8349. if( refList ) {
  8350. key = this.key;
  8351. // Convert key list to node list
  8352. if( includeSelf ) {
  8353. refList = $.map(refList, function(val){ return keyMap[val]; });
  8354. } else {
  8355. refList = $.map(refList, function(val){ return val === key ? null : keyMap[val]; });
  8356. if( refList.length < 1 ) {
  8357. refList = null;
  8358. }
  8359. }
  8360. }
  8361. return refList;
  8362. };
  8363. /**
  8364. * [ext-clones] Return true if this node has at least another clone with same refKey.
  8365. * @returns {boolean}
  8366. *
  8367. * @alias FancytreeNode#isClone
  8368. * @requires jquery.fancytree.clones.js
  8369. */
  8370. $.ui.fancytree._FancytreeNodeClass.prototype.isClone = function(){
  8371. var refKey = this.refKey || null,
  8372. refList = refKey && this.tree.refMap[refKey] || null;
  8373. return !!(refList && refList.length > 1);
  8374. };
  8375. /**
  8376. * [ext-clones] Update key and/or refKey for an existing node.
  8377. * @param {string} key
  8378. * @param {string} refKey
  8379. * @returns {boolean}
  8380. *
  8381. * @alias FancytreeNode#reRegister
  8382. * @requires jquery.fancytree.clones.js
  8383. */
  8384. $.ui.fancytree._FancytreeNodeClass.prototype.reRegister = function(key, refKey){
  8385. key = (key == null) ? null : "" + key;
  8386. refKey = (refKey == null) ? null : "" + refKey;
  8387. // this.debug("reRegister", key, refKey);
  8388. var tree = this.tree,
  8389. prevKey = this.key,
  8390. prevRefKey = this.refKey,
  8391. keyMap = tree.keyMap,
  8392. refMap = tree.refMap,
  8393. refList = refMap[prevRefKey] || null,
  8394. // curCloneKeys = refList ? node.getCloneList(true),
  8395. modified = false;
  8396. // Key has changed: update all references
  8397. if( key != null && key !== this.key ) {
  8398. if( keyMap[key] ) {
  8399. $.error("[ext-clones] reRegister(" + key + "): already exists: " + this);
  8400. }
  8401. // Update keyMap
  8402. delete keyMap[prevKey];
  8403. keyMap[key] = this;
  8404. // Update refMap
  8405. if( refList ) {
  8406. refMap[prevRefKey] = $.map(refList, function(e){
  8407. return e === prevKey ? key : e;
  8408. });
  8409. }
  8410. this.key = key;
  8411. modified = true;
  8412. }
  8413. // refKey has changed
  8414. if( refKey != null && refKey !== this.refKey ) {
  8415. // Remove previous refKeys
  8416. if( refList ){
  8417. if( refList.length === 1 ){
  8418. delete refMap[prevRefKey];
  8419. }else{
  8420. refMap[prevRefKey] = $.map(refList, function(e){
  8421. return e === prevKey ? null : e;
  8422. });
  8423. }
  8424. }
  8425. // Add refKey
  8426. if( refMap[refKey] ) {
  8427. refMap[refKey].append(key);
  8428. }else{
  8429. refMap[refKey] = [ this.key ];
  8430. }
  8431. this.refKey = refKey;
  8432. modified = true;
  8433. }
  8434. return modified;
  8435. };
  8436. /**
  8437. * [ext-clones] Define a refKey for an existing node.
  8438. * @param {string} refKey
  8439. * @returns {boolean}
  8440. *
  8441. * @alias FancytreeNode#setRefKey
  8442. * @requires jquery.fancytree.clones.js
  8443. * @since 2.16
  8444. */
  8445. $.ui.fancytree._FancytreeNodeClass.prototype.setRefKey = function(refKey){
  8446. return this.reRegister(null, refKey);
  8447. };
  8448. /**
  8449. * [ext-clones] Return all nodes with a given refKey (null if not found).
  8450. * @param {string} refKey
  8451. * @param {FancytreeNode} [rootNode] optionally restrict results to descendants of this node
  8452. * @returns {FancytreeNode[] | null}
  8453. * @alias Fancytree#getNodesByRef
  8454. * @requires jquery.fancytree.clones.js
  8455. */
  8456. $.ui.fancytree._FancytreeClass.prototype.getNodesByRef = function(refKey, rootNode){
  8457. var keyMap = this.keyMap,
  8458. refList = this.refMap[refKey] || null;
  8459. if( refList ) {
  8460. // Convert key list to node list
  8461. if( rootNode ) {
  8462. refList = $.map(refList, function(val){
  8463. var node = keyMap[val];
  8464. return node.isDescendantOf(rootNode) ? node : null;
  8465. });
  8466. }else{
  8467. refList = $.map(refList, function(val){ return keyMap[val]; });
  8468. }
  8469. if( refList.length < 1 ) {
  8470. refList = null;
  8471. }
  8472. }
  8473. return refList;
  8474. };
  8475. /**
  8476. * [ext-clones] Replace a refKey with a new one.
  8477. * @param {string} oldRefKey
  8478. * @param {string} newRefKey
  8479. * @alias Fancytree#changeRefKey
  8480. * @requires jquery.fancytree.clones.js
  8481. */
  8482. $.ui.fancytree._FancytreeClass.prototype.changeRefKey = function(oldRefKey, newRefKey) {
  8483. var i, node,
  8484. keyMap = this.keyMap,
  8485. refList = this.refMap[oldRefKey] || null;
  8486. if (refList) {
  8487. for (i = 0; i < refList.length; i++) {
  8488. node = keyMap[refList[i]];
  8489. node.refKey = newRefKey;
  8490. }
  8491. delete this.refMap[oldRefKey];
  8492. this.refMap[newRefKey] = refList;
  8493. }
  8494. };
  8495. /*******************************************************************************
  8496. * Extension code
  8497. */
  8498. $.ui.fancytree.registerExtension({
  8499. name: "clones",
  8500. version: "2.28.1",
  8501. // Default options for this extension.
  8502. options: {
  8503. highlightActiveClones: true, // set 'fancytree-active-clone' on active clones and all peers
  8504. highlightClones: false // set 'fancytree-clone' class on any node that has at least one clone
  8505. },
  8506. treeCreate: function(ctx){
  8507. this._superApply(arguments);
  8508. ctx.tree.refMap = {};
  8509. ctx.tree.keyMap = {};
  8510. },
  8511. treeInit: function(ctx){
  8512. this.$container.addClass("fancytree-ext-clones");
  8513. _assert(ctx.options.defaultKey == null);
  8514. // Generate unique / reproducible default keys
  8515. ctx.options.defaultKey = function(node){
  8516. return calcUniqueKey(node);
  8517. };
  8518. // The default implementation loads initial data
  8519. this._superApply(arguments);
  8520. },
  8521. treeClear: function(ctx){
  8522. ctx.tree.refMap = {};
  8523. ctx.tree.keyMap = {};
  8524. return this._superApply(arguments);
  8525. },
  8526. treeRegisterNode: function(ctx, add, node) {
  8527. var refList, len,
  8528. tree = ctx.tree,
  8529. keyMap = tree.keyMap,
  8530. refMap = tree.refMap,
  8531. key = node.key,
  8532. refKey = (node && node.refKey != null) ? "" + node.refKey : null;
  8533. // ctx.tree.debug("clones.treeRegisterNode", add, node);
  8534. if( node.isStatusNode() ){
  8535. return this._super(ctx, add, node);
  8536. }
  8537. if( add ) {
  8538. if( keyMap[node.key] != null ) {
  8539. $.error("clones.treeRegisterNode: node.key already exists: " + node);
  8540. }
  8541. keyMap[key] = node;
  8542. if( refKey ) {
  8543. refList = refMap[refKey];
  8544. if( refList ) {
  8545. refList.push(key);
  8546. if( refList.length === 2 && ctx.options.clones.highlightClones ) {
  8547. // Mark peer node, if it just became a clone (no need to
  8548. // mark current node, since it will be rendered later anyway)
  8549. keyMap[refList[0]].renderStatus();
  8550. }
  8551. } else {
  8552. refMap[refKey] = [key];
  8553. }
  8554. // node.debug("clones.treeRegisterNode: add clone =>", refMap[refKey]);
  8555. }
  8556. }else {
  8557. if( keyMap[key] == null ) {
  8558. $.error("clones.treeRegisterNode: node.key not registered: " + node.key);
  8559. }
  8560. delete keyMap[key];
  8561. if( refKey ) {
  8562. refList = refMap[refKey];
  8563. // node.debug("clones.treeRegisterNode: remove clone BEFORE =>", refMap[refKey]);
  8564. if( refList ) {
  8565. len = refList.length;
  8566. if( len <= 1 ){
  8567. _assert(len === 1);
  8568. _assert(refList[0] === key);
  8569. delete refMap[refKey];
  8570. }else{
  8571. _removeArrayMember(refList, key);
  8572. // Unmark peer node, if this was the only clone
  8573. if( len === 2 && ctx.options.clones.highlightClones ) {
  8574. // node.debug("clones.treeRegisterNode: last =>", node.getCloneList());
  8575. keyMap[refList[0]].renderStatus();
  8576. }
  8577. }
  8578. // node.debug("clones.treeRegisterNode: remove clone =>", refMap[refKey]);
  8579. }
  8580. }
  8581. }
  8582. return this._super(ctx, add, node);
  8583. },
  8584. nodeRenderStatus: function(ctx) {
  8585. var $span, res,
  8586. node = ctx.node;
  8587. res = this._super(ctx);
  8588. if( ctx.options.clones.highlightClones ) {
  8589. $span = $(node[ctx.tree.statusClassPropName]);
  8590. // Only if span already exists
  8591. if( $span.length && node.isClone() ){
  8592. // node.debug("clones.nodeRenderStatus: ", ctx.options.clones.highlightClones);
  8593. $span.addClass("fancytree-clone");
  8594. }
  8595. }
  8596. return res;
  8597. },
  8598. nodeSetActive: function(ctx, flag, callOpts) {
  8599. var res,
  8600. scpn = ctx.tree.statusClassPropName,
  8601. node = ctx.node;
  8602. res = this._superApply(arguments);
  8603. if( ctx.options.clones.highlightActiveClones && node.isClone() ) {
  8604. $.each(node.getCloneList(true), function(idx, n){
  8605. // n.debug("clones.nodeSetActive: ", flag !== false);
  8606. $(n[scpn]).toggleClass("fancytree-active-clone", flag !== false);
  8607. });
  8608. }
  8609. return res;
  8610. }
  8611. });
  8612. // Value returned by `require('jquery.fancytree..')`
  8613. return $.ui.fancytree;
  8614. })); // End of closure
  8615. /*! Extension 'jquery.fancytree.dnd5.js' *//*!
  8616. * jquery.fancytree.dnd5.js
  8617. *
  8618. * Drag-and-drop support (native HTML5).
  8619. * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
  8620. *
  8621. * Copyright (c) 2008-2018, Martin Wendt (http://wwWendt.de)
  8622. *
  8623. * Released under the MIT license
  8624. * https://github.com/mar10/fancytree/wiki/LicenseInfo
  8625. *
  8626. * @version 2.28.1
  8627. * @date 2018-03-19T06:47:37Z
  8628. */
  8629. /*
  8630. #TODO
  8631. Compatiblity when dragging between *separate* windows:
  8632. Drag from Chrome Edge FF IE11 Safari
  8633. To Chrome ok ok ok NO ?
  8634. Edge ok ok ok NO ?
  8635. FF ok ok ok NO ?
  8636. IE 11 ok ok ok ok ?
  8637. Safari ? ? ? ? ok
  8638. */
  8639. ;(function( factory ) {
  8640. if ( typeof define === "function" && define.amd ) {
  8641. // AMD. Register as an anonymous module.
  8642. define( [ "jquery", "./jquery.fancytree" ], factory );
  8643. } else if ( typeof module === "object" && module.exports ) {
  8644. // Node/CommonJS
  8645. require("./jquery.fancytree");
  8646. module.exports = factory(require("jquery"));
  8647. } else {
  8648. // Browser globals
  8649. factory( jQuery );
  8650. }
  8651. }( function( $ ) {
  8652. "use strict";
  8653. /* *****************************************************************************
  8654. * Private functions and variables
  8655. */
  8656. var FT = $.ui.fancytree,
  8657. isMac = /Mac/.test(navigator.platform),
  8658. classDragSource = "fancytree-drag-source",
  8659. classDragRemove = "fancytree-drag-remove",
  8660. classDropAccept = "fancytree-drop-accept",
  8661. classDropAfter = "fancytree-drop-after",
  8662. classDropBefore = "fancytree-drop-before",
  8663. classDropOver = "fancytree-drop-over",
  8664. classDropReject = "fancytree-drop-reject",
  8665. classDropTarget = "fancytree-drop-target",
  8666. nodeMimeType = "application/x-fancytree-node",
  8667. $dropMarker = null,
  8668. SOURCE_NODE = null,
  8669. SOURCE_NODE_LIST = null,
  8670. $sourceList = null,
  8671. DRAG_ENTER_RESPONSE = null,
  8672. LAST_HIT_MODE = null;
  8673. /* */
  8674. function _clearGlobals() {
  8675. SOURCE_NODE = null;
  8676. SOURCE_NODE_LIST = null;
  8677. $sourceList = null;
  8678. DRAG_ENTER_RESPONSE = null;
  8679. }
  8680. /* Convert number to string and prepend +/-; return empty string for 0.*/
  8681. function offsetString(n){
  8682. return n === 0 ? "" : (( n > 0 ) ? ("+" + n) : ("" + n));
  8683. }
  8684. /* Convert a dragEnter() or dragOver() response to a canonical form.
  8685. * Return false or plain object
  8686. * @param {string|object|boolean} r
  8687. * @return {object|false}
  8688. */
  8689. function normalizeDragEnterResponse(r) {
  8690. var res;
  8691. if( !r ){
  8692. return false;
  8693. }
  8694. if ( $.isPlainObject(r) ) {
  8695. res = {
  8696. over: !!r.over,
  8697. before: !!r.before,
  8698. after: !!r.after
  8699. };
  8700. }else if ( $.isArray(r) ) {
  8701. res = {
  8702. over: ($.inArray("over", r) >= 0),
  8703. before: ($.inArray("before", r) >= 0),
  8704. after: ($.inArray("after", r) >= 0)
  8705. };
  8706. }else{
  8707. res = {
  8708. over: ((r === true) || (r === "over")),
  8709. before: ((r === true) || (r === "before")),
  8710. after: ((r === true) || (r === "after"))
  8711. };
  8712. }
  8713. if( Object.keys(res).length === 0 ) {
  8714. return false;
  8715. }
  8716. // if( Object.keys(res).length === 1 ) {
  8717. // res.unique = res[0];
  8718. // }
  8719. return res;
  8720. }
  8721. /* Implement auto scrolling when drag cursor is in top/bottom area of scroll parent. */
  8722. function autoScroll(tree, event) {
  8723. var spOfs, scrollTop, delta,
  8724. dndOpts = tree.options.dnd5,
  8725. sp = tree.$scrollParent[0],
  8726. sensitivity = dndOpts.scrollSensitivity,
  8727. speed = dndOpts.scrollSpeed,
  8728. scrolled = 0;
  8729. if ( sp !== document && sp.tagName !== "HTML" ) {
  8730. spOfs = tree.$scrollParent.offset();
  8731. scrollTop = sp.scrollTop;
  8732. if ( spOfs.top + sp.offsetHeight - event.pageY < sensitivity ) {
  8733. delta = (sp.scrollHeight - tree.$scrollParent.innerHeight() - scrollTop);
  8734. // console.log ("sp.offsetHeight: " + sp.offsetHeight
  8735. // + ", spOfs.top: " + spOfs.top
  8736. // + ", scrollTop: " + scrollTop
  8737. // + ", innerHeight: " + tree.$scrollParent.innerHeight()
  8738. // + ", scrollHeight: " + sp.scrollHeight
  8739. // + ", delta: " + delta
  8740. // );
  8741. if( delta > 0 ) {
  8742. sp.scrollTop = scrolled = scrollTop + speed;
  8743. }
  8744. } else if ( scrollTop > 0 && event.pageY - spOfs.top < sensitivity ) {
  8745. sp.scrollTop = scrolled = scrollTop - speed;
  8746. }
  8747. } else {
  8748. scrollTop = $(document).scrollTop();
  8749. if (scrollTop > 0 && event.pageY - scrollTop < sensitivity) {
  8750. scrolled = scrollTop - speed;
  8751. $(document).scrollTop(scrolled);
  8752. } else if ($(window).height() - (event.pageY - scrollTop) < sensitivity) {
  8753. scrolled = scrollTop + speed;
  8754. $(document).scrollTop(scrolled);
  8755. }
  8756. }
  8757. if( scrolled ) {
  8758. tree.debug("autoScroll: " + scrolled + "px");
  8759. }
  8760. return scrolled;
  8761. }
  8762. /* Handle dragover event (fired every x ms) and return hitMode. */
  8763. function handleDragOver(event, data) {
  8764. // Implement auto-scrolling
  8765. if ( data.options.dnd5.scroll ) {
  8766. autoScroll(data.tree, event);
  8767. }
  8768. // Bail out with previous response if we get an invalid dragover
  8769. if( !data.node ) {
  8770. data.tree.warn("Ignore dragover for non-node"); //, event, data);
  8771. return LAST_HIT_MODE;
  8772. }
  8773. var markerOffsetX, nodeOfs, pos, relPosY,
  8774. hitMode = null,
  8775. tree = data.tree,
  8776. options = tree.options,
  8777. dndOpts = options.dnd5,
  8778. targetNode = data.node,
  8779. sourceNode = data.otherNode,
  8780. markerAt = "center",
  8781. $target = $(targetNode.span),
  8782. $targetTitle = $target.find("span.fancytree-title");
  8783. if(DRAG_ENTER_RESPONSE === false){
  8784. tree.info("Ignore dragover, since dragenter returned false"); //, event, data);
  8785. // $.error("assert failed: dragenter returned false");
  8786. return false;
  8787. } else if(typeof DRAG_ENTER_RESPONSE === "string") {
  8788. $.error("assert failed: dragenter returned string");
  8789. // Use hitMode from onEnter if provided.
  8790. // hitMode = DRAG_ENTER_RESPONSE;
  8791. } else {
  8792. // Calculate hitMode from relative cursor position.
  8793. nodeOfs = $target.offset();
  8794. relPosY = (event.pageY - nodeOfs.top) / $target.height();
  8795. if( DRAG_ENTER_RESPONSE.after && relPosY > 0.75 ){
  8796. hitMode = "after";
  8797. } else if(!DRAG_ENTER_RESPONSE.over && DRAG_ENTER_RESPONSE.after && relPosY > 0.5 ){
  8798. hitMode = "after";
  8799. } else if(DRAG_ENTER_RESPONSE.before && relPosY <= 0.25) {
  8800. hitMode = "before";
  8801. } else if(!DRAG_ENTER_RESPONSE.over && DRAG_ENTER_RESPONSE.before && relPosY <= 0.5) {
  8802. hitMode = "before";
  8803. } else if(DRAG_ENTER_RESPONSE.over) {
  8804. hitMode = "over";
  8805. }
  8806. // Prevent no-ops like 'before source node'
  8807. // TODO: these are no-ops when moving nodes, but not in copy mode
  8808. if( dndOpts.preventVoidMoves ){
  8809. if(targetNode === sourceNode){
  8810. targetNode.debug("Drop over source node prevented.");
  8811. hitMode = null;
  8812. }else if(hitMode === "before" && sourceNode && targetNode === sourceNode.getNextSibling()){
  8813. targetNode.debug("Drop after source node prevented.");
  8814. hitMode = null;
  8815. }else if(hitMode === "after" && sourceNode && targetNode === sourceNode.getPrevSibling()){
  8816. targetNode.debug("Drop before source node prevented.");
  8817. hitMode = null;
  8818. }else if(hitMode === "over" && sourceNode && sourceNode.parent === targetNode && sourceNode.isLastSibling() ){
  8819. targetNode.debug("Drop last child over own parent prevented.");
  8820. hitMode = null;
  8821. }
  8822. }
  8823. }
  8824. // Let callback modify the calculated hitMode
  8825. data.hitMode = hitMode;
  8826. if(hitMode && dndOpts.dragOver){
  8827. // TODO: http://code.google.com/p/dynatree/source/detail?r=625
  8828. dndOpts.dragOver(targetNode, data);
  8829. hitMode = data.hitMode;
  8830. }
  8831. // LAST_DROP_EFFECT = data.dataTransfer.dropEffect;
  8832. // LAST_EFFECT_ALLOWED = data.dataTransfer.effectAllowed;
  8833. LAST_HIT_MODE = hitMode;
  8834. //
  8835. if( hitMode === "after" || hitMode === "before" || hitMode === "over" ){
  8836. markerOffsetX = dndOpts.dropMarkerOffsetX || 0;
  8837. switch(hitMode){
  8838. case "before":
  8839. markerAt = "top";
  8840. markerOffsetX += (dndOpts.dropMarkerInsertOffsetX || 0);
  8841. break;
  8842. case "after":
  8843. markerAt = "bottom";
  8844. markerOffsetX += (dndOpts.dropMarkerInsertOffsetX || 0);
  8845. break;
  8846. }
  8847. pos = {
  8848. my: "left" + offsetString(markerOffsetX) + " center",
  8849. at: "left " + markerAt,
  8850. of: $targetTitle
  8851. };
  8852. if( options.rtl ) {
  8853. pos.my = "right" + offsetString(-markerOffsetX) + " center";
  8854. pos.at = "right " + markerAt;
  8855. // console.log("rtl", pos);
  8856. }
  8857. $dropMarker
  8858. .toggleClass(classDropAfter, hitMode === "after")
  8859. .toggleClass(classDropOver, hitMode === "over")
  8860. .toggleClass(classDropBefore, hitMode === "before")
  8861. .show()
  8862. .position(FT.fixPositionOptions(pos));
  8863. } else {
  8864. $dropMarker.hide();
  8865. // console.log("hide dropmarker")
  8866. }
  8867. $(targetNode.span)
  8868. .toggleClass(classDropTarget, hitMode === "after" || hitMode === "before" || hitMode === "over")
  8869. .toggleClass(classDropAfter, hitMode === "after")
  8870. .toggleClass(classDropBefore, hitMode === "before")
  8871. .toggleClass(classDropAccept, hitMode === "over")
  8872. .toggleClass(classDropReject, hitMode === false);
  8873. return hitMode;
  8874. }
  8875. /* Guess dropEffect from modifier keys.
  8876. * Safari:
  8877. * It seems that `dataTransfer.dropEffect` can only be set on dragStart, and will remain
  8878. * even if the cursor changes when [Alt] or [Ctrl] are pressed (?)
  8879. * Using rules suggested here:
  8880. * https://ux.stackexchange.com/a/83769
  8881. * @returns
  8882. * 'copy', 'link', 'move', or 'none'
  8883. */
  8884. function getDropEffect(event, data) {
  8885. var dndOpts = data.options.dnd5,
  8886. res = dndOpts.dropEffectDefault
  8887. // dataTransfer = event.dataTransfer || event.originalEvent.dataTransfer,
  8888. ;
  8889. // Use callback if any:
  8890. if( dndOpts.dropEffect ) {
  8891. return dndOpts.dropEffect(event, data);
  8892. }
  8893. if( isMac ) {
  8894. if( event.metaKey && event.altKey ) { // Mac: [Control] + [Option]
  8895. return "link";
  8896. } else if( event.metaKey ) { // Mac: [Command]
  8897. return "move";
  8898. } else if( event.altKey ) { // Mac: [Option]
  8899. return "copy";
  8900. }
  8901. } else {
  8902. if( event.ctrlKey ) { // Windows: [Ctrl]
  8903. return "copy";
  8904. } else if( event.shiftKey ) { // Windows: [Shift]
  8905. return "move";
  8906. } else if( event.altKey ) { // Windows: [Alt]
  8907. return "link";
  8908. }
  8909. }
  8910. // data.tree.debug("getDropEffect: " + res);
  8911. return res;
  8912. }
  8913. /* *****************************************************************************
  8914. *
  8915. */
  8916. $.ui.fancytree.registerExtension({
  8917. name: "dnd5",
  8918. version: "2.28.1",
  8919. // Default options for this extension.
  8920. options: {
  8921. autoExpandMS: 1500, // Expand nodes after n milliseconds of hovering
  8922. dropMarkerInsertOffsetX: -16,// Additional offset for drop-marker with hitMode = "before"/"after"
  8923. dropMarkerOffsetX: -24, // Absolute position offset for .fancytree-drop-marker relatively to ..fancytree-title (icon/img near a node accepting drop)
  8924. multiSource: false, // true: Drag multiple (i.e. selected) nodes.
  8925. dragImage: null, // Callback(node, data) that can be used to call dataTransfer.setDragImage().
  8926. dropEffect: null, // Callback(node, data) that returns 'copy', 'link', 'move', or 'none'.
  8927. dropEffectDefault: "move", // Default dropEffect ('copy', 'link', or 'move').
  8928. preventForeignNodes: false, // Prevent dropping nodes from different Fancytrees
  8929. preventNonNodes: false, // Prevent dropping items other than Fancytree nodes
  8930. preventRecursiveMoves: true, // Prevent dropping nodes on own descendants
  8931. preventVoidMoves: true, // Prevent dropping nodes 'before self', etc.
  8932. scroll: true, // Enable auto-scrolling while dragging
  8933. scrollSensitivity: 20, // Active top/bottom margin in pixel
  8934. scrollSpeed: 5, // Pixel per event
  8935. setTextTypeJson: false, // Allow dragging of nodes to different IE windows
  8936. // Events (drag support)
  8937. dragStart: null, // Callback(sourceNode, data), return true, to enable dnd drag
  8938. dragDrag: $.noop, // Callback(sourceNode, data)
  8939. dragEnd: $.noop, // Callback(sourceNode, data)
  8940. // Events (drop support)
  8941. dragEnter: null, // Callback(targetNode, data), return true, to enable dnd drop
  8942. dragOver: $.noop, // Callback(targetNode, data)
  8943. dragExpand: $.noop, // Callback(targetNode, data), return false to prevent autoExpand
  8944. dragDrop: $.noop, // Callback(targetNode, data)
  8945. dragLeave: $.noop // Callback(targetNode, data)
  8946. },
  8947. treeInit: function(ctx){
  8948. var $dragImage, $extraHelper, $temp,
  8949. tree = ctx.tree,
  8950. opts = ctx.options,
  8951. glyph = opts.glyph || null,
  8952. dndOpts = opts.dnd5,
  8953. getNode = FT.getNode;
  8954. if( $.inArray("dnd", opts.extensions) >= 0 ) {
  8955. $.error("Extensions 'dnd' and 'dnd5' are mutually exclusive.");
  8956. }
  8957. if( dndOpts.dragStop ) {
  8958. $.error("dragStop is not used by ext-dnd5. Use dragEnd instead.");
  8959. }
  8960. // Implement `opts.createNode` event to add the 'draggable' attribute
  8961. // #680: this must happen before calling super.treeInit()
  8962. if( dndOpts.dragStart ) {
  8963. FT.overrideMethod(ctx.options, "createNode", function(event, data) {
  8964. // Default processing if any
  8965. this._super.apply(this, arguments);
  8966. data.node.span.draggable = true;
  8967. });
  8968. }
  8969. this._superApply(arguments);
  8970. this.$container.addClass("fancytree-ext-dnd5");
  8971. // Store the current scroll parent, which may be the tree
  8972. // container, any enclosing div, or the document.
  8973. // #761: scrollParent() always needs a container child
  8974. $temp = $("<span>").appendTo(this.$container);
  8975. this.$scrollParent = $temp.scrollParent();
  8976. $temp.remove();
  8977. $dropMarker = $("#fancytree-drop-marker");
  8978. if( !$dropMarker.length ) {
  8979. $dropMarker = $("<div id='fancytree-drop-marker'></div>")
  8980. .hide()
  8981. .css({
  8982. "z-index": 1000,
  8983. // Drop marker should not steal dragenter/dragover events:
  8984. "pointer-events": "none"
  8985. }).prependTo("body");
  8986. if( glyph ) {
  8987. FT.setSpanIcon($dropMarker[0], glyph.map._addClass, glyph.map.dropMarker);
  8988. // $dropMarker.addClass(glyph.map._addClass + " " + glyph.map.dropMarker);
  8989. }
  8990. }
  8991. $dropMarker.toggleClass("fancytree-rtl", !!opts.rtl);
  8992. // Enable drag support if dragStart() is specified:
  8993. if( dndOpts.dragStart ) {
  8994. // Bind drag event handlers
  8995. tree.$container.on("dragstart drag dragend", function(event){
  8996. var json,
  8997. node = getNode(event),
  8998. dataTransfer = event.dataTransfer || event.originalEvent.dataTransfer,
  8999. data = {
  9000. node: node,
  9001. tree: tree,
  9002. options: tree.options,
  9003. originalEvent: event,
  9004. dataTransfer: dataTransfer,
  9005. // dropEffect: undefined, // set by dragend
  9006. isCancelled: undefined // set by dragend
  9007. },
  9008. dropEffect = getDropEffect(event, data),
  9009. isMove = dropEffect === "move";
  9010. // console.log(event.type, "dropEffect: " + dropEffect);
  9011. switch( event.type ) {
  9012. case "dragstart":
  9013. // Store current source node in different formats
  9014. SOURCE_NODE = node;
  9015. // Also optionally store selected nodes
  9016. if( dndOpts.multiSource === false ) {
  9017. SOURCE_NODE_LIST = [node];
  9018. } else if( dndOpts.multiSource === true ) {
  9019. SOURCE_NODE_LIST = tree.getSelectedNodes();
  9020. if( !node.isSelected() ) {
  9021. SOURCE_NODE_LIST.unshift(node);
  9022. }
  9023. } else {
  9024. SOURCE_NODE_LIST = dndOpts.multiSource(node, data);
  9025. }
  9026. // Cache as array of jQuery objects for faster access:
  9027. $sourceList = $($.map(SOURCE_NODE_LIST, function(n){
  9028. return n.span;
  9029. }));
  9030. // Set visual feedback
  9031. $sourceList.addClass(classDragSource);
  9032. // Set payload
  9033. // Note:
  9034. // Transfer data is only accessible on dragstart and drop!
  9035. // For all other events the formats and kinds in the drag
  9036. // data store list of items representing dragged data can be
  9037. // enumerated, but the data itself is unavailable and no new
  9038. // data can be added.
  9039. json = JSON.stringify(node.toDict());
  9040. try {
  9041. dataTransfer.setData(nodeMimeType, json);
  9042. dataTransfer.setData("text/html", $(node.span).html());
  9043. dataTransfer.setData("text/plain", node.title);
  9044. } catch(ex) {
  9045. // IE only accepts 'text' type
  9046. tree.warn("Could not set data (IE only accepts 'text') - " + ex);
  9047. }
  9048. // We always need to set the 'text' type if we want to drag
  9049. // Because IE 11 only accepts this single type.
  9050. // If we pass JSON here, IE can can access all node properties,
  9051. // even when the source lives in another window. (D'n'd inside
  9052. // the same window will always work.)
  9053. // The drawback is, that in this case ALL browsers will see
  9054. // the JSON representation as 'text', so dragging
  9055. // to a text field will insert the JSON string instead of
  9056. // the node title.
  9057. if( dndOpts.setTextTypeJson ) {
  9058. dataTransfer.setData("text", json);
  9059. } else {
  9060. dataTransfer.setData("text", node.title);
  9061. }
  9062. // Set the allowed and current drag mode (move, copy, or link)
  9063. dataTransfer.effectAllowed = "all"; // "copyMove"
  9064. // dropEffect = "move";
  9065. $extraHelper = null;
  9066. if( dndOpts.dragImage ) {
  9067. // Let caller set a custom drag image using dataTransfer.setDragImage()
  9068. // and/or modify visual feedback otherwise.
  9069. dndOpts.dragImage(node, data);
  9070. } else {
  9071. // Set the title as drag image (otherwise it would contain the expander)
  9072. $dragImage = $(node.span).find(".fancytree-title");
  9073. if( SOURCE_NODE_LIST && SOURCE_NODE_LIST.length > 1 ) {
  9074. // Add a counter badge to node title if dragging more than one node.
  9075. // We want this, because the element that is used as drag image
  9076. // must be *visible* in the DOM, so we cannot create some hidden
  9077. // custom markup.
  9078. // See https://kryogenix.org/code/browser/custom-drag-image.html
  9079. // Also, since IE 11 and Edge don't support setDragImage() alltogether,
  9080. // it gives som feedback to the user.
  9081. // The badge will be removed later on drag end.
  9082. $extraHelper = $("<span class='fancytree-childcounter'/>")
  9083. .text("+" + (SOURCE_NODE_LIST.length - 1))
  9084. .appendTo($dragImage);
  9085. }
  9086. if( dataTransfer.setDragImage ) {
  9087. // IE 11 and Edge do not support this
  9088. dataTransfer.setDragImage( $dragImage[0], -10, -10);
  9089. }
  9090. }
  9091. // Let user modify above settings
  9092. return dndOpts.dragStart(node, data) !== false;
  9093. case "drag":
  9094. // Called every few miliseconds
  9095. // data.tree.info("drag", SOURCE_NODE)
  9096. $sourceList.toggleClass(classDragRemove, isMove);
  9097. dndOpts.dragDrag(node, data);
  9098. break;
  9099. case "dragend":
  9100. $sourceList.removeClass(classDragSource + " " + classDragRemove);
  9101. _clearGlobals();
  9102. // data.dropEffect = dropEffect;
  9103. data.isCancelled = (dropEffect === "none");
  9104. $dropMarker.hide();
  9105. // Take this badge off of me - I can't use it anymore:
  9106. if( $extraHelper ) {
  9107. $extraHelper.remove();
  9108. $extraHelper = null;
  9109. }
  9110. dndOpts.dragEnd(node, data);
  9111. break;
  9112. }
  9113. });
  9114. }
  9115. // Enable drop support if dragEnter() is specified:
  9116. if( dndOpts.dragEnter ) {
  9117. // Bind drop event handlers
  9118. tree.$container.on("dragenter dragover dragleave drop", function(event){
  9119. var json, nodeData, r, res,
  9120. allowDrop = null,
  9121. node = getNode(event),
  9122. dataTransfer = event.dataTransfer || event.originalEvent.dataTransfer,
  9123. // dropEffect = getDropEffect(dataTransfer, opts),
  9124. data = {
  9125. node: node,
  9126. tree: tree,
  9127. options: tree.options,
  9128. hitMode: DRAG_ENTER_RESPONSE,
  9129. originalEvent: event,
  9130. dataTransfer: dataTransfer,
  9131. otherNode: SOURCE_NODE || null,
  9132. otherNodeList: SOURCE_NODE_LIST || null,
  9133. otherNodeData: null, // set by drop event
  9134. dropEffect: undefined, // set by drop event
  9135. isCancelled: undefined // set by drop event
  9136. };
  9137. switch( event.type ) {
  9138. case "dragenter":
  9139. // The dragenter event is fired when a dragged element or
  9140. // text selection enters a valid drop target.
  9141. if( !node ) {
  9142. // Sometimes we get dragenter for the container element
  9143. tree.debug("Ignore non-node " + event.type + ": " + event.target.tagName + "." + event.target.className);
  9144. DRAG_ENTER_RESPONSE = false;
  9145. break;
  9146. }
  9147. $(node.span)
  9148. .addClass(classDropOver)
  9149. .removeClass(classDropAccept + " " + classDropReject);
  9150. if( dndOpts.preventNonNodes && !nodeData ) {
  9151. node.debug("Reject dropping a non-node.");
  9152. DRAG_ENTER_RESPONSE = false;
  9153. break;
  9154. } else if( dndOpts.preventForeignNodes && (!SOURCE_NODE || SOURCE_NODE.tree !== node.tree ) ) {
  9155. node.debug("Reject dropping a foreign node.");
  9156. DRAG_ENTER_RESPONSE = false;
  9157. break;
  9158. }
  9159. // NOTE: dragenter is fired BEFORE the dragleave event
  9160. // of the previous element!
  9161. // https://www.w3.org/Bugs/Public/show_bug.cgi?id=19041
  9162. setTimeout(function(){
  9163. // node.info("DELAYED " + event.type, event.target, DRAG_ENTER_RESPONSE);
  9164. // Auto-expand node (only when 'over' the node, not 'before', or 'after')
  9165. if( dndOpts.autoExpandMS &&
  9166. node.hasChildren() !== false && !node.expanded &&
  9167. (!dndOpts.dragExpand || dndOpts.dragExpand(node, data) !== false)
  9168. ) {
  9169. node.scheduleAction("expand", dndOpts.autoExpandMS);
  9170. }
  9171. }, 0);
  9172. $dropMarker.show();
  9173. // Call dragEnter() to figure out if (and where) dropping is allowed
  9174. if( dndOpts.preventRecursiveMoves && node.isDescendantOf(data.otherNode) ){
  9175. node.debug("Reject dropping below own ancestor.");
  9176. res = false;
  9177. }else{
  9178. r = dndOpts.dragEnter(node, data);
  9179. res = normalizeDragEnterResponse(r);
  9180. }
  9181. DRAG_ENTER_RESPONSE = res;
  9182. allowDrop = res && ( res.over || res.before || res.after );
  9183. break;
  9184. case "dragover":
  9185. // The dragover event is fired when an element or text
  9186. // selection is being dragged over a valid drop target
  9187. // (every few hundred milliseconds).
  9188. // console.log(event.type, "dropEffect: " + dataTransfer.dropEffect)
  9189. LAST_HIT_MODE = handleDragOver(event, data);
  9190. allowDrop = !!LAST_HIT_MODE;
  9191. break;
  9192. case "dragleave":
  9193. // NOTE: dragleave is fired AFTER the dragenter event of the
  9194. // FOLLOWING element.
  9195. if( !node ) {
  9196. tree.debug("Ignore non-node " + event.type + ": " + event.target.tagName + "." + event.target.className);
  9197. break;
  9198. }
  9199. if( !$(node.span).hasClass(classDropOver) ) {
  9200. node.debug("Ignore dragleave (multi)"); //, event.currentTarget);
  9201. break;
  9202. }
  9203. $(node.span).removeClass(classDropOver + " " + classDropAccept + " " + classDropReject);
  9204. node.scheduleAction("cancel");
  9205. dndOpts.dragLeave(node, data);
  9206. $dropMarker.hide();
  9207. break;
  9208. case "drop":
  9209. // Data is only readable in the (dragenter and) drop event:
  9210. if( $.inArray(nodeMimeType, dataTransfer.types) >= 0 ) {
  9211. nodeData = dataTransfer.getData(nodeMimeType);
  9212. tree.info(event.type + ": getData('application/x-fancytree-node'): '" + nodeData + "'");
  9213. }
  9214. if( !nodeData ) {
  9215. // 1. Source is not a Fancytree node, or
  9216. // 2. If the FT mime type was set, but returns '', this
  9217. // is probably IE 11 (which only supports 'text')
  9218. nodeData = dataTransfer.getData("text");
  9219. tree.info(event.type + ": getData('text'): '" + nodeData + "'");
  9220. }
  9221. if( nodeData ) {
  9222. try {
  9223. // 'text' type may contain JSON if IE is involved
  9224. // and setTextTypeJson option was set
  9225. json = JSON.parse(nodeData);
  9226. if( json.title !== undefined ) {
  9227. data.otherNodeData = json;
  9228. }
  9229. } catch(ex) {
  9230. // assume 'text' type contains plain text, so `otherNodeData`
  9231. // should not be set
  9232. }
  9233. }
  9234. tree.debug(event.type + ": nodeData: '" + nodeData + "', otherNodeData: ", data.otherNodeData);
  9235. $(node.span).removeClass(classDropOver + " " + classDropAccept + " " + classDropReject);
  9236. $dropMarker.hide();
  9237. data.hitMode = LAST_HIT_MODE;
  9238. data.dropEffect = dataTransfer.dropEffect;
  9239. data.isCancelled = data.dropEffect === "none";
  9240. // Let user implement the actual drop operation
  9241. dndOpts.dragDrop(node, data);
  9242. // Prevent browser's default drop handling
  9243. event.preventDefault();
  9244. _clearGlobals();
  9245. break;
  9246. }
  9247. // Dnd API madness: we must PREVENT default handling to enable dropping
  9248. if( allowDrop ) {
  9249. event.preventDefault();
  9250. return false;
  9251. }
  9252. });
  9253. }
  9254. }
  9255. });
  9256. // Value returned by `require('jquery.fancytree..')`
  9257. return $.ui.fancytree;
  9258. })); // End of closure
  9259. /*! Extension 'jquery.fancytree.edit.js' *//*!
  9260. * jquery.fancytree.edit.js
  9261. *
  9262. * Make node titles editable.
  9263. * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
  9264. *
  9265. * Copyright (c) 2008-2018, Martin Wendt (http://wwWendt.de)
  9266. *
  9267. * Released under the MIT license
  9268. * https://github.com/mar10/fancytree/wiki/LicenseInfo
  9269. *
  9270. * @version 2.28.1
  9271. * @date 2018-03-19T06:47:37Z
  9272. */
  9273. ;(function( factory ) {
  9274. if ( typeof define === "function" && define.amd ) {
  9275. // AMD. Register as an anonymous module.
  9276. define( [ "jquery", "./jquery.fancytree" ], factory );
  9277. } else if ( typeof module === "object" && module.exports ) {
  9278. // Node/CommonJS
  9279. require("./jquery.fancytree");
  9280. module.exports = factory(require("jquery"));
  9281. } else {
  9282. // Browser globals
  9283. factory( jQuery );
  9284. }
  9285. }( function( $ ) {
  9286. "use strict";
  9287. /*******************************************************************************
  9288. * Private functions and variables
  9289. */
  9290. var isMac = /Mac/.test(navigator.platform),
  9291. escapeHtml = $.ui.fancytree.escapeHtml,
  9292. unescapeHtml = $.ui.fancytree.unescapeHtml;
  9293. /**
  9294. * [ext-edit] Start inline editing of current node title.
  9295. *
  9296. * @alias FancytreeNode#editStart
  9297. * @requires Fancytree
  9298. */
  9299. $.ui.fancytree._FancytreeNodeClass.prototype.editStart = function(){
  9300. var $input,
  9301. node = this,
  9302. tree = this.tree,
  9303. local = tree.ext.edit,
  9304. instOpts = tree.options.edit,
  9305. $title = $(".fancytree-title", node.span),
  9306. eventData = {
  9307. node: node,
  9308. tree: tree,
  9309. options: tree.options,
  9310. isNew: $(node[tree.statusClassPropName]).hasClass("fancytree-edit-new"),
  9311. orgTitle: node.title,
  9312. input: null,
  9313. dirty: false
  9314. };
  9315. // beforeEdit may want to modify the title before editing
  9316. if( instOpts.beforeEdit.call(node, {type: "beforeEdit"}, eventData) === false ) {
  9317. return false;
  9318. }
  9319. $.ui.fancytree.assert(!local.currentNode, "recursive edit");
  9320. local.currentNode = this;
  9321. local.eventData = eventData;
  9322. // Disable standard Fancytree mouse- and key handling
  9323. tree.widget._unbind();
  9324. // #116: ext-dnd prevents the blur event, so we have to catch outer clicks
  9325. $(document).on("mousedown.fancytree-edit", function(event){
  9326. if( ! $(event.target).hasClass("fancytree-edit-input") ){
  9327. node.editEnd(true, event);
  9328. }
  9329. });
  9330. // Replace node with <input>
  9331. $input = $("<input />", {
  9332. "class": "fancytree-edit-input",
  9333. type: "text",
  9334. value: tree.options.escapeTitles ? eventData.orgTitle : unescapeHtml(eventData.orgTitle)
  9335. });
  9336. local.eventData.input = $input;
  9337. if ( instOpts.adjustWidthOfs != null ) {
  9338. $input.width($title.width() + instOpts.adjustWidthOfs);
  9339. }
  9340. if ( instOpts.inputCss != null ) {
  9341. $input.css(instOpts.inputCss);
  9342. }
  9343. $title.html($input);
  9344. // Focus <input> and bind keyboard handler
  9345. $input
  9346. .focus()
  9347. .change(function(event){
  9348. $input.addClass("fancytree-edit-dirty");
  9349. }).keydown(function(event){
  9350. switch( event.which ) {
  9351. case $.ui.keyCode.ESCAPE:
  9352. node.editEnd(false, event);
  9353. break;
  9354. case $.ui.keyCode.ENTER:
  9355. node.editEnd(true, event);
  9356. return false; // so we don't start editmode on Mac
  9357. }
  9358. event.stopPropagation();
  9359. }).blur(function(event){
  9360. return node.editEnd(true, event);
  9361. });
  9362. instOpts.edit.call(node, {type: "edit"}, eventData);
  9363. };
  9364. /**
  9365. * [ext-edit] Stop inline editing.
  9366. * @param {Boolean} [applyChanges=false] false: cancel edit, true: save (if modified)
  9367. * @alias FancytreeNode#editEnd
  9368. * @requires jquery.fancytree.edit.js
  9369. */
  9370. $.ui.fancytree._FancytreeNodeClass.prototype.editEnd = function(applyChanges, _event){
  9371. var newVal,
  9372. node = this,
  9373. tree = this.tree,
  9374. local = tree.ext.edit,
  9375. eventData = local.eventData,
  9376. instOpts = tree.options.edit,
  9377. $title = $(".fancytree-title", node.span),
  9378. $input = $title.find("input.fancytree-edit-input");
  9379. if( instOpts.trim ) {
  9380. $input.val($.trim($input.val()));
  9381. }
  9382. newVal = $input.val();
  9383. eventData.dirty = ( newVal !== node.title );
  9384. eventData.originalEvent = _event;
  9385. // Find out, if saving is required
  9386. if( applyChanges === false ) {
  9387. // If true/false was passed, honor this (except in rename mode, if unchanged)
  9388. eventData.save = false;
  9389. } else if( eventData.isNew ) {
  9390. // In create mode, we save everything, except for empty text
  9391. eventData.save = (newVal !== "");
  9392. } else {
  9393. // In rename mode, we save everyting, except for empty or unchanged text
  9394. eventData.save = eventData.dirty && (newVal !== "");
  9395. }
  9396. // Allow to break (keep editor open), modify input, or re-define data.save
  9397. if( instOpts.beforeClose.call(node, {type: "beforeClose"}, eventData) === false){
  9398. return false;
  9399. }
  9400. if( eventData.save && instOpts.save.call(node, {type: "save"}, eventData) === false){
  9401. return false;
  9402. }
  9403. $input
  9404. .removeClass("fancytree-edit-dirty")
  9405. .off();
  9406. // Unbind outer-click handler
  9407. $(document).off(".fancytree-edit");
  9408. if( eventData.save ) {
  9409. // # 171: escape user input (not required if global escaping is on)
  9410. node.setTitle( tree.options.escapeTitles ? newVal : escapeHtml(newVal) );
  9411. node.setFocus();
  9412. }else{
  9413. if( eventData.isNew ) {
  9414. node.remove();
  9415. node = eventData.node = null;
  9416. local.relatedNode.setFocus();
  9417. } else {
  9418. node.renderTitle();
  9419. node.setFocus();
  9420. }
  9421. }
  9422. local.eventData = null;
  9423. local.currentNode = null;
  9424. local.relatedNode = null;
  9425. // Re-enable mouse and keyboard handling
  9426. tree.widget._bind();
  9427. // Set keyboard focus, even if setFocus() claims 'nothing to do'
  9428. $(tree.$container).focus();
  9429. eventData.input = null;
  9430. instOpts.close.call(node, {type: "close"}, eventData);
  9431. return true;
  9432. };
  9433. /**
  9434. * [ext-edit] Create a new child or sibling node and start edit mode.
  9435. *
  9436. * @param {String} [mode='child'] 'before', 'after', or 'child'
  9437. * @param {Object} [init] NodeData (or simple title string)
  9438. * @alias FancytreeNode#editCreateNode
  9439. * @requires jquery.fancytree.edit.js
  9440. * @since 2.4
  9441. */
  9442. $.ui.fancytree._FancytreeNodeClass.prototype.editCreateNode = function(mode, init){
  9443. var newNode,
  9444. tree = this.tree,
  9445. self = this;
  9446. mode = mode || "child";
  9447. if( init == null ) {
  9448. init = { title: "" };
  9449. } else if( typeof init === "string" ) {
  9450. init = { title: init };
  9451. } else {
  9452. $.ui.fancytree.assert($.isPlainObject(init));
  9453. }
  9454. // Make sure node is expanded (and loaded) in 'child' mode
  9455. if( mode === "child" && !this.isExpanded() && this.hasChildren() !== false ) {
  9456. this.setExpanded().done(function(){
  9457. self.editCreateNode(mode, init);
  9458. });
  9459. return;
  9460. }
  9461. newNode = this.addNode(init, mode);
  9462. // #644: Don't filter new nodes.
  9463. newNode.match = true;
  9464. $(newNode[tree.statusClassPropName])
  9465. .removeClass("fancytree-hide")
  9466. .addClass("fancytree-match");
  9467. newNode.makeVisible(/*{noAnimation: true}*/).done(function(){
  9468. $(newNode[tree.statusClassPropName]).addClass("fancytree-edit-new");
  9469. self.tree.ext.edit.relatedNode = self;
  9470. newNode.editStart();
  9471. });
  9472. };
  9473. /**
  9474. * [ext-edit] Check if any node in this tree in edit mode.
  9475. *
  9476. * @returns {FancytreeNode | null}
  9477. * @alias Fancytree#isEditing
  9478. * @requires jquery.fancytree.edit.js
  9479. */
  9480. $.ui.fancytree._FancytreeClass.prototype.isEditing = function(){
  9481. return this.ext.edit ? this.ext.edit.currentNode : null;
  9482. };
  9483. /**
  9484. * [ext-edit] Check if this node is in edit mode.
  9485. * @returns {Boolean} true if node is currently beeing edited
  9486. * @alias FancytreeNode#isEditing
  9487. * @requires jquery.fancytree.edit.js
  9488. */
  9489. $.ui.fancytree._FancytreeNodeClass.prototype.isEditing = function(){
  9490. return this.tree.ext.edit ? this.tree.ext.edit.currentNode === this : false;
  9491. };
  9492. /*******************************************************************************
  9493. * Extension code
  9494. */
  9495. $.ui.fancytree.registerExtension({
  9496. name: "edit",
  9497. version: "2.28.1",
  9498. // Default options for this extension.
  9499. options: {
  9500. adjustWidthOfs: 4, // null: don't adjust input size to content
  9501. allowEmpty: false, // Prevent empty input
  9502. inputCss: {minWidth: "3em"},
  9503. // triggerCancel: ["esc", "tab", "click"],
  9504. triggerStart: ["f2", "mac+enter", "shift+click"],
  9505. trim: true, // Trim whitespace before save
  9506. // Events:
  9507. beforeClose: $.noop, // Return false to prevent cancel/save (data.input is available)
  9508. beforeEdit: $.noop, // Return false to prevent edit mode
  9509. close: $.noop, // Editor was removed
  9510. edit: $.noop, // Editor was opened (available as data.input)
  9511. // keypress: $.noop, // Not yet implemented
  9512. save: $.noop // Save data.input.val() or return false to keep editor open
  9513. },
  9514. // Local attributes
  9515. currentNode: null,
  9516. treeInit: function(ctx){
  9517. this._superApply(arguments);
  9518. this.$container.addClass("fancytree-ext-edit");
  9519. },
  9520. nodeClick: function(ctx) {
  9521. if( $.inArray("shift+click", ctx.options.edit.triggerStart) >= 0 ){
  9522. if( ctx.originalEvent.shiftKey ){
  9523. ctx.node.editStart();
  9524. return false;
  9525. }
  9526. }
  9527. if( $.inArray("clickActive", ctx.options.edit.triggerStart) >= 0 ){
  9528. // Only when click was inside title text (not aynwhere else in the row)
  9529. if( ctx.node.isActive() && !ctx.node.isEditing() &&
  9530. $(ctx.originalEvent.target).hasClass("fancytree-title")
  9531. ){
  9532. ctx.node.editStart();
  9533. return false;
  9534. }
  9535. }
  9536. return this._superApply(arguments);
  9537. },
  9538. nodeDblclick: function(ctx) {
  9539. if( $.inArray("dblclick", ctx.options.edit.triggerStart) >= 0 ){
  9540. ctx.node.editStart();
  9541. return false;
  9542. }
  9543. return this._superApply(arguments);
  9544. },
  9545. nodeKeydown: function(ctx) {
  9546. switch( ctx.originalEvent.which ) {
  9547. case 113: // [F2]
  9548. if( $.inArray("f2", ctx.options.edit.triggerStart) >= 0 ){
  9549. ctx.node.editStart();
  9550. return false;
  9551. }
  9552. break;
  9553. case $.ui.keyCode.ENTER:
  9554. if( $.inArray("mac+enter", ctx.options.edit.triggerStart) >= 0 && isMac ){
  9555. ctx.node.editStart();
  9556. return false;
  9557. }
  9558. break;
  9559. }
  9560. return this._superApply(arguments);
  9561. }
  9562. });
  9563. // Value returned by `require('jquery.fancytree..')`
  9564. return $.ui.fancytree;
  9565. })); // End of closure
  9566. /*! Extension 'jquery.fancytree.filter.js' *//*!
  9567. * jquery.fancytree.filter.js
  9568. *
  9569. * Remove or highlight tree nodes, based on a filter.
  9570. * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
  9571. *
  9572. * Copyright (c) 2008-2018, Martin Wendt (http://wwWendt.de)
  9573. *
  9574. * Released under the MIT license
  9575. * https://github.com/mar10/fancytree/wiki/LicenseInfo
  9576. *
  9577. * @version 2.28.1
  9578. * @date 2018-03-19T06:47:37Z
  9579. */
  9580. ;(function( factory ) {
  9581. if ( typeof define === "function" && define.amd ) {
  9582. // AMD. Register as an anonymous module.
  9583. define( [ "jquery", "./jquery.fancytree" ], factory );
  9584. } else if ( typeof module === "object" && module.exports ) {
  9585. // Node/CommonJS
  9586. require("./jquery.fancytree");
  9587. module.exports = factory(require("jquery"));
  9588. } else {
  9589. // Browser globals
  9590. factory( jQuery );
  9591. }
  9592. }( function( $ ) {
  9593. "use strict";
  9594. /*******************************************************************************
  9595. * Private functions and variables
  9596. */
  9597. var KeyNoData = "__not_found__",
  9598. escapeHtml = $.ui.fancytree.escapeHtml;
  9599. function _escapeRegex(str){
  9600. /*jshint regexdash:true */
  9601. return (str + "").replace(/([.?*+\^\$\[\]\\(){}|-])/g, "\\$1");
  9602. }
  9603. function extractHtmlText(s){
  9604. if( s.indexOf(">") >= 0 ) {
  9605. return $("<div/>").html(s).text();
  9606. }
  9607. return s;
  9608. }
  9609. $.ui.fancytree._FancytreeClass.prototype._applyFilterImpl = function(filter, branchMode, _opts){
  9610. var match, statusNode, re, reHighlight, temp,
  9611. count = 0,
  9612. treeOpts = this.options,
  9613. escapeTitles = treeOpts.escapeTitles,
  9614. prevAutoCollapse = treeOpts.autoCollapse,
  9615. opts = $.extend({}, treeOpts.filter, _opts),
  9616. hideMode = opts.mode === "hide",
  9617. leavesOnly = !!opts.leavesOnly && !branchMode;
  9618. // Default to 'match title substring (not case sensitive)'
  9619. if(typeof filter === "string"){
  9620. if( filter === "" ) {
  9621. this.warn("Fancytree passing an empty string as a filter is handled as clearFilter().");
  9622. this.clearFilter();
  9623. return;
  9624. }
  9625. if( opts.fuzzy ) {
  9626. // See https://codereview.stackexchange.com/questions/23899/faster-javascript-fuzzy-string-matching-function/23905#23905
  9627. // and http://www.quora.com/How-is-the-fuzzy-search-algorithm-in-Sublime-Text-designed
  9628. // and http://www.dustindiaz.com/autocomplete-fuzzy-matching
  9629. match = filter.split("").reduce(function(a, b) {
  9630. return a + "[^" + b + "]*" + b;
  9631. });
  9632. } else {
  9633. match = _escapeRegex(filter); // make sure a '.' is treated literally
  9634. }
  9635. re = new RegExp(".*" + match + ".*", "i");
  9636. reHighlight = new RegExp(_escapeRegex(filter), "gi");
  9637. filter = function(node){
  9638. if( !node.title ) {
  9639. return false;
  9640. }
  9641. var text = escapeTitles ? node.title : extractHtmlText(node.title),
  9642. res = !!re.test(text);
  9643. if( res && opts.highlight ) {
  9644. if( escapeTitles ) {
  9645. // #740: we must not apply the marks to escaped entity names, e.g. `&quot;`
  9646. // Use some exotic characters to mark matches:
  9647. temp = text.replace(reHighlight, function(s){
  9648. return "\uFFF7" + s + "\uFFF8";
  9649. });
  9650. // now we can escape the title...
  9651. node.titleWithHighlight = escapeHtml(temp)
  9652. // ... and finally insert the desired `<mark>` tags
  9653. .replace(/\uFFF7/g, "<mark>")
  9654. .replace(/\uFFF8/g, "</mark>");
  9655. } else {
  9656. node.titleWithHighlight = text.replace(reHighlight, function(s){
  9657. return "<mark>" + s + "</mark>";
  9658. });
  9659. }
  9660. // node.debug("filter", escapeTitles, text, node.titleWithHighlight);
  9661. }
  9662. return res;
  9663. };
  9664. }
  9665. this.enableFilter = true;
  9666. this.lastFilterArgs = arguments;
  9667. this.$div.addClass("fancytree-ext-filter");
  9668. if( hideMode ){
  9669. this.$div.addClass("fancytree-ext-filter-hide");
  9670. } else {
  9671. this.$div.addClass("fancytree-ext-filter-dimm");
  9672. }
  9673. this.$div.toggleClass("fancytree-ext-filter-hide-expanders", !!opts.hideExpanders);
  9674. // Reset current filter
  9675. this.visit(function(node){
  9676. delete node.match;
  9677. delete node.titleWithHighlight;
  9678. node.subMatchCount = 0;
  9679. });
  9680. statusNode = this.getRootNode()._findDirectChild(KeyNoData);
  9681. if( statusNode ) {
  9682. statusNode.remove();
  9683. }
  9684. // Adjust node.hide, .match, and .subMatchCount properties
  9685. treeOpts.autoCollapse = false; // #528
  9686. this.visit(function(node){
  9687. if ( leavesOnly && node.children != null ) {
  9688. return;
  9689. }
  9690. var res = filter(node),
  9691. matchedByBranch = false;
  9692. if( res === "skip" ) {
  9693. node.visit(function(c){
  9694. c.match = false;
  9695. }, true);
  9696. return "skip";
  9697. }
  9698. if( !res && (branchMode || res === "branch") && node.parent.match ) {
  9699. res = true;
  9700. matchedByBranch = true;
  9701. }
  9702. if( res ) {
  9703. count++;
  9704. node.match = true;
  9705. node.visitParents(function(p){
  9706. p.subMatchCount += 1;
  9707. // Expand match (unless this is no real match, but only a node in a matched branch)
  9708. if( opts.autoExpand && !matchedByBranch && !p.expanded ) {
  9709. p.setExpanded(true, {noAnimation: true, noEvents: true, scrollIntoView: false});
  9710. p._filterAutoExpanded = true;
  9711. }
  9712. });
  9713. }
  9714. });
  9715. treeOpts.autoCollapse = prevAutoCollapse;
  9716. if( count === 0 && opts.nodata && hideMode ) {
  9717. statusNode = opts.nodata;
  9718. if( $.isFunction(statusNode) ) {
  9719. statusNode = statusNode();
  9720. }
  9721. if( statusNode === true ) {
  9722. statusNode = {};
  9723. } else if( typeof statusNode === "string" ) {
  9724. statusNode = { title: statusNode };
  9725. }
  9726. statusNode = $.extend({
  9727. statusNodeType: "nodata",
  9728. key: KeyNoData,
  9729. title: this.options.strings.noData
  9730. }, statusNode);
  9731. this.getRootNode().addNode(statusNode).match = true;
  9732. }
  9733. // Redraw whole tree
  9734. this.render();
  9735. return count;
  9736. };
  9737. /**
  9738. * [ext-filter] Dimm or hide nodes.
  9739. *
  9740. * @param {function | string} filter
  9741. * @param {boolean} [opts={autoExpand: false, leavesOnly: false}]
  9742. * @returns {integer} count
  9743. * @alias Fancytree#filterNodes
  9744. * @requires jquery.fancytree.filter.js
  9745. */
  9746. $.ui.fancytree._FancytreeClass.prototype.filterNodes = function(filter, opts) {
  9747. if( typeof opts === "boolean" ) {
  9748. opts = { leavesOnly: opts };
  9749. this.warn("Fancytree.filterNodes() leavesOnly option is deprecated since 2.9.0 / 2015-04-19. Use opts.leavesOnly instead.");
  9750. }
  9751. return this._applyFilterImpl(filter, false, opts);
  9752. };
  9753. /**
  9754. * @deprecated
  9755. */
  9756. $.ui.fancytree._FancytreeClass.prototype.applyFilter = function(filter){
  9757. this.warn("Fancytree.applyFilter() is deprecated since 2.1.0 / 2014-05-29. Use .filterNodes() instead.");
  9758. return this.filterNodes.apply(this, arguments);
  9759. };
  9760. /**
  9761. * [ext-filter] Dimm or hide whole branches.
  9762. *
  9763. * @param {function | string} filter
  9764. * @param {boolean} [opts={autoExpand: false}]
  9765. * @returns {integer} count
  9766. * @alias Fancytree#filterBranches
  9767. * @requires jquery.fancytree.filter.js
  9768. */
  9769. $.ui.fancytree._FancytreeClass.prototype.filterBranches = function(filter, opts){
  9770. return this._applyFilterImpl(filter, true, opts);
  9771. };
  9772. /**
  9773. * [ext-filter] Reset the filter.
  9774. *
  9775. * @alias Fancytree#clearFilter
  9776. * @requires jquery.fancytree.filter.js
  9777. */
  9778. $.ui.fancytree._FancytreeClass.prototype.clearFilter = function(){
  9779. var $title,
  9780. statusNode = this.getRootNode()._findDirectChild(KeyNoData),
  9781. escapeTitles = this.options.escapeTitles,
  9782. enhanceTitle = this.options.enhanceTitle;
  9783. if( statusNode ) {
  9784. statusNode.remove();
  9785. }
  9786. this.visit(function(node){
  9787. if( node.match && node.span ) { // #491, #601
  9788. $title = $(node.span).find(">span.fancytree-title");
  9789. if( escapeTitles ) {
  9790. $title.text(node.title);
  9791. } else {
  9792. $title.html(node.title);
  9793. }
  9794. if( enhanceTitle ) {
  9795. enhanceTitle({type: "enhanceTitle"}, {node: node, $title: $title});
  9796. }
  9797. }
  9798. delete node.match;
  9799. delete node.subMatchCount;
  9800. delete node.titleWithHighlight;
  9801. if ( node.$subMatchBadge ) {
  9802. node.$subMatchBadge.remove();
  9803. delete node.$subMatchBadge;
  9804. }
  9805. if( node._filterAutoExpanded && node.expanded ) {
  9806. node.setExpanded(false, {noAnimation: true, noEvents: true, scrollIntoView: false});
  9807. }
  9808. delete node._filterAutoExpanded;
  9809. });
  9810. this.enableFilter = false;
  9811. this.lastFilterArgs = null;
  9812. this.$div.removeClass("fancytree-ext-filter fancytree-ext-filter-dimm fancytree-ext-filter-hide");
  9813. this.render();
  9814. };
  9815. /**
  9816. * [ext-filter] Return true if a filter is currently applied.
  9817. *
  9818. * @returns {Boolean}
  9819. * @alias Fancytree#isFilterActive
  9820. * @requires jquery.fancytree.filter.js
  9821. * @since 2.13
  9822. */
  9823. $.ui.fancytree._FancytreeClass.prototype.isFilterActive = function(){
  9824. return !!this.enableFilter;
  9825. };
  9826. /**
  9827. * [ext-filter] Return true if this node is matched by current filter (or no filter is active).
  9828. *
  9829. * @returns {Boolean}
  9830. * @alias FancytreeNode#isMatched
  9831. * @requires jquery.fancytree.filter.js
  9832. * @since 2.13
  9833. */
  9834. $.ui.fancytree._FancytreeNodeClass.prototype.isMatched = function(){
  9835. return !(this.tree.enableFilter && !this.match);
  9836. };
  9837. /*******************************************************************************
  9838. * Extension code
  9839. */
  9840. $.ui.fancytree.registerExtension({
  9841. name: "filter",
  9842. version: "2.28.1",
  9843. // Default options for this extension.
  9844. options: {
  9845. autoApply: true, // Re-apply last filter if lazy data is loaded
  9846. autoExpand: false, // Expand all branches that contain matches while filtered
  9847. counter: true, // Show a badge with number of matching child nodes near parent icons
  9848. fuzzy: false, // Match single characters in order, e.g. 'fb' will match 'FooBar'
  9849. hideExpandedCounter: true, // Hide counter badge if parent is expanded
  9850. hideExpanders: false, // Hide expanders if all child nodes are hidden by filter
  9851. highlight: true, // Highlight matches by wrapping inside <mark> tags
  9852. leavesOnly: false, // Match end nodes only
  9853. nodata: true, // Display a 'no data' status node if result is empty
  9854. mode: "dimm" // Grayout unmatched nodes (pass "hide" to remove unmatched node instead)
  9855. },
  9856. nodeLoadChildren: function(ctx, source) {
  9857. return this._superApply(arguments).done(function() {
  9858. if( ctx.tree.enableFilter && ctx.tree.lastFilterArgs && ctx.options.filter.autoApply ) {
  9859. ctx.tree._applyFilterImpl.apply(ctx.tree, ctx.tree.lastFilterArgs);
  9860. }
  9861. });
  9862. },
  9863. nodeSetExpanded: function(ctx, flag, callOpts) {
  9864. delete ctx.node._filterAutoExpanded;
  9865. // Make sure counter badge is displayed again, when node is beeing collapsed
  9866. if( !flag && ctx.options.filter.hideExpandedCounter && ctx.node.$subMatchBadge ) {
  9867. ctx.node.$subMatchBadge.show();
  9868. }
  9869. return this._superApply(arguments);
  9870. },
  9871. nodeRenderStatus: function(ctx) {
  9872. // Set classes for current status
  9873. var res,
  9874. node = ctx.node,
  9875. tree = ctx.tree,
  9876. opts = ctx.options.filter,
  9877. $title = $(node.span).find("span.fancytree-title"),
  9878. $span = $(node[tree.statusClassPropName]),
  9879. enhanceTitle = ctx.options.enhanceTitle,
  9880. escapeTitles = ctx.options.escapeTitles;
  9881. res = this._super(ctx);
  9882. // nothing to do, if node was not yet rendered
  9883. if( !$span.length || !tree.enableFilter ) {
  9884. return res;
  9885. }
  9886. $span
  9887. .toggleClass("fancytree-match", !!node.match)
  9888. .toggleClass("fancytree-submatch", !!node.subMatchCount)
  9889. .toggleClass("fancytree-hide", !(node.match || node.subMatchCount));
  9890. // Add/update counter badge
  9891. if( opts.counter && node.subMatchCount && (!node.isExpanded() || !opts.hideExpandedCounter) ) {
  9892. if( !node.$subMatchBadge ) {
  9893. node.$subMatchBadge = $("<span class='fancytree-childcounter'/>");
  9894. $("span.fancytree-icon, span.fancytree-custom-icon", node.span).append(node.$subMatchBadge);
  9895. }
  9896. node.$subMatchBadge.show().text(node.subMatchCount);
  9897. } else if ( node.$subMatchBadge ) {
  9898. node.$subMatchBadge.hide();
  9899. }
  9900. // node.debug("nodeRenderStatus", node.titleWithHighlight, node.title)
  9901. // #601: also chek for $title.length, because we don't need to render
  9902. // if node.span is null (i.e. not rendered)
  9903. if( node.span && (!node.isEditing || !node.isEditing.call(node)) ) {
  9904. if( node.titleWithHighlight ) {
  9905. $title.html(node.titleWithHighlight);
  9906. } else if ( escapeTitles ) {
  9907. $title.text(node.title);
  9908. } else {
  9909. $title.html(node.title);
  9910. }
  9911. if( enhanceTitle ) {
  9912. enhanceTitle({type: "enhanceTitle"}, {node: node, $title: $title});
  9913. }
  9914. }
  9915. return res;
  9916. }
  9917. });
  9918. // Value returned by `require('jquery.fancytree..')`
  9919. return $.ui.fancytree;
  9920. })); // End of closure
  9921. /*! Extension 'jquery.fancytree.glyph.js' *//*!
  9922. * jquery.fancytree.glyph.js
  9923. *
  9924. * Use glyph-fonts, ligature-fonts, or SVG icons instead of icon sprites.
  9925. * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
  9926. *
  9927. * Copyright (c) 2008-2018, Martin Wendt (http://wwWendt.de)
  9928. *
  9929. * Released under the MIT license
  9930. * https://github.com/mar10/fancytree/wiki/LicenseInfo
  9931. *
  9932. * @version 2.28.1
  9933. * @date 2018-03-19T06:47:37Z
  9934. */
  9935. ;(function( factory ) {
  9936. if ( typeof define === "function" && define.amd ) {
  9937. // AMD. Register as an anonymous module.
  9938. define( [ "jquery", "./jquery.fancytree" ], factory );
  9939. } else if ( typeof module === "object" && module.exports ) {
  9940. // Node/CommonJS
  9941. require("./jquery.fancytree");
  9942. module.exports = factory(require("jquery"));
  9943. } else {
  9944. // Browser globals
  9945. factory( jQuery );
  9946. }
  9947. }( function( $ ) {
  9948. "use strict";
  9949. /* *****************************************************************************
  9950. * Private functions and variables
  9951. */
  9952. var FT = $.ui.fancytree,
  9953. PRESETS = {
  9954. "awesome3": { // Outdated!
  9955. _addClass: "",
  9956. checkbox: "icon-check-empty",
  9957. checkboxSelected: "icon-check",
  9958. checkboxUnknown: "icon-check icon-muted",
  9959. dragHelper: "icon-caret-right",
  9960. dropMarker: "icon-caret-right",
  9961. error: "icon-exclamation-sign",
  9962. expanderClosed: "icon-caret-right",
  9963. expanderLazy: "icon-angle-right",
  9964. expanderOpen: "icon-caret-down",
  9965. loading: "icon-refresh icon-spin",
  9966. nodata: "icon-meh",
  9967. noExpander: "",
  9968. radio: "icon-circle-blank",
  9969. radioSelected: "icon-circle",
  9970. // radioUnknown: "icon-circle icon-muted",
  9971. // Default node icons.
  9972. // (Use tree.options.icon callback to define custom icons based on node data)
  9973. doc: "icon-file-alt",
  9974. docOpen: "icon-file-alt",
  9975. folder: "icon-folder-close-alt",
  9976. folderOpen: "icon-folder-open-alt"
  9977. },
  9978. "awesome4": {
  9979. _addClass: "fa",
  9980. checkbox: "fa-square-o",
  9981. checkboxSelected: "fa-check-square-o",
  9982. checkboxUnknown: "fa-square fancytree-helper-indeterminate-cb",
  9983. dragHelper: "fa-arrow-right",
  9984. dropMarker: "fa-long-arrow-right",
  9985. error: "fa-warning",
  9986. expanderClosed: "fa-caret-right",
  9987. expanderLazy: "fa-angle-right",
  9988. expanderOpen: "fa-caret-down",
  9989. // We may prevent wobbling rotations on FF by creating a separate sub element:
  9990. loading: {html: "<span class='fa fa-spinner fa-pulse' />"},
  9991. nodata: "fa-meh-o",
  9992. noExpander: "",
  9993. radio: "fa-circle-thin", // "fa-circle-o"
  9994. radioSelected: "fa-circle",
  9995. // radioUnknown: "fa-dot-circle-o",
  9996. // Default node icons.
  9997. // (Use tree.options.icon callback to define custom icons based on node data)
  9998. doc: "fa-file-o",
  9999. docOpen: "fa-file-o",
  10000. folder: "fa-folder-o",
  10001. folderOpen: "fa-folder-open-o"
  10002. },
  10003. "awesome5": {
  10004. // fontawesome 5 have several different base classes
  10005. // "far, fas, fal and fab" The rendered svg puts that prefix
  10006. // in a different location so we have to keep them separate here
  10007. _addClass: "",
  10008. checkbox: "far fa-square",
  10009. checkboxSelected: "far fa-check-square",
  10010. // checkboxUnknown: "far fa-window-close",
  10011. checkboxUnknown: "fas fa-square fancytree-helper-indeterminate-cb",
  10012. radio: "far fa-circle",
  10013. radioSelected: "fas fa-circle",
  10014. radioUnknown: "far fa-dot-circle",
  10015. dragHelper: "fas fa-arrow-right",
  10016. dropMarker: "fas fa-long-arrow-right",
  10017. error: "fas fa-exclamation-triangle",
  10018. expanderClosed: "fas fa-caret-right",
  10019. expanderLazy: "fas fa-angle-right",
  10020. expanderOpen: "fas fa-caret-down",
  10021. loading: "fas fa-spinner fa-pulse",
  10022. nodata: "far fa-meh",
  10023. noExpander: "",
  10024. // Default node icons.
  10025. // (Use tree.options.icon callback to define custom icons based on node data)
  10026. doc: "far fa-file",
  10027. docOpen: "far fa-file",
  10028. folder: "far fa-folder",
  10029. folderOpen: "far fa-folder-open"
  10030. },
  10031. "bootstrap3": {
  10032. _addClass: "glyphicon",
  10033. checkbox: "glyphicon-unchecked",
  10034. checkboxSelected: "glyphicon-check",
  10035. checkboxUnknown: "glyphicon-expand fancytree-helper-indeterminate-cb", // "glyphicon-share",
  10036. dragHelper: "glyphicon-play",
  10037. dropMarker: "glyphicon-arrow-right",
  10038. error: "glyphicon-warning-sign",
  10039. expanderClosed: "glyphicon-menu-right", // glyphicon-plus-sign
  10040. expanderLazy: "glyphicon-menu-right", // glyphicon-plus-sign
  10041. expanderOpen: "glyphicon-menu-down", // glyphicon-minus-sign
  10042. loading: "glyphicon-refresh fancytree-helper-spin",
  10043. nodata: "glyphicon-info-sign",
  10044. noExpander: "",
  10045. radio: "glyphicon-remove-circle", // "glyphicon-unchecked",
  10046. radioSelected: "glyphicon-ok-circle", // "glyphicon-check",
  10047. // radioUnknown: "glyphicon-ban-circle",
  10048. // Default node icons.
  10049. // (Use tree.options.icon callback to define custom icons based on node data)
  10050. doc: "glyphicon-file",
  10051. docOpen: "glyphicon-file",
  10052. folder: "glyphicon-folder-close",
  10053. folderOpen: "glyphicon-folder-open"
  10054. },
  10055. "material": {
  10056. _addClass: "material-icons",
  10057. checkbox: { text: "check_box_outline_blank" },
  10058. checkboxSelected: { text: "check_box" },
  10059. checkboxUnknown: { text: "indeterminate_check_box" },
  10060. dragHelper: { text: "play_arrow" },
  10061. dropMarker: { text: "arrow-forward" },
  10062. error: { text: "warning" },
  10063. expanderClosed: { text: "chevron_right" },
  10064. expanderLazy: { text: "last_page" },
  10065. expanderOpen: { text: "expand_more" },
  10066. loading: { text: "autorenew", addClass: "fancytree-helper-spin" },
  10067. nodata: { text: "info" },
  10068. noExpander: { text: "" },
  10069. radio: { text: "radio_button_unchecked" },
  10070. radioSelected: { text: "radio_button_checked" },
  10071. // Default node icons.
  10072. // (Use tree.options.icon callback to define custom icons based on node data)
  10073. doc: { text: "insert_drive_file" },
  10074. docOpen: { text: "insert_drive_file" },
  10075. folder: { text: "folder" },
  10076. folderOpen: { text: "folder_open" }
  10077. }
  10078. };
  10079. function setIcon( span, baseClass, opts, type ) {
  10080. var map = opts.map,
  10081. icon = map[ type ],
  10082. $span = $( span ),
  10083. setClass = baseClass + " " + (map._addClass || "");
  10084. if( typeof icon === "string" ) {
  10085. $span.attr( "class", setClass + " " + icon );
  10086. } else if ( icon ) {
  10087. if( icon.text ) {
  10088. // $span.text( "" + icon.text );
  10089. span.textContent = "" + icon.text;
  10090. } else if ( icon.html ) {
  10091. // $(span).append($(icon.html));
  10092. span.innerHTML = icon.html;
  10093. }
  10094. $span.attr( "class", setClass + " " + ( icon.addClass || "" ) );
  10095. }
  10096. }
  10097. $.ui.fancytree.registerExtension({
  10098. name: "glyph",
  10099. version: "2.28.1",
  10100. // Default options for this extension.
  10101. options: {
  10102. preset: null, // 'awesome3', 'awesome4', 'bootstrap3', 'material'
  10103. map: {
  10104. }
  10105. },
  10106. treeInit: function(ctx){
  10107. var tree = ctx.tree,
  10108. opts = ctx.options.glyph;
  10109. if( opts.preset ) {
  10110. FT.assert( !!PRESETS[opts.preset],
  10111. "Invalid value for `options.glyph.preset`: " + opts.preset);
  10112. opts.map = $.extend({}, PRESETS[opts.preset], opts.map);
  10113. } else {
  10114. tree.warn("ext-glyph: missing `preset` option.");
  10115. }
  10116. this._superApply(arguments);
  10117. tree.$container.addClass("fancytree-ext-glyph");
  10118. },
  10119. nodeRenderStatus: function(ctx) {
  10120. var checkbox, icon, res, span,
  10121. node = ctx.node,
  10122. $span = $( node.span ),
  10123. opts = ctx.options.glyph;
  10124. res = this._super(ctx);
  10125. if( node.isRoot() ){
  10126. return res;
  10127. }
  10128. span = $span.children("span.fancytree-expander").get(0);
  10129. if( span ){
  10130. // if( node.isLoading() ){
  10131. // icon = "loading";
  10132. if( node.expanded && node.hasChildren() ){
  10133. icon = "expanderOpen";
  10134. }else if( node.isUndefined() ){
  10135. icon = "expanderLazy";
  10136. }else if( node.hasChildren() ){
  10137. icon = "expanderClosed";
  10138. }else{
  10139. icon = "noExpander";
  10140. }
  10141. // span.className = "fancytree-expander " + map[icon];
  10142. setIcon( span, "fancytree-expander", opts, icon );
  10143. }
  10144. if( node.tr ){
  10145. span = $("td", node.tr).find("span.fancytree-checkbox").get(0);
  10146. }else{
  10147. span = $span.children("span.fancytree-checkbox").get(0);
  10148. }
  10149. if( span ) {
  10150. checkbox = FT.evalOption("checkbox", node, node, opts, false);
  10151. if( (node.parent && node.parent.radiogroup ) || checkbox === "radio" ) {
  10152. icon = node.selected ? "radioSelected" : "radio";
  10153. setIcon( span, "fancytree-checkbox fancytree-radio", opts, icon );
  10154. } else {
  10155. icon = node.selected ? "checkboxSelected" : (node.partsel ? "checkboxUnknown" : "checkbox");
  10156. // span.className = "fancytree-checkbox " + map[icon];
  10157. setIcon( span, "fancytree-checkbox", opts, icon );
  10158. }
  10159. }
  10160. // Standard icon (note that this does not match .fancytree-custom-icon,
  10161. // that might be set by opts.icon callbacks)
  10162. span = $span.children("span.fancytree-icon").get(0);
  10163. if( span ){
  10164. if( node.statusNodeType ){
  10165. icon = node.statusNodeType; // loading, error
  10166. }else if( node.folder ){
  10167. icon = ( node.expanded && node.hasChildren() ) ? "folderOpen" : "folder";
  10168. }else{
  10169. icon = node.expanded ? "docOpen" : "doc";
  10170. }
  10171. setIcon( span, "fancytree-icon", opts, icon );
  10172. }
  10173. return res;
  10174. },
  10175. nodeSetStatus: function(ctx, status, message, details) {
  10176. var res, span,
  10177. opts = ctx.options.glyph,
  10178. node = ctx.node;
  10179. res = this._superApply(arguments);
  10180. if( status === "error" || status === "loading" || status === "nodata" ){
  10181. if(node.parent){
  10182. span = $("span.fancytree-expander", node.span).get(0);
  10183. if( span ) {
  10184. setIcon( span, "fancytree-expander", opts, status );
  10185. }
  10186. }else{ //
  10187. span = $(".fancytree-statusnode-" + status, node[this.nodeContainerAttrName])
  10188. .find("span.fancytree-icon").get(0);
  10189. if( span ) {
  10190. setIcon( span, "fancytree-icon", opts, status );
  10191. }
  10192. }
  10193. }
  10194. return res;
  10195. }
  10196. });
  10197. // Value returned by `require('jquery.fancytree..')`
  10198. return $.ui.fancytree;
  10199. })); // End of closure
  10200. /*! Extension 'jquery.fancytree.gridnav.js' *//*!
  10201. * jquery.fancytree.gridnav.js
  10202. *
  10203. * Support keyboard navigation for trees with embedded input controls.
  10204. * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
  10205. *
  10206. * Copyright (c) 2008-2018, Martin Wendt (http://wwWendt.de)
  10207. *
  10208. * Released under the MIT license
  10209. * https://github.com/mar10/fancytree/wiki/LicenseInfo
  10210. *
  10211. * @version 2.28.1
  10212. * @date 2018-03-19T06:47:37Z
  10213. */
  10214. ;(function( factory ) {
  10215. if ( typeof define === "function" && define.amd ) {
  10216. // AMD. Register as an anonymous module.
  10217. define([
  10218. "jquery",
  10219. "./jquery.fancytree",
  10220. "./jquery.fancytree.table"
  10221. ], factory );
  10222. } else if ( typeof module === "object" && module.exports ) {
  10223. // Node/CommonJS
  10224. require("./jquery.fancytree.table"); // core + table
  10225. module.exports = factory(require("jquery"));
  10226. } else {
  10227. // Browser globals
  10228. factory( jQuery );
  10229. }
  10230. }( function( $ ) {
  10231. "use strict";
  10232. /*******************************************************************************
  10233. * Private functions and variables
  10234. */
  10235. // Allow these navigation keys even when input controls are focused
  10236. var KC = $.ui.keyCode,
  10237. // which keys are *not* handled by embedded control, but passed to tree
  10238. // navigation handler:
  10239. NAV_KEYS = {
  10240. "text": [KC.UP, KC.DOWN],
  10241. "checkbox": [KC.UP, KC.DOWN, KC.LEFT, KC.RIGHT],
  10242. "link": [KC.UP, KC.DOWN, KC.LEFT, KC.RIGHT],
  10243. "radiobutton": [KC.UP, KC.DOWN, KC.LEFT, KC.RIGHT],
  10244. "select-one": [KC.LEFT, KC.RIGHT],
  10245. "select-multiple": [KC.LEFT, KC.RIGHT]
  10246. };
  10247. /* Calculate TD column index (considering colspans).*/
  10248. function getColIdx($tr, $td) {
  10249. var colspan,
  10250. td = $td.get(0),
  10251. idx = 0;
  10252. $tr.children().each(function () {
  10253. if( this === td ) {
  10254. return false;
  10255. }
  10256. colspan = $(this).prop("colspan");
  10257. idx += colspan ? colspan : 1;
  10258. });
  10259. return idx;
  10260. }
  10261. /* Find TD at given column index (considering colspans).*/
  10262. function findTdAtColIdx($tr, colIdx) {
  10263. var colspan,
  10264. res = null,
  10265. idx = 0;
  10266. $tr.children().each(function () {
  10267. if( idx >= colIdx ) {
  10268. res = $(this);
  10269. return false;
  10270. }
  10271. colspan = $(this).prop("colspan");
  10272. idx += colspan ? colspan : 1;
  10273. });
  10274. return res;
  10275. }
  10276. /* Find adjacent cell for a given direction. Skip empty cells and consider merged cells */
  10277. function findNeighbourTd($target, keyCode){
  10278. var $tr, colIdx,
  10279. $td = $target.closest("td"),
  10280. $tdNext = null;
  10281. switch( keyCode ){
  10282. case KC.LEFT:
  10283. $tdNext = $td.prev();
  10284. break;
  10285. case KC.RIGHT:
  10286. $tdNext = $td.next();
  10287. break;
  10288. case KC.UP:
  10289. case KC.DOWN:
  10290. $tr = $td.parent();
  10291. colIdx = getColIdx($tr, $td);
  10292. while( true ) {
  10293. $tr = (keyCode === KC.UP) ? $tr.prev() : $tr.next();
  10294. if( !$tr.length ) {
  10295. break;
  10296. }
  10297. // Skip hidden rows
  10298. if( $tr.is(":hidden") ) {
  10299. continue;
  10300. }
  10301. // Find adjacent cell in the same column
  10302. $tdNext = findTdAtColIdx($tr, colIdx);
  10303. // Skip cells that don't conatain a focusable element
  10304. if( $tdNext && $tdNext.find(":input,a").length ) {
  10305. break;
  10306. }
  10307. }
  10308. break;
  10309. }
  10310. return $tdNext;
  10311. }
  10312. /*******************************************************************************
  10313. * Extension code
  10314. */
  10315. $.ui.fancytree.registerExtension({
  10316. name: "gridnav",
  10317. version: "2.28.1",
  10318. // Default options for this extension.
  10319. options: {
  10320. autofocusInput: false, // Focus first embedded input if node gets activated
  10321. handleCursorKeys: true // Allow UP/DOWN in inputs to move to prev/next node
  10322. },
  10323. treeInit: function(ctx){
  10324. // gridnav requires the table extension to be loaded before itself
  10325. this._requireExtension("table", true, true);
  10326. this._superApply(arguments);
  10327. this.$container.addClass("fancytree-ext-gridnav");
  10328. // Activate node if embedded input gets focus (due to a click)
  10329. this.$container.on("focusin", function(event){
  10330. var ctx2,
  10331. node = $.ui.fancytree.getNode(event.target);
  10332. if( node && !node.isActive() ){
  10333. // Call node.setActive(), but also pass the event
  10334. ctx2 = ctx.tree._makeHookContext(node, event);
  10335. ctx.tree._callHook("nodeSetActive", ctx2, true);
  10336. }
  10337. });
  10338. },
  10339. nodeSetActive: function(ctx, flag, callOpts) {
  10340. var $outer,
  10341. opts = ctx.options.gridnav,
  10342. node = ctx.node,
  10343. event = ctx.originalEvent || {},
  10344. triggeredByInput = $(event.target).is(":input");
  10345. flag = (flag !== false);
  10346. this._superApply(arguments);
  10347. if( flag ){
  10348. if( ctx.options.titlesTabbable ){
  10349. if( !triggeredByInput ) {
  10350. $(node.span).find("span.fancytree-title").focus();
  10351. node.setFocus();
  10352. }
  10353. // If one node is tabbable, the container no longer needs to be
  10354. ctx.tree.$container.attr("tabindex", "-1");
  10355. // ctx.tree.$container.removeAttr("tabindex");
  10356. } else if( opts.autofocusInput && !triggeredByInput ){
  10357. // Set focus to input sub input (if node was clicked, but not
  10358. // when TAB was pressed )
  10359. $outer = $(node.tr || node.span);
  10360. $outer.find(":input:enabled:first").focus();
  10361. }
  10362. }
  10363. },
  10364. nodeKeydown: function(ctx) {
  10365. var inputType, handleKeys, $td,
  10366. opts = ctx.options.gridnav,
  10367. event = ctx.originalEvent,
  10368. $target = $(event.target);
  10369. if( $target.is(":input:enabled") ) {
  10370. inputType = $target.prop("type");
  10371. } else if( $target.is("a") ) {
  10372. inputType = "link";
  10373. }
  10374. // ctx.tree.debug("ext-gridnav nodeKeydown", event, inputType);
  10375. if( inputType && opts.handleCursorKeys ){
  10376. handleKeys = NAV_KEYS[inputType];
  10377. if( handleKeys && $.inArray(event.which, handleKeys) >= 0 ){
  10378. $td = findNeighbourTd($target, event.which);
  10379. if( $td && $td.length ) {
  10380. // ctx.node.debug("ignore keydown in input", event.which, handleKeys);
  10381. $td.find(":input:enabled,a").focus();
  10382. // Prevent Fancytree default navigation
  10383. return false;
  10384. }
  10385. }
  10386. return true;
  10387. }
  10388. // ctx.tree.debug("ext-gridnav NOT HANDLED", event, inputType);
  10389. return this._superApply(arguments);
  10390. }
  10391. });
  10392. // Value returned by `require('jquery.fancytree..')`
  10393. return $.ui.fancytree;
  10394. })); // End of closure
  10395. /*! Extension 'jquery.fancytree.persist.js' *//*!
  10396. * jquery.fancytree.persist.js
  10397. *
  10398. * Persist tree status in cookiesRemove or highlight tree nodes, based on a filter.
  10399. * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
  10400. *
  10401. * @depends: js-cookie or jquery-cookie
  10402. *
  10403. * Copyright (c) 2008-2018, Martin Wendt (http://wwWendt.de)
  10404. *
  10405. * Released under the MIT license
  10406. * https://github.com/mar10/fancytree/wiki/LicenseInfo
  10407. *
  10408. * @version 2.28.1
  10409. * @date 2018-03-19T06:47:37Z
  10410. */
  10411. ;(function( factory ) {
  10412. if ( typeof define === "function" && define.amd ) {
  10413. // AMD. Register as an anonymous module.
  10414. define( [ "jquery", "./jquery.fancytree" ], factory );
  10415. } else if ( typeof module === "object" && module.exports ) {
  10416. // Node/CommonJS
  10417. require("./jquery.fancytree");
  10418. module.exports = factory(require("jquery"));
  10419. } else {
  10420. // Browser globals
  10421. factory( jQuery );
  10422. }
  10423. }( function( $ ) {
  10424. "use strict";
  10425. /* global Cookies:false */
  10426. /*******************************************************************************
  10427. * Private functions and variables
  10428. */
  10429. var cookieStore = null,
  10430. localStorageStore = window.localStorage ? {
  10431. get: function(key){ return window.localStorage.getItem(key); },
  10432. set: function(key, value){ window.localStorage.setItem(key, value); },
  10433. remove: function(key){ window.localStorage.removeItem(key); }
  10434. } : null,
  10435. sessionStorageStore = window.sessionStorage ? {
  10436. get: function(key){ return window.sessionStorage.getItem(key); },
  10437. set: function(key, value){ window.sessionStorage.setItem(key, value); },
  10438. remove: function(key){ window.sessionStorage.removeItem(key); }
  10439. } : null,
  10440. _assert = $.ui.fancytree.assert,
  10441. ACTIVE = "active",
  10442. EXPANDED = "expanded",
  10443. FOCUS = "focus",
  10444. SELECTED = "selected";
  10445. if( typeof Cookies === "function" ) {
  10446. // Assume https://github.com/js-cookie/js-cookie
  10447. cookieStore = {
  10448. get: Cookies.get,
  10449. set: function(key, value) {
  10450. Cookies.set(key, value, this.options.persist.cookie);
  10451. },
  10452. remove: Cookies.remove
  10453. };
  10454. } else if ( $ && typeof $.cookie === "function" ) {
  10455. // Fall back to https://github.com/carhartl/jquery-cookie
  10456. cookieStore = {
  10457. get: $.cookie,
  10458. set: function(key, value) {
  10459. $.cookie.set(key, value, this.options.persist.cookie);
  10460. },
  10461. remove: $.removeCookie
  10462. };
  10463. }
  10464. /* Recursively load lazy nodes
  10465. * @param {string} mode 'load', 'expand', false
  10466. */
  10467. function _loadLazyNodes(tree, local, keyList, mode, dfd) {
  10468. var i, key, l, node,
  10469. foundOne = false,
  10470. expandOpts = tree.options.persist.expandOpts,
  10471. deferredList = [],
  10472. missingKeyList = [];
  10473. keyList = keyList || [];
  10474. dfd = dfd || $.Deferred();
  10475. for( i=0, l=keyList.length; i<l; i++ ) {
  10476. key = keyList[i];
  10477. node = tree.getNodeByKey(key);
  10478. if( node ) {
  10479. if( mode && node.isUndefined() ) {
  10480. foundOne = true;
  10481. tree.debug("_loadLazyNodes: " + node + " is lazy: loading...");
  10482. if( mode === "expand" ) {
  10483. deferredList.push(node.setExpanded(true, expandOpts));
  10484. } else {
  10485. deferredList.push(node.load());
  10486. }
  10487. } else {
  10488. tree.debug("_loadLazyNodes: " + node + " already loaded.");
  10489. node.setExpanded(true, expandOpts);
  10490. }
  10491. } else {
  10492. missingKeyList.push(key);
  10493. tree.debug("_loadLazyNodes: " + node + " was not yet found.");
  10494. }
  10495. }
  10496. $.when.apply($, deferredList).always(function(){
  10497. // All lazy-expands have finished
  10498. if( foundOne && missingKeyList.length > 0 ) {
  10499. // If we read new nodes from server, try to resolve yet-missing keys
  10500. _loadLazyNodes(tree, local, missingKeyList, mode, dfd);
  10501. } else {
  10502. if( missingKeyList.length ) {
  10503. tree.warn("_loadLazyNodes: could not load those keys: ", missingKeyList);
  10504. for( i=0, l=missingKeyList.length; i<l; i++ ) {
  10505. key = keyList[i];
  10506. local._appendKey(EXPANDED, keyList[i], false);
  10507. }
  10508. }
  10509. dfd.resolve();
  10510. }
  10511. });
  10512. return dfd;
  10513. }
  10514. /**
  10515. * [ext-persist] Remove persistence data of the given type(s).
  10516. * Called like
  10517. * $("#tree").fancytree("getTree").clearCookies("active expanded focus selected");
  10518. *
  10519. * @alias Fancytree#clearPersistData
  10520. * @requires jquery.fancytree.persist.js
  10521. */
  10522. $.ui.fancytree._FancytreeClass.prototype.clearPersistData = function(types){
  10523. var local = this.ext.persist,
  10524. prefix = local.cookiePrefix;
  10525. types = types || "active expanded focus selected";
  10526. if(types.indexOf(ACTIVE) >= 0){
  10527. local._data(prefix + ACTIVE, null);
  10528. }
  10529. if(types.indexOf(EXPANDED) >= 0){
  10530. local._data(prefix + EXPANDED, null);
  10531. }
  10532. if(types.indexOf(FOCUS) >= 0){
  10533. local._data(prefix + FOCUS, null);
  10534. }
  10535. if(types.indexOf(SELECTED) >= 0){
  10536. local._data(prefix + SELECTED, null);
  10537. }
  10538. };
  10539. $.ui.fancytree._FancytreeClass.prototype.clearCookies = function(types){
  10540. this.warn("'tree.clearCookies()' is deprecated since v2.27.0: use 'clearPersistData()' instead.");
  10541. return this.clearPersistData(types);
  10542. };
  10543. /**
  10544. * [ext-persist] Return persistence information from cookies
  10545. *
  10546. * Called like
  10547. * $("#tree").fancytree("getTree").getPersistData();
  10548. *
  10549. * @alias Fancytree#getPersistData
  10550. * @requires jquery.fancytree.persist.js
  10551. */
  10552. $.ui.fancytree._FancytreeClass.prototype.getPersistData = function(){
  10553. var local = this.ext.persist,
  10554. prefix = local.cookiePrefix,
  10555. delim = local.cookieDelimiter,
  10556. res = {};
  10557. res[ACTIVE] = local._data(prefix + ACTIVE);
  10558. res[EXPANDED] = (local._data(prefix + EXPANDED) || "").split(delim);
  10559. res[SELECTED] = (local._data(prefix + SELECTED) || "").split(delim);
  10560. res[FOCUS] = local._data(prefix + FOCUS);
  10561. return res;
  10562. };
  10563. /* *****************************************************************************
  10564. * Extension code
  10565. */
  10566. $.ui.fancytree.registerExtension({
  10567. name: "persist",
  10568. version: "2.28.1",
  10569. // Default options for this extension.
  10570. options: {
  10571. cookieDelimiter: "~",
  10572. cookiePrefix: undefined, // 'fancytree-<treeId>-' by default
  10573. cookie: {
  10574. raw: false,
  10575. expires: "",
  10576. path: "",
  10577. domain: "",
  10578. secure: false
  10579. },
  10580. expandLazy: false, // true: recursively expand and load lazy nodes
  10581. expandOpts: undefined, // optional `opts` argument passed to setExpanded()
  10582. fireActivate: true, // false: suppress `activate` event after active node was restored
  10583. overrideSource: true, // true: cookie takes precedence over `source` data attributes.
  10584. store: "auto", // 'cookie': force cookie, 'local': force localStore, 'session': force sessionStore
  10585. types: "active expanded focus selected"
  10586. },
  10587. /* Generic read/write string data to cookie, sessionStorage or localStorage. */
  10588. _data: function(key, value){
  10589. var store = this._local.store;
  10590. if( value === undefined ) {
  10591. return store.get.call(this, key);
  10592. } else if ( value === null ) {
  10593. store.remove.call(this, key);
  10594. } else {
  10595. store.set.call(this, key, value);
  10596. }
  10597. },
  10598. /* Append `key` to a cookie. */
  10599. _appendKey: function(type, key, flag){
  10600. key = "" + key; // #90
  10601. var local = this._local,
  10602. instOpts = this.options.persist,
  10603. delim = instOpts.cookieDelimiter,
  10604. cookieName = local.cookiePrefix + type,
  10605. data = local._data(cookieName),
  10606. keyList = data ? data.split(delim) : [],
  10607. idx = $.inArray(key, keyList);
  10608. // Remove, even if we add a key, so the key is always the last entry
  10609. if(idx >= 0){
  10610. keyList.splice(idx, 1);
  10611. }
  10612. // Append key to cookie
  10613. if(flag){
  10614. keyList.push(key);
  10615. }
  10616. local._data(cookieName, keyList.join(delim));
  10617. },
  10618. treeInit: function(ctx){
  10619. var tree = ctx.tree,
  10620. opts = ctx.options,
  10621. local = this._local,
  10622. instOpts = this.options.persist;
  10623. // // For 'auto' or 'cookie' mode, the cookie plugin must be available
  10624. // _assert((instOpts.store !== "auto" && instOpts.store !== "cookie") || cookieStore,
  10625. // "Missing required plugin for 'persist' extension: js.cookie.js or jquery.cookie.js");
  10626. local.cookiePrefix = instOpts.cookiePrefix || ("fancytree-" + tree._id + "-");
  10627. local.storeActive = instOpts.types.indexOf(ACTIVE) >= 0;
  10628. local.storeExpanded = instOpts.types.indexOf(EXPANDED) >= 0;
  10629. local.storeSelected = instOpts.types.indexOf(SELECTED) >= 0;
  10630. local.storeFocus = instOpts.types.indexOf(FOCUS) >= 0;
  10631. local.store = null;
  10632. if( instOpts.store === "auto" ) {
  10633. instOpts.store = localStorageStore ? "local" : "cookie";
  10634. }
  10635. if( $.isPlainObject(instOpts.store) ) {
  10636. local.store = instOpts.store;
  10637. } else if( instOpts.store === "cookie" ) {
  10638. local.store = cookieStore;
  10639. } else if( instOpts.store === "local" ){
  10640. local.store = (instOpts.store === "local") ? localStorageStore : sessionStorageStore;
  10641. } else if( instOpts.store === "session" ){
  10642. local.store = (instOpts.store === "local") ? localStorageStore : sessionStorageStore;
  10643. }
  10644. _assert(local.store, "Need a valid store.");
  10645. // Bind init-handler to apply cookie state
  10646. tree.$div.on("fancytreeinit", function(event){
  10647. if ( tree._triggerTreeEvent("beforeRestore", null, {}) === false ) {
  10648. return;
  10649. }
  10650. var cookie, dfd, i, keyList, node,
  10651. prevFocus = local._data(local.cookiePrefix + FOCUS), // record this before node.setActive() overrides it;
  10652. noEvents = instOpts.fireActivate === false;
  10653. // tree.debug("document.cookie:", document.cookie);
  10654. cookie = local._data(local.cookiePrefix + EXPANDED);
  10655. keyList = cookie && cookie.split(instOpts.cookieDelimiter);
  10656. if( local.storeExpanded ) {
  10657. // Recursively load nested lazy nodes if expandLazy is 'expand' or 'load'
  10658. // Also remove expand-cookies for unmatched nodes
  10659. dfd = _loadLazyNodes(tree, local, keyList, instOpts.expandLazy ? "expand" : false , null);
  10660. } else {
  10661. // nothing to do
  10662. dfd = new $.Deferred().resolve();
  10663. }
  10664. dfd.done(function(){
  10665. if(local.storeSelected){
  10666. cookie = local._data(local.cookiePrefix + SELECTED);
  10667. if(cookie){
  10668. keyList = cookie.split(instOpts.cookieDelimiter);
  10669. for(i=0; i<keyList.length; i++){
  10670. node = tree.getNodeByKey(keyList[i]);
  10671. if(node){
  10672. if(node.selected === undefined || instOpts.overrideSource && (node.selected === false)){
  10673. // node.setSelected();
  10674. node.selected = true;
  10675. node.renderStatus();
  10676. }
  10677. }else{
  10678. // node is no longer member of the tree: remove from cookie also
  10679. local._appendKey(SELECTED, keyList[i], false);
  10680. }
  10681. }
  10682. }
  10683. // In selectMode 3 we have to fix the child nodes, since we
  10684. // only stored the selected *top* nodes
  10685. if( tree.options.selectMode === 3 ){
  10686. tree.visit(function(n){
  10687. if( n.selected ) {
  10688. n.fixSelection3AfterClick();
  10689. return "skip";
  10690. }
  10691. });
  10692. }
  10693. }
  10694. if(local.storeActive){
  10695. cookie = local._data(local.cookiePrefix + ACTIVE);
  10696. if(cookie && (opts.persist.overrideSource || !tree.activeNode)){
  10697. node = tree.getNodeByKey(cookie);
  10698. if(node){
  10699. node.debug("persist: set active", cookie);
  10700. // We only want to set the focus if the container
  10701. // had the keyboard focus before
  10702. node.setActive(true, {
  10703. noFocus: true,
  10704. noEvents: noEvents
  10705. });
  10706. }
  10707. }
  10708. }
  10709. if(local.storeFocus && prevFocus){
  10710. node = tree.getNodeByKey(prevFocus);
  10711. if(node){
  10712. // node.debug("persist: set focus", cookie);
  10713. if( tree.options.titlesTabbable ) {
  10714. $(node.span).find(".fancytree-title").focus();
  10715. } else {
  10716. $(tree.$container).focus();
  10717. }
  10718. // node.setFocus();
  10719. }
  10720. }
  10721. tree._triggerTreeEvent("restore", null, {});
  10722. });
  10723. });
  10724. // Init the tree
  10725. return this._superApply(arguments);
  10726. },
  10727. nodeSetActive: function(ctx, flag, callOpts) {
  10728. var res,
  10729. local = this._local;
  10730. flag = (flag !== false);
  10731. res = this._superApply(arguments);
  10732. if(local.storeActive){
  10733. local._data(local.cookiePrefix + ACTIVE, this.activeNode ? this.activeNode.key : null);
  10734. }
  10735. return res;
  10736. },
  10737. nodeSetExpanded: function(ctx, flag, callOpts) {
  10738. var res,
  10739. node = ctx.node,
  10740. local = this._local;
  10741. flag = (flag !== false);
  10742. res = this._superApply(arguments);
  10743. if(local.storeExpanded){
  10744. local._appendKey(EXPANDED, node.key, flag);
  10745. }
  10746. return res;
  10747. },
  10748. nodeSetFocus: function(ctx, flag) {
  10749. var res,
  10750. local = this._local;
  10751. flag = (flag !== false);
  10752. res = this._superApply(arguments);
  10753. if( local.storeFocus ) {
  10754. local._data(local.cookiePrefix + FOCUS, this.focusNode ? this.focusNode.key : null);
  10755. }
  10756. return res;
  10757. },
  10758. nodeSetSelected: function(ctx, flag, callOpts) {
  10759. var res, selNodes,
  10760. tree = ctx.tree,
  10761. node = ctx.node,
  10762. local = this._local;
  10763. flag = (flag !== false);
  10764. res = this._superApply(arguments);
  10765. if(local.storeSelected){
  10766. if( tree.options.selectMode === 3 ){
  10767. // In selectMode 3 we only store the the selected *top* nodes.
  10768. // De-selecting a node may also de-select some parents, so we
  10769. // calculate the current status again
  10770. selNodes = $.map(tree.getSelectedNodes(true), function(n){
  10771. return n.key;
  10772. });
  10773. selNodes = selNodes.join(ctx.options.persist.cookieDelimiter);
  10774. local._data(local.cookiePrefix + SELECTED, selNodes);
  10775. } else {
  10776. // beforeSelect can prevent the change - flag doesn't reflect the node.selected state
  10777. local._appendKey(SELECTED, node.key, node.selected);
  10778. }
  10779. }
  10780. return res;
  10781. }
  10782. });
  10783. // Value returned by `require('jquery.fancytree..')`
  10784. return $.ui.fancytree;
  10785. })); // End of closure
  10786. /*! Extension 'jquery.fancytree.table.js' *//*!
  10787. * jquery.fancytree.table.js
  10788. *
  10789. * Render tree as table (aka 'tree grid', 'table tree').
  10790. * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
  10791. *
  10792. * Copyright (c) 2008-2018, Martin Wendt (http://wwWendt.de)
  10793. *
  10794. * Released under the MIT license
  10795. * https://github.com/mar10/fancytree/wiki/LicenseInfo
  10796. *
  10797. * @version 2.28.1
  10798. * @date 2018-03-19T06:47:37Z
  10799. */
  10800. ;(function( factory ) {
  10801. if ( typeof define === "function" && define.amd ) {
  10802. // AMD. Register as an anonymous module.
  10803. define( [ "jquery", "./jquery.fancytree" ], factory );
  10804. } else if ( typeof module === "object" && module.exports ) {
  10805. // Node/CommonJS
  10806. require("./jquery.fancytree");
  10807. module.exports = factory(require("jquery"));
  10808. } else {
  10809. // Browser globals
  10810. factory( jQuery );
  10811. }
  10812. }( function( $ ) {
  10813. "use strict";
  10814. /* *****************************************************************************
  10815. * Private functions and variables
  10816. */
  10817. function _assert(cond, msg){
  10818. msg = msg || "";
  10819. if(!cond){
  10820. $.error("Assertion failed " + msg);
  10821. }
  10822. }
  10823. function insertFirstChild(referenceNode, newNode) {
  10824. referenceNode.insertBefore(newNode, referenceNode.firstChild);
  10825. }
  10826. function insertSiblingAfter(referenceNode, newNode) {
  10827. referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
  10828. }
  10829. /* Show/hide all rows that are structural descendants of `parent`. */
  10830. function setChildRowVisibility(parent, flag) {
  10831. parent.visit(function(node){
  10832. var tr = node.tr;
  10833. // currentFlag = node.hide ? false : flag; // fix for ext-filter
  10834. if(tr){
  10835. tr.style.display = (node.hide || !flag) ? "none" : "";
  10836. }
  10837. if(!node.expanded){
  10838. return "skip";
  10839. }
  10840. });
  10841. }
  10842. /* Find node that is rendered in previous row. */
  10843. function findPrevRowNode(node){
  10844. var i, last, prev,
  10845. parent = node.parent,
  10846. siblings = parent ? parent.children : null;
  10847. if(siblings && siblings.length > 1 && siblings[0] !== node){
  10848. // use the lowest descendant of the preceeding sibling
  10849. i = $.inArray(node, siblings);
  10850. prev = siblings[i - 1];
  10851. _assert(prev.tr);
  10852. // descend to lowest child (with a <tr> tag)
  10853. while(prev.children && prev.children.length){
  10854. last = prev.children[prev.children.length - 1];
  10855. if(!last.tr){
  10856. break;
  10857. }
  10858. prev = last;
  10859. }
  10860. }else{
  10861. // if there is no preceding sibling, use the direct parent
  10862. prev = parent;
  10863. }
  10864. return prev;
  10865. }
  10866. /* Render callback for 'wide' mode. */
  10867. // function _renderStatusNodeWide(event, data) {
  10868. // var node = data.node,
  10869. // nodeColumnIdx = data.options.table.nodeColumnIdx,
  10870. // $tdList = $(node.tr).find(">td");
  10871. // $tdList.eq(nodeColumnIdx).attr("colspan", data.tree.columnCount);
  10872. // $tdList.not(":eq(" + nodeColumnIdx + ")").remove();
  10873. // }
  10874. $.ui.fancytree.registerExtension({
  10875. name: "table",
  10876. version: "2.28.1",
  10877. // Default options for this extension.
  10878. options: {
  10879. checkboxColumnIdx: null, // render the checkboxes into the this column index (default: nodeColumnIdx)
  10880. // customStatus: false, // true: generate renderColumns events for status nodes
  10881. indentation: 16, // indent every node level by 16px
  10882. nodeColumnIdx: 0 // render node expander, icon, and title to this column (default: #0)
  10883. },
  10884. // Overide virtual methods for this extension.
  10885. // `this` : is this extension object
  10886. // `this._super`: the virtual function that was overriden (member of prev. extension or Fancytree)
  10887. treeInit: function(ctx){
  10888. var i, columnCount, n, $row, $tbody,
  10889. tree = ctx.tree,
  10890. opts = ctx.options,
  10891. tableOpts = opts.table,
  10892. $table = tree.widget.element;
  10893. if( tableOpts.customStatus != null ) {
  10894. if( opts.renderStatusColumns != null) {
  10895. $.error("The 'customStatus' option is deprecated since v2.15.0. Use 'renderStatusColumns' only instead.");
  10896. } else {
  10897. tree.warn("The 'customStatus' option is deprecated since v2.15.0. Use 'renderStatusColumns' instead.");
  10898. opts.renderStatusColumns = tableOpts.customStatus;
  10899. }
  10900. }
  10901. if( opts.renderStatusColumns ) {
  10902. if( opts.renderStatusColumns === true ) {
  10903. opts.renderStatusColumns = opts.renderColumns;
  10904. // } else if( opts.renderStatusColumns === "wide" ) {
  10905. // opts.renderStatusColumns = _renderStatusNodeWide;
  10906. }
  10907. }
  10908. $table.addClass("fancytree-container fancytree-ext-table");
  10909. $tbody = $table.find(">tbody");
  10910. if( !$tbody.length ) {
  10911. // TODO: not sure if we can rely on browsers to insert missing <tbody> before <tr>s:
  10912. if( $table.find(">tr").length ) {
  10913. $.error("Expected table > tbody > tr. If you see this please open an issue.");
  10914. }
  10915. $tbody = $("<tbody>").appendTo($table);
  10916. }
  10917. tree.tbody = $tbody[0];
  10918. // Prepare row templates:
  10919. // Determine column count from table header if any
  10920. columnCount = $("thead >tr:last >th", $table).length;
  10921. // Read TR templates from tbody if any
  10922. $row = $tbody.children("tr:first");
  10923. if( $row.length ) {
  10924. n = $row.children("td").length;
  10925. if( columnCount && n !== columnCount ) {
  10926. tree.warn("Column count mismatch between thead (" + columnCount + ") and tbody (" + n + "): using tbody.");
  10927. columnCount = n;
  10928. }
  10929. $row = $row.clone();
  10930. } else {
  10931. // Only thead is defined: create default row markup
  10932. _assert(columnCount >= 1, "Need either <thead> or <tbody> with <td> elements to determine column count.");
  10933. $row = $("<tr />");
  10934. for(i=0; i<columnCount; i++) {
  10935. $row.append("<td />");
  10936. }
  10937. }
  10938. $row.find(">td").eq(tableOpts.nodeColumnIdx)
  10939. .html("<span class='fancytree-node' />");
  10940. if( opts.aria ) {
  10941. $row.attr("role", "row");
  10942. $row.find("td").attr("role", "gridcell");
  10943. }
  10944. tree.rowFragment = document.createDocumentFragment();
  10945. tree.rowFragment.appendChild($row.get(0));
  10946. // // If tbody contains a second row, use this as status node template
  10947. // $row = $tbody.children("tr:eq(1)");
  10948. // if( $row.length === 0 ) {
  10949. // tree.statusRowFragment = tree.rowFragment;
  10950. // } else {
  10951. // $row = $row.clone();
  10952. // tree.statusRowFragment = document.createDocumentFragment();
  10953. // tree.statusRowFragment.appendChild($row.get(0));
  10954. // }
  10955. //
  10956. $tbody.empty();
  10957. // Make sure that status classes are set on the node's <tr> elements
  10958. tree.statusClassPropName = "tr";
  10959. tree.ariaPropName = "tr";
  10960. this.nodeContainerAttrName = "tr";
  10961. // #489: make sure $container is set to <table>, even if ext-dnd is listed before ext-table
  10962. tree.$container = $table;
  10963. this._superApply(arguments);
  10964. // standard Fancytree created a root UL
  10965. $(tree.rootNode.ul).remove();
  10966. tree.rootNode.ul = null;
  10967. // Add container to the TAB chain
  10968. // #577: Allow to set tabindex to "0", "-1" and ""
  10969. this.$container.attr("tabindex", opts.tabindex);
  10970. // this.$container.attr("tabindex", opts.tabbable ? "0" : "-1");
  10971. if(opts.aria) {
  10972. tree.$container
  10973. .attr("role", "treegrid")
  10974. .attr("aria-readonly", true);
  10975. }
  10976. },
  10977. nodeRemoveChildMarkup: function(ctx) {
  10978. var node = ctx.node;
  10979. // node.debug("nodeRemoveChildMarkup()");
  10980. node.visit(function(n){
  10981. if(n.tr){
  10982. $(n.tr).remove();
  10983. n.tr = null;
  10984. }
  10985. });
  10986. },
  10987. nodeRemoveMarkup: function(ctx) {
  10988. var node = ctx.node;
  10989. // node.debug("nodeRemoveMarkup()");
  10990. if(node.tr){
  10991. $(node.tr).remove();
  10992. node.tr = null;
  10993. }
  10994. this.nodeRemoveChildMarkup(ctx);
  10995. },
  10996. /* Override standard render. */
  10997. nodeRender: function(ctx, force, deep, collapsed, _recursive) {
  10998. var children, firstTr, i, l, newRow, prevNode, prevTr, subCtx,
  10999. tree = ctx.tree,
  11000. node = ctx.node,
  11001. opts = ctx.options,
  11002. isRootNode = !node.parent;
  11003. if( tree._enableUpdate === false ) {
  11004. // $.ui.fancytree.debug("*** nodeRender _enableUpdate: false");
  11005. return;
  11006. }
  11007. if( !_recursive ){
  11008. ctx.hasCollapsedParents = node.parent && !node.parent.expanded;
  11009. }
  11010. // $.ui.fancytree.debug("*** nodeRender " + node + ", isRoot=" + isRootNode, "tr=" + node.tr, "hcp=" + ctx.hasCollapsedParents, "parent.tr=" + (node.parent && node.parent.tr));
  11011. if( !isRootNode ){
  11012. if( node.tr && force ) {
  11013. this.nodeRemoveMarkup(ctx);
  11014. }
  11015. if( !node.tr ) {
  11016. if( ctx.hasCollapsedParents && !deep ) {
  11017. // #166: we assume that the parent will be (recursively) rendered
  11018. // later anyway.
  11019. // node.debug("nodeRender ignored due to unrendered parent");
  11020. return;
  11021. }
  11022. // Create new <tr> after previous row
  11023. // if( node.isStatusNode() ) {
  11024. // newRow = tree.statusRowFragment.firstChild.cloneNode(true);
  11025. // } else {
  11026. newRow = tree.rowFragment.firstChild.cloneNode(true);
  11027. // }
  11028. prevNode = findPrevRowNode(node);
  11029. // $.ui.fancytree.debug("*** nodeRender " + node + ": prev: " + prevNode.key);
  11030. _assert(prevNode);
  11031. if(collapsed === true && _recursive){
  11032. // hide all child rows, so we can use an animation to show it later
  11033. newRow.style.display = "none";
  11034. }else if(deep && ctx.hasCollapsedParents){
  11035. // also hide this row if deep === true but any parent is collapsed
  11036. newRow.style.display = "none";
  11037. // newRow.style.color = "red";
  11038. }
  11039. if(!prevNode.tr){
  11040. _assert(!prevNode.parent, "prev. row must have a tr, or be system root");
  11041. // tree.tbody.appendChild(newRow);
  11042. insertFirstChild(tree.tbody, newRow); // #675
  11043. }else{
  11044. insertSiblingAfter(prevNode.tr, newRow);
  11045. }
  11046. node.tr = newRow;
  11047. if( node.key && opts.generateIds ){
  11048. node.tr.id = opts.idPrefix + node.key;
  11049. }
  11050. node.tr.ftnode = node;
  11051. // if(opts.aria){
  11052. // $(node.tr).attr("aria-labelledby", "ftal_" + opts.idPrefix + node.key);
  11053. // }
  11054. node.span = $("span.fancytree-node", node.tr).get(0);
  11055. // Set icon, link, and title (normally this is only required on initial render)
  11056. this.nodeRenderTitle(ctx);
  11057. // Allow tweaking, binding, after node was created for the first time
  11058. // tree._triggerNodeEvent("createNode", ctx);
  11059. if ( opts.createNode ){
  11060. opts.createNode.call(tree, {type: "createNode"}, ctx);
  11061. }
  11062. } else {
  11063. if( force ) {
  11064. // Set icon, link, and title (normally this is only required on initial render)
  11065. this.nodeRenderTitle(ctx); // triggers renderColumns()
  11066. } else {
  11067. // Update element classes according to node state
  11068. this.nodeRenderStatus(ctx);
  11069. }
  11070. }
  11071. }
  11072. // Allow tweaking after node state was rendered
  11073. // tree._triggerNodeEvent("renderNode", ctx);
  11074. if ( opts.renderNode ){
  11075. opts.renderNode.call(tree, {type: "renderNode"}, ctx);
  11076. }
  11077. // Visit child nodes
  11078. // Add child markup
  11079. children = node.children;
  11080. if(children && (isRootNode || deep || node.expanded)){
  11081. for(i=0, l=children.length; i<l; i++) {
  11082. subCtx = $.extend({}, ctx, {node: children[i]});
  11083. subCtx.hasCollapsedParents = subCtx.hasCollapsedParents || !node.expanded;
  11084. this.nodeRender(subCtx, force, deep, collapsed, true);
  11085. }
  11086. }
  11087. // Make sure, that <tr> order matches node.children order.
  11088. if(children && !_recursive){ // we only have to do it once, for the root branch
  11089. prevTr = node.tr || null;
  11090. firstTr = tree.tbody.firstChild;
  11091. // Iterate over all descendants
  11092. node.visit(function(n){
  11093. if(n.tr){
  11094. if(!n.parent.expanded && n.tr.style.display !== "none"){
  11095. // fix after a node was dropped over a collapsed
  11096. n.tr.style.display = "none";
  11097. setChildRowVisibility(n, false);
  11098. }
  11099. if(n.tr.previousSibling !== prevTr){
  11100. node.debug("_fixOrder: mismatch at node: " + n);
  11101. var nextTr = prevTr ? prevTr.nextSibling : firstTr;
  11102. tree.tbody.insertBefore(n.tr, nextTr);
  11103. }
  11104. prevTr = n.tr;
  11105. }
  11106. });
  11107. }
  11108. // Update element classes according to node state
  11109. // if(!isRootNode){
  11110. // this.nodeRenderStatus(ctx);
  11111. // }
  11112. },
  11113. nodeRenderTitle: function(ctx, title) {
  11114. var $cb, res,
  11115. node = ctx.node,
  11116. opts = ctx.options,
  11117. isStatusNode = node.isStatusNode();
  11118. res = this._super(ctx, title);
  11119. if( node.isRootNode() ) {
  11120. return res;
  11121. }
  11122. // Move checkbox to custom column
  11123. if(opts.checkbox && !isStatusNode && opts.table.checkboxColumnIdx != null ){
  11124. $cb = $("span.fancytree-checkbox", node.span); //.detach();
  11125. $(node.tr).find("td").eq(+opts.table.checkboxColumnIdx).html($cb);
  11126. }
  11127. // Update element classes according to node state
  11128. this.nodeRenderStatus(ctx);
  11129. if( isStatusNode ) {
  11130. if( opts.renderStatusColumns ) {
  11131. // Let user code write column content
  11132. opts.renderStatusColumns.call(ctx.tree, {type: "renderStatusColumns"}, ctx);
  11133. } // else: default rendering for status node: leave other cells empty
  11134. } else if ( opts.renderColumns ) {
  11135. opts.renderColumns.call(ctx.tree, {type: "renderColumns"}, ctx);
  11136. }
  11137. return res;
  11138. },
  11139. nodeRenderStatus: function(ctx) {
  11140. var indent,
  11141. node = ctx.node,
  11142. opts = ctx.options;
  11143. this._super(ctx);
  11144. $(node.tr).removeClass("fancytree-node");
  11145. // indent
  11146. indent = (node.getLevel() - 1) * opts.table.indentation;
  11147. if( opts.rtl ) {
  11148. $(node.span).css({paddingRight: indent + "px"});
  11149. } else {
  11150. $(node.span).css({paddingLeft: indent + "px"});
  11151. }
  11152. },
  11153. /* Expand node, return Deferred.promise. */
  11154. nodeSetExpanded: function(ctx, flag, callOpts) {
  11155. // flag defaults to true
  11156. flag = (flag !== false);
  11157. if((ctx.node.expanded && flag) || (!ctx.node.expanded && !flag)) {
  11158. // Expanded state isn't changed - just call base implementation
  11159. return this._superApply(arguments);
  11160. }
  11161. var dfd = new $.Deferred(),
  11162. subOpts = $.extend({}, callOpts, {noEvents: true, noAnimation: true});
  11163. callOpts = callOpts || {};
  11164. function _afterExpand(ok) {
  11165. setChildRowVisibility(ctx.node, flag);
  11166. if( ok ) {
  11167. if( flag && ctx.options.autoScroll && !callOpts.noAnimation && ctx.node.hasChildren() ) {
  11168. // Scroll down to last child, but keep current node visible
  11169. ctx.node.getLastChild().scrollIntoView(true, {topNode: ctx.node}).always(function(){
  11170. if( !callOpts.noEvents ) {
  11171. ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
  11172. }
  11173. dfd.resolveWith(ctx.node);
  11174. });
  11175. } else {
  11176. if( !callOpts.noEvents ) {
  11177. ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
  11178. }
  11179. dfd.resolveWith(ctx.node);
  11180. }
  11181. } else {
  11182. if( !callOpts.noEvents ) {
  11183. ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
  11184. }
  11185. dfd.rejectWith(ctx.node);
  11186. }
  11187. }
  11188. // Call base-expand with disabled events and animation
  11189. this._super(ctx, flag, subOpts).done(function () {
  11190. _afterExpand(true);
  11191. }).fail(function () {
  11192. _afterExpand(false);
  11193. });
  11194. return dfd.promise();
  11195. },
  11196. nodeSetStatus: function(ctx, status, message, details) {
  11197. if(status === "ok"){
  11198. var node = ctx.node,
  11199. firstChild = ( node.children ? node.children[0] : null );
  11200. if ( firstChild && firstChild.isStatusNode() ) {
  11201. $(firstChild.tr).remove();
  11202. }
  11203. }
  11204. return this._superApply(arguments);
  11205. },
  11206. treeClear: function(ctx) {
  11207. this.nodeRemoveChildMarkup(this._makeHookContext(this.rootNode));
  11208. return this._superApply(arguments);
  11209. },
  11210. treeDestroy: function(ctx) {
  11211. this.$container.find("tbody").empty();
  11212. this.$source && this.$source.removeClass("fancytree-helper-hidden");
  11213. return this._superApply(arguments);
  11214. }
  11215. /*,
  11216. treeSetFocus: function(ctx, flag) {
  11217. // alert("treeSetFocus" + ctx.tree.$container);
  11218. ctx.tree.$container.focus();
  11219. $.ui.fancytree.focusTree = ctx.tree;
  11220. }*/
  11221. });
  11222. // Value returned by `require('jquery.fancytree..')`
  11223. return $.ui.fancytree;
  11224. })); // End of closure
  11225. /*! Extension 'jquery.fancytree.themeroller.js' *//*!
  11226. * jquery.fancytree.themeroller.js
  11227. *
  11228. * Enable jQuery UI ThemeRoller styles.
  11229. * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
  11230. *
  11231. * @see http://jqueryui.com/themeroller/
  11232. *
  11233. * Copyright (c) 2008-2018, Martin Wendt (http://wwWendt.de)
  11234. *
  11235. * Released under the MIT license
  11236. * https://github.com/mar10/fancytree/wiki/LicenseInfo
  11237. *
  11238. * @version 2.28.1
  11239. * @date 2018-03-19T06:47:37Z
  11240. */
  11241. ;(function( factory ) {
  11242. if ( typeof define === "function" && define.amd ) {
  11243. // AMD. Register as an anonymous module.
  11244. define( [ "jquery", "./jquery.fancytree" ], factory );
  11245. } else if ( typeof module === "object" && module.exports ) {
  11246. // Node/CommonJS
  11247. require("./jquery.fancytree");
  11248. module.exports = factory(require("jquery"));
  11249. } else {
  11250. // Browser globals
  11251. factory( jQuery );
  11252. }
  11253. }( function( $ ) {
  11254. "use strict";
  11255. /*******************************************************************************
  11256. * Extension code
  11257. */
  11258. $.ui.fancytree.registerExtension({
  11259. name: "themeroller",
  11260. version: "2.28.1",
  11261. // Default options for this extension.
  11262. options: {
  11263. activeClass: "ui-state-active", // Class added to active node
  11264. // activeClass: "ui-state-highlight",
  11265. addClass: "ui-corner-all", // Class added to all nodes
  11266. focusClass: "ui-state-focus", // Class added to focused node
  11267. hoverClass: "ui-state-hover", // Class added to hovered node
  11268. selectedClass: "ui-state-highlight" // Class added to selected nodes
  11269. // selectedClass: "ui-state-active"
  11270. },
  11271. treeInit: function(ctx){
  11272. var $el = ctx.widget.element,
  11273. opts = ctx.options.themeroller;
  11274. this._superApply(arguments);
  11275. if($el[0].nodeName === "TABLE"){
  11276. $el.addClass("ui-widget ui-corner-all");
  11277. $el.find(">thead tr").addClass("ui-widget-header");
  11278. $el.find(">tbody").addClass("ui-widget-conent");
  11279. }else{
  11280. $el.addClass("ui-widget ui-widget-content ui-corner-all");
  11281. }
  11282. $el.delegate(".fancytree-node", "mouseenter mouseleave", function(event){
  11283. var node = $.ui.fancytree.getNode(event.target),
  11284. flag = (event.type === "mouseenter");
  11285. $(node.tr ? node.tr : node.span)
  11286. .toggleClass(opts.hoverClass + " " + opts.addClass, flag);
  11287. });
  11288. },
  11289. treeDestroy: function(ctx){
  11290. this._superApply(arguments);
  11291. ctx.widget.element.removeClass("ui-widget ui-widget-content ui-corner-all");
  11292. },
  11293. nodeRenderStatus: function(ctx){
  11294. var classes = {},
  11295. node = ctx.node,
  11296. $el = $(node.tr ? node.tr : node.span),
  11297. opts = ctx.options.themeroller;
  11298. this._super(ctx);
  11299. /*
  11300. .ui-state-highlight: Class to be applied to highlighted or selected elements. Applies "highlight" container styles to an element and its child text, links, and icons.
  11301. .ui-state-error: Class to be applied to error messaging container elements. Applies "error" container styles to an element and its child text, links, and icons.
  11302. .ui-state-error-text: An additional class that applies just the error text color without background. Can be used on form labels for instance. Also applies error icon color to child icons.
  11303. .ui-state-default: Class to be applied to clickable button-like elements. Applies "clickable default" container styles to an element and its child text, links, and icons.
  11304. .ui-state-hover: Class to be applied on mouseover to clickable button-like elements. Applies "clickable hover" container styles to an element and its child text, links, and icons.
  11305. .ui-state-focus: Class to be applied on keyboard focus to clickable button-like elements. Applies "clickable hover" container styles to an element and its child text, links, and icons.
  11306. .ui-state-active: Class to be applied on mousedown to clickable button-like elements. Applies "clickable active" container styles to an element and its child text, links, and icons.
  11307. */
  11308. // Set ui-state-* class (handle the case that the same class is assigned
  11309. // to different states)
  11310. classes[opts.activeClass] = false;
  11311. classes[opts.focusClass] = false;
  11312. classes[opts.selectedClass] = false;
  11313. if( node.isActive() ) { classes[opts.activeClass] = true; }
  11314. if( node.hasFocus() ) { classes[opts.focusClass] = true; }
  11315. // activeClass takes precedence before selectedClass:
  11316. if( node.isSelected() && !node.isActive() ) { classes[opts.selectedClass] = true; }
  11317. $el.toggleClass(opts.activeClass, classes[opts.activeClass]);
  11318. $el.toggleClass(opts.focusClass, classes[opts.focusClass]);
  11319. $el.toggleClass(opts.selectedClass, classes[opts.selectedClass]);
  11320. // Additional classes (e.g. 'ui-corner-all')
  11321. $el.addClass(opts.addClass);
  11322. }
  11323. });
  11324. // Value returned by `require('jquery.fancytree..')`
  11325. return $.ui.fancytree;
  11326. })); // End of closure
  11327. /*! Extension 'jquery.fancytree.wide.js' *//*!
  11328. * jquery.fancytree.wide.js
  11329. * Support for 100% wide selection bars.
  11330. * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
  11331. *
  11332. * Copyright (c) 2008-2018, Martin Wendt (http://wwWendt.de)
  11333. *
  11334. * Released under the MIT license
  11335. * https://github.com/mar10/fancytree/wiki/LicenseInfo
  11336. *
  11337. * @version 2.28.1
  11338. * @date 2018-03-19T06:47:37Z
  11339. */
  11340. ;(function( factory ) {
  11341. if ( typeof define === "function" && define.amd ) {
  11342. // AMD. Register as an anonymous module.
  11343. define( [ "jquery", "./jquery.fancytree" ], factory );
  11344. } else if ( typeof module === "object" && module.exports ) {
  11345. // Node/CommonJS
  11346. require("./jquery.fancytree");
  11347. module.exports = factory(require("jquery"));
  11348. } else {
  11349. // Browser globals
  11350. factory( jQuery );
  11351. }
  11352. }( function( $ ) {
  11353. "use strict";
  11354. var reNumUnit = /^([+-]?(?:\d+|\d*\.\d+))([a-z]*|%)$/; // split "1.5em" to ["1.5", "em"]
  11355. /*******************************************************************************
  11356. * Private functions and variables
  11357. */
  11358. // var _assert = $.ui.fancytree.assert;
  11359. /* Calculate inner width without scrollbar */
  11360. // function realInnerWidth($el) {
  11361. // // http://blog.jquery.com/2012/08/16/jquery-1-8-box-sizing-width-csswidth-and-outerwidth/
  11362. // // inst.contWidth = parseFloat(this.$container.css("width"), 10);
  11363. // // 'Client width without scrollbar' - 'padding'
  11364. // return $el[0].clientWidth - ($el.innerWidth() - parseFloat($el.css("width"), 10));
  11365. // }
  11366. /* Create a global embedded CSS style for the tree. */
  11367. function defineHeadStyleElement(id, cssText) {
  11368. id = "fancytree-style-" + id;
  11369. var $headStyle = $("#" + id);
  11370. if( !cssText ) {
  11371. $headStyle.remove();
  11372. return null;
  11373. }
  11374. if( !$headStyle.length ) {
  11375. $headStyle = $("<style />")
  11376. .attr("id", id)
  11377. .addClass("fancytree-style")
  11378. .prop("type", "text/css")
  11379. .appendTo("head");
  11380. }
  11381. try {
  11382. $headStyle.html(cssText);
  11383. } catch ( e ) {
  11384. // fix for IE 6-8
  11385. $headStyle[0].styleSheet.cssText = cssText;
  11386. }
  11387. return $headStyle;
  11388. }
  11389. /* Calculate the CSS rules that indent title spans. */
  11390. function renderLevelCss(containerId, depth, levelOfs, lineOfs, labelOfs, measureUnit)
  11391. {
  11392. var i,
  11393. prefix = "#" + containerId + " span.fancytree-level-",
  11394. rules = [];
  11395. for(i = 0; i < depth; i++) {
  11396. rules.push(prefix + (i + 1) + " span.fancytree-title { padding-left: " +
  11397. (i * levelOfs + lineOfs) + measureUnit + "; }");
  11398. }
  11399. // Some UI animations wrap the UL inside a DIV and set position:relative on both.
  11400. // This breaks the left:0 and padding-left:nn settings of the title
  11401. rules.push(
  11402. "#" + containerId + " div.ui-effects-wrapper ul li span.fancytree-title, " +
  11403. "#" + containerId + " li.fancytree-animating span.fancytree-title " + // #716
  11404. "{ padding-left: " + labelOfs + measureUnit + "; position: static; width: auto; }");
  11405. return rules.join("\n");
  11406. }
  11407. // /**
  11408. // * [ext-wide] Recalculate the width of the selection bar after the tree container
  11409. // * was resized.<br>
  11410. // * May be called explicitly on container resize, since there is no resize event
  11411. // * for DIV tags.
  11412. // *
  11413. // * @alias Fancytree#wideUpdate
  11414. // * @requires jquery.fancytree.wide.js
  11415. // */
  11416. // $.ui.fancytree._FancytreeClass.prototype.wideUpdate = function(){
  11417. // var inst = this.ext.wide,
  11418. // prevCw = inst.contWidth,
  11419. // prevLo = inst.lineOfs;
  11420. // inst.contWidth = realInnerWidth(this.$container);
  11421. // // Each title is precceeded by 2 or 3 icons (16px + 3 margin)
  11422. // // + 1px title border and 3px title padding
  11423. // // TODO: use code from treeInit() below
  11424. // inst.lineOfs = (this.options.checkbox ? 3 : 2) * 19;
  11425. // if( prevCw !== inst.contWidth || prevLo !== inst.lineOfs ) {
  11426. // this.debug("wideUpdate: " + inst.contWidth);
  11427. // this.visit(function(node){
  11428. // node.tree._callHook("nodeRenderTitle", node);
  11429. // });
  11430. // }
  11431. // };
  11432. /*******************************************************************************
  11433. * Extension code
  11434. */
  11435. $.ui.fancytree.registerExtension({
  11436. name: "wide",
  11437. version: "2.28.1",
  11438. // Default options for this extension.
  11439. options: {
  11440. iconWidth: null, // Adjust this if @fancy-icon-width != "16px"
  11441. iconSpacing: null, // Adjust this if @fancy-icon-spacing != "3px"
  11442. labelSpacing: null, // Adjust this if padding between icon and label != "3px"
  11443. levelOfs: null // Adjust this if ul padding != "16px"
  11444. },
  11445. treeCreate: function(ctx){
  11446. this._superApply(arguments);
  11447. this.$container.addClass("fancytree-ext-wide");
  11448. var containerId, cssText, iconSpacingUnit, labelSpacingUnit, iconWidthUnit, levelOfsUnit,
  11449. instOpts = ctx.options.wide,
  11450. // css sniffing
  11451. $dummyLI = $("<li id='fancytreeTemp'><span class='fancytree-node'><span class='fancytree-icon' /><span class='fancytree-title' /></span><ul />")
  11452. .appendTo(ctx.tree.$container),
  11453. $dummyIcon = $dummyLI.find(".fancytree-icon"),
  11454. $dummyUL = $dummyLI.find("ul"),
  11455. // $dummyTitle = $dummyLI.find(".fancytree-title"),
  11456. iconSpacing = instOpts.iconSpacing || $dummyIcon.css("margin-left"),
  11457. iconWidth = instOpts.iconWidth || $dummyIcon.css("width"),
  11458. labelSpacing = instOpts.labelSpacing || "3px",
  11459. levelOfs = instOpts.levelOfs || $dummyUL.css("padding-left");
  11460. $dummyLI.remove();
  11461. iconSpacingUnit = iconSpacing.match(reNumUnit)[2];
  11462. iconSpacing = parseFloat(iconSpacing, 10);
  11463. labelSpacingUnit = labelSpacing.match(reNumUnit)[2];
  11464. labelSpacing = parseFloat(labelSpacing, 10);
  11465. iconWidthUnit = iconWidth.match(reNumUnit)[2];
  11466. iconWidth = parseFloat(iconWidth, 10);
  11467. levelOfsUnit = levelOfs.match(reNumUnit)[2];
  11468. if( iconSpacingUnit !== iconWidthUnit || levelOfsUnit !== iconWidthUnit || labelSpacingUnit !== iconWidthUnit ) {
  11469. $.error("iconWidth, iconSpacing, and levelOfs must have the same css measure unit");
  11470. }
  11471. this._local.measureUnit = iconWidthUnit;
  11472. this._local.levelOfs = parseFloat(levelOfs);
  11473. this._local.lineOfs = (1 + (ctx.options.checkbox ? 1 : 0) +
  11474. (ctx.options.icon === false ? 0 : 1)) * (iconWidth + iconSpacing) +
  11475. iconSpacing;
  11476. this._local.labelOfs = labelSpacing;
  11477. this._local.maxDepth = 10;
  11478. // Get/Set a unique Id on the container (if not already exists)
  11479. containerId = this.$container.uniqueId().attr("id");
  11480. // Generated css rules for some levels (extended on demand)
  11481. cssText = renderLevelCss(containerId, this._local.maxDepth,
  11482. this._local.levelOfs, this._local.lineOfs, this._local.labelOfs,
  11483. this._local.measureUnit);
  11484. defineHeadStyleElement(containerId, cssText);
  11485. },
  11486. treeDestroy: function(ctx){
  11487. // Remove generated css rules
  11488. defineHeadStyleElement(this.$container.attr("id"), null);
  11489. return this._superApply(arguments);
  11490. },
  11491. nodeRenderStatus: function(ctx) {
  11492. var containerId, cssText, res,
  11493. node = ctx.node,
  11494. level = node.getLevel();
  11495. res = this._super(ctx);
  11496. // Generate some more level-n rules if required
  11497. if( level > this._local.maxDepth ) {
  11498. containerId = this.$container.attr("id");
  11499. this._local.maxDepth *= 2;
  11500. node.debug("Define global ext-wide css up to level " + this._local.maxDepth);
  11501. cssText = renderLevelCss(containerId, this._local.maxDepth,
  11502. this._local.levelOfs, this._local.lineOfs, this._local.labelSpacing,
  11503. this._local.measureUnit);
  11504. defineHeadStyleElement(containerId, cssText);
  11505. }
  11506. // Add level-n class to apply indentation padding.
  11507. // (Setting element style would not work, since it cannot easily be
  11508. // overriden while animations run)
  11509. $(node.span).addClass("fancytree-level-" + level);
  11510. return res;
  11511. }
  11512. });
  11513. // Value returned by `require('jquery.fancytree..')`
  11514. return $.ui.fancytree;
  11515. })); // End of closure
  11516. // Value returned by `require('jquery.fancytree')`
  11517. return $.ui.fancytree;
  11518. })); // End of closure