elfinder.full.js 1.0 MB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028310293103031031310323103331034310353103631037310383103931040310413104231043310443104531046310473104831049310503105131052310533105431055310563105731058310593106031061310623106331064310653106631067310683106931070310713107231073310743107531076310773107831079310803108131082310833108431085310863108731088310893109031091310923109331094310953109631097310983109931100311013110231103311043110531106311073110831109311103111131112311133111431115311163111731118311193112031121311223112331124311253112631127311283112931130311313113231133311343113531136311373113831139311403114131142311433114431145311463114731148311493115031151311523115331154311553115631157311583115931160311613116231163311643116531166311673116831169311703117131172311733117431175311763117731178311793118031181311823118331184311853118631187311883118931190311913119231193311943119531196311973119831199312003120131202312033120431205312063120731208312093121031211312123121331214312153121631217312183121931220312213122231223312243122531226312273122831229312303123131232312333123431235312363123731238312393124031241312423124331244312453124631247312483124931250312513125231253312543125531256312573125831259312603126131262312633126431265312663126731268312693127031271312723127331274312753127631277312783127931280312813128231283312843128531286312873128831289312903129131292312933129431295312963129731298312993130031301313023130331304313053130631307313083130931310313113131231313313143131531316313173131831319313203132131322313233132431325313263132731328313293133031331313323133331334313353133631337313383133931340313413134231343313443134531346313473134831349313503135131352313533135431355313563135731358313593136031361313623136331364313653136631367313683136931370313713137231373313743137531376313773137831379313803138131382313833138431385313863138731388313893139031391313923139331394313953139631397313983139931400314013140231403314043140531406314073140831409314103141131412314133141431415314163141731418314193142031421314223142331424314253142631427314283142931430314313143231433314343143531436314373143831439314403144131442314433144431445314463144731448314493145031451314523145331454314553145631457314583145931460314613146231463314643146531466314673146831469314703147131472314733147431475314763147731478314793148031481314823148331484314853148631487314883148931490314913149231493314943149531496314973149831499315003150131502315033150431505315063150731508315093151031511315123151331514315153151631517315183151931520315213152231523315243152531526315273152831529315303153131532315333153431535315363153731538315393154031541315423154331544315453154631547315483154931550315513155231553315543155531556315573155831559315603156131562315633156431565315663156731568315693157031571315723157331574315753157631577315783157931580315813158231583315843158531586315873158831589315903159131592315933159431595315963159731598315993160031601316023160331604316053160631607316083160931610316113161231613316143161531616316173161831619316203162131622316233162431625316263162731628316293163031631316323163331634316353163631637316383163931640316413164231643316443164531646316473164831649316503165131652316533165431655316563165731658316593166031661316623166331664316653166631667316683166931670316713167231673316743167531676316773167831679316803168131682316833168431685316863168731688316893169031691316923169331694316953169631697316983169931700317013170231703317043170531706317073170831709317103171131712317133171431715317163171731718317193172031721317223172331724317253172631727317283172931730317313173231733317343173531736317373173831739317403174131742317433174431745317463174731748317493175031751317523175331754317553175631757317583175931760317613176231763317643176531766317673176831769317703177131772317733177431775317763177731778317793178031781317823178331784317853178631787317883178931790317913179231793317943179531796317973179831799318003180131802318033180431805318063180731808318093181031811318123181331814318153181631817318183181931820318213182231823318243182531826318273182831829318303183131832318333183431835318363183731838318393184031841318423184331844318453184631847318483184931850318513185231853318543185531856318573185831859318603186131862318633186431865318663186731868318693187031871318723187331874318753187631877318783187931880318813188231883318843188531886318873188831889318903189131892318933189431895318963189731898318993190031901319023190331904319053190631907319083190931910319113191231913319143191531916319173191831919319203192131922319233192431925319263192731928319293193031931319323193331934319353193631937319383193931940319413194231943319443194531946319473194831949319503195131952319533195431955319563195731958319593196031961319623196331964319653196631967319683196931970319713197231973319743197531976319773197831979319803198131982319833198431985319863198731988319893199031991319923199331994319953199631997319983199932000320013200232003320043200532006320073200832009320103201132012320133201432015320163201732018320193202032021320223202332024320253202632027320283202932030320313203232033320343203532036320373203832039320403204132042320433204432045320463204732048320493205032051320523205332054320553205632057320583205932060320613206232063320643206532066320673206832069320703207132072320733207432075320763207732078320793208032081320823208332084320853208632087320883208932090320913209232093320943209532096320973209832099321003210132102321033210432105321063210732108321093211032111321123211332114321153211632117321183211932120321213212232123321243212532126321273212832129321303213132132321333213432135321363213732138321393214032141321423214332144321453214632147321483214932150321513215232153321543215532156321573215832159321603216132162321633216432165321663216732168321693217032171321723217332174321753217632177321783217932180321813218232183321843218532186321873218832189321903219132192321933219432195321963219732198321993220032201322023220332204322053220632207322083220932210322113221232213322143221532216322173221832219322203222132222322233222432225322263222732228322293223032231322323223332234322353223632237322383223932240322413224232243322443224532246322473224832249322503225132252322533225432255322563225732258322593226032261322623226332264322653226632267322683226932270322713227232273322743227532276322773227832279322803228132282322833228432285322863228732288322893229032291322923229332294322953229632297322983229932300323013230232303323043230532306323073230832309323103231132312323133231432315323163231732318323193232032321323223232332324323253232632327323283232932330323313233232333323343233532336323373233832339323403234132342323433234432345323463234732348323493235032351323523235332354323553235632357323583235932360323613236232363323643236532366323673236832369323703237132372323733237432375323763237732378323793238032381323823238332384323853238632387323883238932390323913239232393323943239532396323973239832399324003240132402324033240432405324063240732408324093241032411324123241332414324153241632417324183241932420324213242232423324243242532426324273242832429324303243132432324333243432435324363243732438324393244032441324423244332444324453244632447324483244932450324513245232453324543245532456324573245832459324603246132462324633246432465324663246732468324693247032471324723247332474324753247632477324783247932480324813248232483324843248532486324873248832489324903249132492324933249432495324963249732498324993250032501325023250332504325053250632507325083250932510325113251232513325143251532516325173251832519325203252132522325233252432525325263252732528325293253032531325323253332534325353253632537325383253932540325413254232543325443254532546325473254832549325503255132552325533255432555325563255732558325593256032561325623256332564325653256632567325683256932570325713257232573325743257532576325773257832579325803258132582325833258432585325863258732588325893259032591325923259332594325953259632597325983259932600326013260232603326043260532606326073260832609326103261132612326133261432615326163261732618326193262032621326223262332624326253262632627326283262932630326313263232633326343263532636326373263832639326403264132642326433264432645326463264732648326493265032651326523265332654326553265632657326583265932660326613266232663326643266532666326673266832669326703267132672326733267432675326763267732678326793268032681326823268332684326853268632687326883268932690326913269232693326943269532696326973269832699327003270132702327033270432705327063270732708327093271032711327123271332714327153271632717327183271932720327213272232723327243272532726327273272832729327303273132732327333273432735327363273732738327393274032741327423274332744327453274632747327483274932750327513275232753327543275532756327573275832759327603276132762327633276432765327663276732768327693277032771327723277332774327753277632777327783277932780327813278232783327843278532786327873278832789327903279132792327933279432795327963279732798327993280032801328023280332804328053280632807328083280932810328113281232813328143281532816328173281832819328203282132822328233282432825328263282732828328293283032831328323283332834328353283632837328383283932840328413284232843328443284532846328473284832849328503285132852328533285432855328563285732858328593286032861328623286332864328653286632867328683286932870328713287232873328743287532876328773287832879328803288132882328833288432885328863288732888328893289032891328923289332894328953289632897328983289932900329013290232903329043290532906329073290832909329103291132912329133291432915329163291732918329193292032921329223292332924329253292632927329283292932930329313293232933329343293532936329373293832939329403294132942329433294432945329463294732948329493295032951329523295332954329553295632957329583295932960329613296232963329643296532966329673296832969329703297132972329733297432975329763297732978329793298032981329823298332984329853298632987329883298932990329913299232993329943299532996329973299832999330003300133002330033300433005330063300733008330093301033011330123301333014330153301633017330183301933020330213302233023330243302533026330273302833029330303303133032330333303433035330363303733038330393304033041330423304333044330453304633047330483304933050330513305233053330543305533056330573305833059330603306133062330633306433065330663306733068330693307033071330723307333074330753307633077330783307933080330813308233083330843308533086330873308833089330903309133092330933309433095330963309733098330993310033101331023310333104331053310633107331083310933110331113311233113331143311533116331173311833119331203312133122331233312433125331263312733128331293313033131331323313333134331353313633137331383313933140331413314233143331443314533146331473314833149331503315133152331533315433155331563315733158331593316033161331623316333164331653316633167331683316933170331713317233173331743317533176331773317833179331803318133182331833318433185331863318733188331893319033191331923319333194331953319633197331983319933200332013320233203332043320533206332073320833209332103321133212332133321433215332163321733218332193322033221332223322333224332253322633227332283322933230332313323233233332343323533236332373323833239332403324133242332433324433245332463324733248332493325033251332523325333254332553325633257332583325933260332613326233263332643326533266332673326833269332703327133272332733327433275332763327733278332793328033281332823328333284332853328633287332883328933290332913329233293332943329533296332973329833299333003330133302333033330433305333063330733308333093331033311333123331333314333153331633317333183331933320333213332233323333243332533326333273332833329333303333133332333333333433335333363333733338333393334033341333423334333344333453334633347333483334933350333513335233353333543335533356333573335833359333603336133362333633336433365333663336733368333693337033371333723337333374333753337633377333783337933380333813338233383333843338533386333873338833389333903339133392333933339433395333963339733398333993340033401334023340333404334053340633407334083340933410334113341233413334143341533416334173341833419334203342133422334233342433425334263342733428334293343033431334323343333434334353343633437334383343933440334413344233443334443344533446334473344833449334503345133452334533345433455334563345733458334593346033461334623346333464334653346633467334683346933470334713347233473334743347533476334773347833479334803348133482334833348433485334863348733488334893349033491334923349333494334953349633497334983349933500335013350233503335043350533506335073350833509335103351133512335133351433515335163351733518335193352033521335223352333524335253352633527335283352933530335313353233533335343353533536335373353833539335403354133542335433354433545335463354733548335493355033551335523355333554335553355633557335583355933560335613356233563335643356533566335673356833569335703357133572335733357433575335763357733578335793358033581335823358333584335853358633587335883358933590335913359233593335943359533596335973359833599336003360133602336033360433605336063360733608336093361033611336123361333614336153361633617336183361933620336213362233623336243362533626336273362833629336303363133632336333363433635336363363733638336393364033641336423364333644336453364633647336483364933650336513365233653336543365533656336573365833659336603366133662336633366433665336663366733668336693367033671336723367333674336753367633677336783367933680336813368233683336843368533686336873368833689336903369133692336933369433695336963369733698336993370033701337023370333704337053370633707337083370933710337113371233713337143371533716337173371833719337203372133722337233372433725337263372733728337293373033731337323373333734337353373633737337383373933740337413374233743337443374533746337473374833749337503375133752337533375433755337563375733758337593376033761337623376333764337653376633767337683376933770337713377233773337743377533776337773377833779337803378133782337833378433785337863378733788337893379033791337923379333794337953379633797337983379933800338013380233803338043380533806338073380833809338103381133812338133381433815338163381733818338193382033821338223382333824338253382633827338283382933830338313383233833338343383533836338373383833839338403384133842338433384433845338463384733848338493385033851338523385333854338553385633857338583385933860338613386233863338643386533866338673386833869338703387133872338733387433875338763387733878338793388033881338823388333884338853388633887338883388933890338913389233893338943389533896338973389833899339003390133902339033390433905339063390733908339093391033911339123391333914339153391633917339183391933920339213392233923339243392533926339273392833929339303393133932339333393433935339363393733938339393394033941339423394333944339453394633947339483394933950339513395233953339543395533956339573395833959339603396133962339633396433965339663396733968339693397033971339723397333974339753397633977339783397933980339813398233983339843398533986339873398833989339903399133992339933399433995339963399733998339993400034001340023400334004340053400634007340083400934010340113401234013340143401534016340173401834019340203402134022340233402434025340263402734028340293403034031340323403334034340353403634037340383403934040340413404234043340443404534046340473404834049340503405134052340533405434055340563405734058340593406034061340623406334064340653406634067340683406934070340713407234073340743407534076340773407834079340803408134082340833408434085340863408734088340893409034091340923409334094340953409634097340983409934100341013410234103341043410534106341073410834109341103411134112341133411434115341163411734118341193412034121341223412334124341253412634127341283412934130341313413234133341343413534136341373413834139341403414134142341433414434145341463414734148341493415034151341523415334154341553415634157341583415934160341613416234163341643416534166341673416834169341703417134172341733417434175341763417734178341793418034181341823418334184341853418634187341883418934190341913419234193341943419534196341973419834199342003420134202342033420434205342063420734208342093421034211342123421334214342153421634217342183421934220342213422234223342243422534226342273422834229342303423134232342333423434235342363423734238342393424034241342423424334244342453424634247342483424934250342513425234253342543425534256342573425834259342603426134262342633426434265342663426734268342693427034271342723427334274342753427634277342783427934280342813428234283342843428534286342873428834289342903429134292342933429434295342963429734298342993430034301343023430334304343053430634307343083430934310343113431234313343143431534316343173431834319343203432134322343233432434325343263432734328343293433034331343323433334334343353433634337343383433934340343413434234343343443434534346343473434834349343503435134352343533435434355343563435734358343593436034361343623436334364343653436634367343683436934370343713437234373343743437534376343773437834379343803438134382343833438434385343863438734388343893439034391343923439334394343953439634397343983439934400344013440234403344043440534406344073440834409344103441134412344133441434415344163441734418344193442034421344223442334424344253442634427344283442934430344313443234433344343443534436344373443834439344403444134442344433444434445344463444734448344493445034451344523445334454344553445634457344583445934460344613446234463344643446534466344673446834469344703447134472344733447434475344763447734478344793448034481344823448334484344853448634487344883448934490344913449234493344943449534496344973449834499345003450134502345033450434505345063450734508345093451034511345123451334514345153451634517345183451934520345213452234523345243452534526345273452834529345303453134532345333453434535345363453734538345393454034541345423454334544345453454634547345483454934550345513455234553345543455534556345573455834559345603456134562345633456434565345663456734568345693457034571345723457334574345753457634577345783457934580345813458234583345843458534586345873458834589345903459134592345933459434595345963459734598345993460034601346023460334604346053460634607346083460934610346113461234613346143461534616346173461834619346203462134622346233462434625346263462734628346293463034631346323463334634346353463634637346383463934640346413464234643346443464534646346473464834649346503465134652346533465434655346563465734658346593466034661346623466334664346653466634667346683466934670346713467234673346743467534676346773467834679346803468134682346833468434685346863468734688346893469034691346923469334694346953469634697346983469934700347013470234703347043470534706347073470834709347103471134712347133471434715347163471734718347193472034721347223472334724347253472634727347283472934730347313473234733347343473534736347373473834739347403474134742347433474434745347463474734748347493475034751347523475334754347553475634757347583475934760347613476234763347643476534766347673476834769347703477134772347733477434775347763477734778347793478034781347823478334784347853478634787347883478934790347913479234793347943479534796347973479834799348003480134802348033480434805348063480734808348093481034811348123481334814348153481634817348183481934820348213482234823348243482534826348273482834829348303483134832348333483434835348363483734838348393484034841348423484334844348453484634847348483484934850348513485234853348543485534856348573485834859348603486134862348633486434865348663486734868348693487034871348723487334874348753487634877348783487934880348813488234883348843488534886348873488834889348903489134892348933489434895348963489734898348993490034901349023490334904349053490634907349083490934910349113491234913349143491534916349173491834919349203492134922349233492434925349263492734928349293493034931349323493334934349353493634937349383493934940349413494234943349443494534946349473494834949349503495134952349533495434955349563495734958349593496034961349623496334964349653496634967349683496934970349713497234973349743497534976349773497834979349803498134982349833498434985349863498734988349893499034991349923499334994349953499634997349983499935000350013500235003350043500535006350073500835009350103501135012350133501435015350163501735018350193502035021350223502335024350253502635027350283502935030350313503235033350343503535036350373503835039350403504135042350433504435045350463504735048350493505035051350523505335054350553505635057350583505935060350613506235063350643506535066350673506835069350703507135072350733507435075350763507735078350793508035081350823508335084350853508635087350883508935090350913509235093350943509535096350973509835099351003510135102351033510435105351063510735108351093511035111351123511335114351153511635117351183511935120351213512235123351243512535126351273512835129351303513135132351333513435135351363513735138351393514035141351423514335144351453514635147351483514935150351513515235153351543515535156351573515835159351603516135162351633516435165351663516735168351693517035171351723517335174351753517635177351783517935180351813518235183351843518535186351873518835189351903519135192351933519435195351963519735198351993520035201352023520335204352053520635207352083520935210352113521235213352143521535216352173521835219352203522135222352233522435225352263522735228352293523035231352323523335234352353523635237352383523935240352413524235243352443524535246352473524835249352503525135252352533525435255352563525735258352593526035261352623526335264352653526635267352683526935270352713527235273352743527535276352773527835279352803528135282352833528435285352863528735288352893529035291352923529335294352953529635297352983529935300353013530235303353043530535306353073530835309353103531135312353133531435315353163531735318353193532035321353223532335324353253532635327353283532935330353313533235333353343533535336353373533835339353403534135342353433534435345353463534735348353493535035351353523535335354353553535635357353583535935360353613536235363353643536535366353673536835369353703537135372353733537435375353763537735378353793538035381353823538335384353853538635387353883538935390353913539235393353943539535396353973539835399354003540135402354033540435405354063540735408354093541035411354123541335414354153541635417354183541935420354213542235423354243542535426354273542835429354303543135432354333543435435354363543735438354393544035441354423544335444354453544635447354483544935450354513545235453354543545535456354573545835459354603546135462354633546435465354663546735468354693547035471354723547335474354753547635477354783547935480354813548235483354843548535486354873548835489354903549135492354933549435495354963549735498354993550035501355023550335504355053550635507355083550935510355113551235513355143551535516355173551835519355203552135522355233552435525355263552735528355293553035531355323553335534355353553635537355383553935540355413554235543355443554535546355473554835549355503555135552355533555435555355563555735558355593556035561355623556335564355653556635567355683556935570355713557235573355743557535576355773557835579355803558135582355833558435585355863558735588355893559035591355923559335594355953559635597355983559935600356013560235603356043560535606356073560835609356103561135612356133561435615356163561735618356193562035621356223562335624356253562635627356283562935630356313563235633356343563535636356373563835639356403564135642356433564435645356463564735648356493565035651356523565335654356553565635657356583565935660356613566235663356643566535666356673566835669356703567135672356733567435675356763567735678356793568035681356823568335684356853568635687356883568935690356913569235693356943569535696356973569835699357003570135702357033570435705357063570735708357093571035711357123571335714357153571635717357183571935720357213572235723357243572535726357273572835729357303573135732357333573435735357363573735738357393574035741357423574335744357453574635747357483574935750357513575235753357543575535756357573575835759357603576135762357633576435765357663576735768357693577035771357723577335774357753577635777357783577935780357813578235783357843578535786357873578835789357903579135792357933579435795357963579735798357993580035801358023580335804358053580635807358083580935810358113581235813358143581535816358173581835819358203582135822358233582435825358263582735828358293583035831358323583335834358353583635837358383583935840358413584235843358443584535846358473584835849358503585135852358533585435855358563585735858358593586035861358623586335864358653586635867358683586935870358713587235873358743587535876358773587835879358803588135882358833588435885
  1. /*!
  2. * elFinder - file manager for web
  3. * Version 2.1.57 (2020-06-05)
  4. * http://elfinder.org
  5. *
  6. * Copyright 2009-2020, Studio 42
  7. * Licensed under a 3-clauses BSD license
  8. */
  9. (function(root, factory) {
  10. if (typeof define === 'function' && define.amd) {
  11. // AMD
  12. define(['jquery','jquery-ui'], factory);
  13. } else if (typeof exports !== 'undefined') {
  14. // CommonJS
  15. var $, ui;
  16. try {
  17. $ = require('jquery');
  18. ui = require('jquery-ui');
  19. } catch (e) {}
  20. module.exports = factory($, ui);
  21. } else {
  22. // Browser globals (Note: root is window)
  23. factory(root.jQuery, root.jQuery.ui, true);
  24. }
  25. }(this, function($, _ui, toGlobal) {
  26. toGlobal = toGlobal || false;
  27. /*
  28. * File: /js/elFinder.js
  29. */
  30. /**
  31. * @class elFinder - file manager for web
  32. *
  33. * @author Dmitry (dio) Levashov
  34. **/
  35. var elFinder = function(elm, opts, bootCallback) {
  36. //this.time('load');
  37. var self = this,
  38. /**
  39. * Objects array of jQuery.Deferred that calls before elFinder boot up
  40. *
  41. * @type Array
  42. */
  43. dfrdsBeforeBootup = [],
  44. /**
  45. * Plugin name to check for conflicts with bootstrap etc
  46. *
  47. * @type Array
  48. **/
  49. conflictChecks = ['button', 'tooltip'],
  50. /**
  51. * Node on which elfinder creating
  52. *
  53. * @type jQuery
  54. **/
  55. node = $(elm),
  56. /**
  57. * Object of events originally registered in this node
  58. *
  59. * @type Object
  60. */
  61. prevEvents = $.extend(true, {}, $._data(node.get(0), 'events')),
  62. /**
  63. * Store node contents.
  64. *
  65. * @see this.destroy
  66. * @type jQuery
  67. **/
  68. prevContent = $('<div></div>').append(node.contents()).attr('class', node.attr('class') || '').attr('style', node.attr('style') || ''),
  69. /**
  70. * Instance ID. Required to get/set cookie
  71. *
  72. * @type String
  73. **/
  74. id = node.attr('id') || node.attr('id', 'elfauto' + $('.elfinder').length).attr('id'),
  75. /**
  76. * Events namespace
  77. *
  78. * @type String
  79. **/
  80. namespace = 'elfinder-' + id,
  81. /**
  82. * Mousedown event
  83. *
  84. * @type String
  85. **/
  86. mousedown = 'mousedown.'+namespace,
  87. /**
  88. * Keydown event
  89. *
  90. * @type String
  91. **/
  92. keydown = 'keydown.'+namespace,
  93. /**
  94. * Keypress event
  95. *
  96. * @type String
  97. **/
  98. keypress = 'keypress.'+namespace,
  99. /**
  100. * Keypup event
  101. *
  102. * @type String
  103. **/
  104. keyup = 'keyup.'+namespace,
  105. /**
  106. * Is shortcuts/commands enabled
  107. *
  108. * @type Boolean
  109. **/
  110. enabled = false,
  111. /**
  112. * Store enabled value before ajax request
  113. *
  114. * @type Boolean
  115. **/
  116. prevEnabled = false,
  117. /**
  118. * List of build-in events which mapped into methods with same names
  119. *
  120. * @type Array
  121. **/
  122. events = ['enable', 'disable', 'load', 'open', 'reload', 'select', 'add', 'remove', 'change', 'dblclick', 'getfile', 'lockfiles', 'unlockfiles', 'selectfiles', 'unselectfiles', 'dragstart', 'dragstop', 'search', 'searchend', 'viewchange'],
  123. /**
  124. * Rules to validate data from backend
  125. *
  126. * @type Object
  127. **/
  128. rules = {},
  129. /**
  130. * Current working directory hash
  131. *
  132. * @type String
  133. **/
  134. cwd = '',
  135. /**
  136. * Current working directory options default
  137. *
  138. * @type Object
  139. **/
  140. cwdOptionsDefault = {
  141. path : '',
  142. url : '',
  143. tmbUrl : '',
  144. disabled : [],
  145. separator : '/',
  146. archives : [],
  147. extract : [],
  148. copyOverwrite : true,
  149. uploadOverwrite : true,
  150. uploadMaxSize : 0,
  151. jpgQuality : 100,
  152. tmbCrop : false,
  153. tmbReqCustomData : false,
  154. tmb : false // old API
  155. },
  156. /**
  157. * Current working directory options
  158. *
  159. * @type Object
  160. **/
  161. cwdOptions = {},
  162. /**
  163. * Files/dirs cache
  164. *
  165. * @type Object
  166. **/
  167. files = {},
  168. /**
  169. * Hidden Files/dirs cache
  170. *
  171. * @type Object
  172. **/
  173. hiddenFiles = {},
  174. /**
  175. * Files/dirs hash cache of each dirs
  176. *
  177. * @type Object
  178. **/
  179. ownFiles = {},
  180. /**
  181. * Selected files hashes
  182. *
  183. * @type Array
  184. **/
  185. selected = [],
  186. /**
  187. * Events listeners
  188. *
  189. * @type Object
  190. **/
  191. listeners = {},
  192. /**
  193. * Shortcuts
  194. *
  195. * @type Object
  196. **/
  197. shortcuts = {},
  198. /**
  199. * Buffer for copied files
  200. *
  201. * @type Array
  202. **/
  203. clipboard = [],
  204. /**
  205. * Copied/cuted files hashes
  206. * Prevent from remove its from cache.
  207. * Required for dispaly correct files names in error messages
  208. *
  209. * @type Object
  210. **/
  211. remember = {},
  212. /**
  213. * Queue for 'open' requests
  214. *
  215. * @type Array
  216. **/
  217. queue = [],
  218. /**
  219. * Queue for only cwd requests e.g. `tmb`
  220. *
  221. * @type Array
  222. **/
  223. cwdQueue = [],
  224. /**
  225. * Commands prototype
  226. *
  227. * @type Object
  228. **/
  229. base = new self.command(self),
  230. /**
  231. * elFinder node width
  232. *
  233. * @type String
  234. * @default "auto"
  235. **/
  236. width = 'auto',
  237. /**
  238. * elFinder node height
  239. * Number: pixcel or String: Number + "%"
  240. *
  241. * @type Number | String
  242. * @default 400
  243. **/
  244. height = 400,
  245. /**
  246. * Base node object or selector
  247. * Element which is the reference of the height percentage
  248. *
  249. * @type Object|String
  250. * @default null | $(window) (if height is percentage)
  251. **/
  252. heightBase = null,
  253. /**
  254. * MIME type list(Associative array) handled as a text file
  255. *
  256. * @type Object|null
  257. */
  258. textMimes = null,
  259. /**
  260. * elfinder path for sound played on remove
  261. * @type String
  262. * @default ./sounds/
  263. **/
  264. soundPath = 'sounds/',
  265. /**
  266. * JSON.stringify of previous fm.sorters
  267. * @type String
  268. */
  269. prevSorterStr = '',
  270. /**
  271. * Map table of file extention to MIME-Type
  272. * @type Object
  273. */
  274. extToMimeTable,
  275. /**
  276. * Disabled page unload function
  277. * @type Boolean
  278. */
  279. diableUnloadCheck = false,
  280. beeper = $(document.createElement('audio')).hide().appendTo('body')[0],
  281. syncInterval,
  282. autoSyncStop = 0,
  283. uiCmdMapPrev = '',
  284. gcJobRes = null,
  285. open = function(data) {
  286. // NOTES: Do not touch data object
  287. var volumeid, contextmenu, emptyDirs = {}, stayDirs = {},
  288. rmClass, hashes, calc, gc, collapsed, prevcwd, sorterStr, diff;
  289. if (self.api >= 2.1) {
  290. // support volume driver option `uiCmdMap`
  291. self.commandMap = (data.options.uiCmdMap && Object.keys(data.options.uiCmdMap).length)? data.options.uiCmdMap : {};
  292. if (uiCmdMapPrev !== JSON.stringify(self.commandMap)) {
  293. uiCmdMapPrev = JSON.stringify(self.commandMap);
  294. }
  295. } else {
  296. self.options.sync = 0;
  297. }
  298. if (data.init) {
  299. // init - reset cache
  300. files = {};
  301. ownFiles = {};
  302. } else {
  303. // remove only files from prev cwd
  304. // and collapsed directory (included 100+ directories) to empty for perfomance tune in DnD
  305. prevcwd = cwd;
  306. rmClass = 'elfinder-subtree-loaded ' + self.res('class', 'navexpand');
  307. collapsed = self.res('class', 'navcollapse');
  308. hashes = Object.keys(files);
  309. calc = function(i) {
  310. if (!files[i]) {
  311. return true;
  312. }
  313. var isDir = (files[i].mime === 'directory'),
  314. phash = files[i].phash,
  315. pnav;
  316. if (
  317. (!isDir
  318. || emptyDirs[phash]
  319. || (!stayDirs[phash]
  320. && self.navHash2Elm(files[i].hash).is(':hidden')
  321. && self.navHash2Elm(phash).next('.elfinder-navbar-subtree').children().length > 100
  322. )
  323. )
  324. && (isDir || phash !== cwd)
  325. && ! remember[i]
  326. ) {
  327. if (isDir && !emptyDirs[phash]) {
  328. emptyDirs[phash] = true;
  329. self.navHash2Elm(phash)
  330. .removeClass(rmClass)
  331. .next('.elfinder-navbar-subtree').empty();
  332. }
  333. deleteCache(files[i]);
  334. } else if (isDir) {
  335. stayDirs[phash] = true;
  336. }
  337. };
  338. gc = function() {
  339. if (hashes.length) {
  340. gcJobRes && gcJobRes._abort();
  341. gcJobRes = self.asyncJob(calc, hashes, {
  342. interval : 20,
  343. numPerOnce : 100
  344. }).done(function() {
  345. var hd = self.storage('hide') || {items: {}};
  346. if (Object.keys(hiddenFiles).length) {
  347. $.each(hiddenFiles, function(h) {
  348. if (!hd.items[h]) {
  349. delete hiddenFiles[h];
  350. }
  351. });
  352. }
  353. });
  354. }
  355. };
  356. self.trigger('filesgc').one('filesgc', function() {
  357. hashes = [];
  358. });
  359. self.one('opendone', function() {
  360. if (prevcwd !== cwd) {
  361. if (! node.data('lazycnt')) {
  362. gc();
  363. } else {
  364. self.one('lazydone', gc);
  365. }
  366. }
  367. });
  368. }
  369. self.sorters = {};
  370. cwd = data.cwd.hash;
  371. cache(data.files);
  372. if (!files[cwd]) {
  373. cache([data.cwd]);
  374. } else {
  375. diff = self.diff([data.cwd], true);
  376. if (diff.changed.length) {
  377. cache(diff.changed, 'change');
  378. self.change({changed: diff.changed});
  379. }
  380. }
  381. data.changed && data.changed.length && cache(data.changed, 'change');
  382. // trigger event 'sorterupdate'
  383. sorterStr = JSON.stringify(self.sorters);
  384. if (prevSorterStr !== sorterStr) {
  385. self.trigger('sorterupdate');
  386. prevSorterStr = sorterStr;
  387. }
  388. self.lastDir(cwd);
  389. self.autoSync();
  390. },
  391. /**
  392. * Store info about files/dirs in "files" object.
  393. *
  394. * @param Array files
  395. * @param String data type
  396. * @return void
  397. **/
  398. cache = function(data, type) {
  399. var type = type || 'files',
  400. keeps = ['sizeInfo', 'encoding'],
  401. defsorter = { name: true, perm: true, date: true, size: true, kind: true },
  402. sorterChk = !self.sorters._checked && (type === 'files'),
  403. l = data.length,
  404. setSorter = function(file) {
  405. var f = file || {},
  406. sorters = [];
  407. $.each(self.sortRules, function(key) {
  408. if (defsorter[key] || typeof f[key] !== 'undefined' || (key === 'mode' && typeof f.perm !== 'undefined')) {
  409. sorters.push(key);
  410. }
  411. });
  412. self.sorters = self.arrayFlip(sorters, true);
  413. self.sorters._checked = true;
  414. },
  415. changedParents = {},
  416. hideData = self.storage('hide') || {},
  417. hides = hideData.items || {},
  418. f, i, i1, keepProp, parents, hidden;
  419. for (i = 0; i < l; i++) {
  420. f = Object.assign({}, data[i]);
  421. hidden = (!hideData.show && hides[f.hash])? true : false;
  422. if (f.name && f.hash && f.mime) {
  423. if (!hidden) {
  424. if (sorterChk && f.phash === cwd) {
  425. setSorter(f);
  426. sorterChk = false;
  427. }
  428. if (f.phash && (type === 'add' || (type === 'change' && (!files[f.hash] || f.size !== files[f.hash])))) {
  429. if (parents = self.parents(f.phash)) {
  430. $.each(parents, function() {
  431. changedParents[this] = true;
  432. });
  433. }
  434. }
  435. }
  436. if (files[f.hash]) {
  437. for (i1 =0; i1 < keeps.length; i1++) {
  438. if(files[f.hash][keeps[i1]] && ! f[keeps[i1]]) {
  439. f[keeps[i1]] = files[f.hash][keeps[i1]];
  440. }
  441. }
  442. if (f.sizeInfo && !f.size) {
  443. f.size = f.sizeInfo.size;
  444. }
  445. deleteCache(files[f.hash], true);
  446. }
  447. if (hides[f.hash]) {
  448. hiddenFiles[f.hash] = f;
  449. }
  450. if (hidden) {
  451. l--;
  452. data.splice(i--, 1);
  453. } else {
  454. files[f.hash] = f;
  455. if (f.mime === 'directory' && !ownFiles[f.hash]) {
  456. ownFiles[f.hash] = {};
  457. }
  458. if (f.phash) {
  459. if (!ownFiles[f.phash]) {
  460. ownFiles[f.phash] = {};
  461. }
  462. ownFiles[f.phash][f.hash] = true;
  463. }
  464. }
  465. }
  466. }
  467. // delete sizeInfo cache
  468. $.each(Object.keys(changedParents), function() {
  469. var target = files[this];
  470. if (target && target.sizeInfo) {
  471. delete target.sizeInfo;
  472. }
  473. });
  474. // for empty folder
  475. sorterChk && setSorter();
  476. },
  477. /**
  478. * Delete file object from files caches
  479. *
  480. * @param Array removed hashes
  481. * @return void
  482. */
  483. remove = function(removed) {
  484. var l = removed.length,
  485. roots = {},
  486. rm = function(hash) {
  487. var file = files[hash], i;
  488. if (file) {
  489. if (file.mime === 'directory') {
  490. if (roots[hash]) {
  491. delete self.roots[roots[hash]];
  492. }
  493. // restore stats of deleted root parent directory
  494. $.each(self.leafRoots, function(phash, roots) {
  495. var idx, pdir;
  496. if ((idx = $.inArray(hash, roots))!== -1) {
  497. if (roots.length === 1) {
  498. if ((pdir = Object.assign({}, files[phash])) && pdir._realStats) {
  499. $.each(pdir._realStats, function(k, v) {
  500. pdir[k] = v;
  501. });
  502. remove(files[phash]._realStats);
  503. self.change({ changed: [pdir] });
  504. }
  505. delete self.leafRoots[phash];
  506. } else {
  507. self.leafRoots[phash].splice(idx, 1);
  508. }
  509. }
  510. });
  511. if (self.searchStatus.state < 2) {
  512. $.each(files, function(h, f) {
  513. f.phash == hash && rm(h);
  514. });
  515. }
  516. }
  517. if (file.phash) {
  518. if (parents = self.parents(file.phash)) {
  519. $.each(parents, function() {
  520. changedParents[this] = true;
  521. });
  522. }
  523. }
  524. deleteCache(files[hash]);
  525. }
  526. },
  527. changedParents = {},
  528. parents;
  529. $.each(self.roots, function(k, v) {
  530. roots[v] = k;
  531. });
  532. while (l--) {
  533. rm(removed[l]);
  534. }
  535. // delete sizeInfo cache
  536. $.each(Object.keys(changedParents), function() {
  537. var target = files[this];
  538. if (target && target.sizeInfo) {
  539. delete target.sizeInfo;
  540. }
  541. });
  542. },
  543. /**
  544. * Update file object in files caches
  545. *
  546. * @param Array changed file objects
  547. * @return void
  548. * @deprecated should be use `cache(updatesArrayData, 'change');`
  549. */
  550. change = function(changed) {
  551. $.each(changed, function(i, file) {
  552. var hash = file.hash;
  553. if (files[hash]) {
  554. $.each(Object.keys(files[hash]), function(i, v){
  555. if (typeof file[v] === 'undefined') {
  556. delete files[hash][v];
  557. }
  558. });
  559. }
  560. files[hash] = files[hash] ? Object.assign(files[hash], file) : file;
  561. });
  562. },
  563. /**
  564. * Delete cache data of files, ownFiles and self.optionsByHashes
  565. *
  566. * @param Object file
  567. * @param Boolean update
  568. * @return void
  569. */
  570. deleteCache = function(file, update) {
  571. var hash = file.hash,
  572. phash = file.phash;
  573. if (phash && ownFiles[phash]) {
  574. delete ownFiles[phash][hash];
  575. }
  576. if (!update) {
  577. ownFiles[hash] && delete ownFiles[hash];
  578. self.optionsByHashes[hash] && delete self.optionsByHashes[hash];
  579. }
  580. delete files[hash];
  581. },
  582. /**
  583. * Maximum number of concurrent connections on request
  584. *
  585. * @type Number
  586. */
  587. requestMaxConn,
  588. /**
  589. * Current number of connections
  590. *
  591. * @type Number
  592. */
  593. requestCnt = 0,
  594. /**
  595. * Queue waiting for connection
  596. *
  597. * @type Array
  598. */
  599. requestQueue = [],
  600. /**
  601. * Current open command instance
  602. *
  603. * @type Object
  604. */
  605. currentOpenCmd = null,
  606. /**
  607. * Exec shortcut
  608. *
  609. * @param jQuery.Event keydown/keypress event
  610. * @return void
  611. */
  612. execShortcut = function(e) {
  613. var code = e.keyCode,
  614. ctrlKey = !!(e.ctrlKey || e.metaKey),
  615. isMousedown = e.type === 'mousedown',
  616. ddm;
  617. !isMousedown && (self.keyState.keyCode = code);
  618. self.keyState.ctrlKey = ctrlKey;
  619. self.keyState.shiftKey = e.shiftKey;
  620. self.keyState.metaKey = e.metaKey;
  621. self.keyState.altKey = e.altKey;
  622. if (isMousedown) {
  623. return;
  624. } else if (e.type === 'keyup') {
  625. self.keyState.keyCode = null;
  626. return;
  627. }
  628. if (enabled) {
  629. $.each(shortcuts, function(i, shortcut) {
  630. if (shortcut.type == e.type
  631. && shortcut.keyCode == code
  632. && shortcut.shiftKey == e.shiftKey
  633. && shortcut.ctrlKey == ctrlKey
  634. && shortcut.altKey == e.altKey) {
  635. e.preventDefault();
  636. e.stopPropagation();
  637. shortcut.callback(e, self);
  638. self.debug('shortcut-exec', i+' : '+shortcut.description);
  639. }
  640. });
  641. // prevent tab out of elfinder
  642. if (code == $.ui.keyCode.TAB && !$(e.target).is(':input')) {
  643. e.preventDefault();
  644. }
  645. // cancel any actions by [Esc] key
  646. if (e.type === 'keydown' && code == $.ui.keyCode.ESCAPE) {
  647. // copy or cut
  648. if (! node.find('.ui-widget:visible').length) {
  649. self.clipboard().length && self.clipboard([]);
  650. }
  651. // dragging
  652. if ($.ui.ddmanager) {
  653. ddm = $.ui.ddmanager.current;
  654. ddm && ddm.helper && ddm.cancel();
  655. }
  656. // button menus
  657. self.toHide(node.find('.ui-widget.elfinder-button-menu.elfinder-frontmost:visible'));
  658. // trigger keydownEsc
  659. self.trigger('keydownEsc', e);
  660. }
  661. }
  662. },
  663. date = new Date(),
  664. utc,
  665. i18n,
  666. inFrame = (window.parent !== window),
  667. parentIframe = (function() {
  668. var pifm, ifms;
  669. if (inFrame) {
  670. try {
  671. ifms = $('iframe', window.parent.document);
  672. if (ifms.length) {
  673. $.each(ifms, function(i, ifm) {
  674. if (ifm.contentWindow === window) {
  675. pifm = $(ifm);
  676. return false;
  677. }
  678. });
  679. }
  680. } catch(e) {}
  681. }
  682. return pifm;
  683. })(),
  684. /**
  685. * elFinder boot up function
  686. *
  687. * @type Function
  688. */
  689. bootUp,
  690. /**
  691. * Original function of XMLHttpRequest.prototype.send
  692. *
  693. * @type Function
  694. */
  695. savedXhrSend;
  696. // opts must be an object
  697. if (!opts) {
  698. opts = {};
  699. }
  700. // set UA.Angle, UA.Rotated for mobile devices
  701. if (self.UA.Mobile) {
  702. $(window).on('orientationchange.'+namespace, function() {
  703. var a = ((screen && screen.orientation && screen.orientation.angle) || window.orientation || 0) + 0;
  704. if (a === -90) {
  705. a = 270;
  706. }
  707. self.UA.Angle = a;
  708. self.UA.Rotated = a % 180 === 0? false : true;
  709. }).trigger('orientationchange.'+namespace);
  710. }
  711. // check opt.bootCallback
  712. if (opts.bootCallback && typeof opts.bootCallback === 'function') {
  713. (function() {
  714. var func = bootCallback,
  715. opFunc = opts.bootCallback;
  716. bootCallback = function(fm, extraObj) {
  717. func && typeof func === 'function' && func.call(this, fm, extraObj);
  718. opFunc.call(this, fm, extraObj);
  719. };
  720. })();
  721. }
  722. delete opts.bootCallback;
  723. /**
  724. * Protocol version
  725. *
  726. * @type String
  727. **/
  728. this.api = null;
  729. /**
  730. * elFinder use new api
  731. *
  732. * @type Boolean
  733. **/
  734. this.newAPI = false;
  735. /**
  736. * elFinder use old api
  737. *
  738. * @type Boolean
  739. **/
  740. this.oldAPI = false;
  741. /**
  742. * Net drivers names
  743. *
  744. * @type Array
  745. **/
  746. this.netDrivers = [];
  747. /**
  748. * Base URL of elfFinder library starting from Manager HTML
  749. *
  750. * @type String
  751. */
  752. this.baseUrl = '';
  753. /**
  754. * Base URL of i18n js files
  755. * baseUrl + "js/i18n/" when empty value
  756. *
  757. * @type String
  758. */
  759. this.i18nBaseUrl = '';
  760. /**
  761. * Is elFinder CSS loaded
  762. *
  763. * @type Boolean
  764. */
  765. this.cssloaded = false;
  766. /**
  767. * Current theme object
  768. *
  769. * @type Object|Null
  770. */
  771. this.theme = null;
  772. this.mimesCanMakeEmpty = {};
  773. /**
  774. * Callback function at boot up that option specified at elFinder starting
  775. *
  776. * @type Function
  777. */
  778. this.bootCallback;
  779. /**
  780. * Callback function at reload(restart) elFinder
  781. *
  782. * @type Function
  783. */
  784. this.reloadCallback;
  785. /**
  786. * ID. Required to create unique cookie name
  787. *
  788. * @type String
  789. **/
  790. this.id = id;
  791. /**
  792. * Method to store/fetch data
  793. *
  794. * @type Function
  795. **/
  796. this.storage = (function() {
  797. try {
  798. if ('localStorage' in window && window.localStorage !== null) {
  799. if (self.UA.Safari) {
  800. // check for Mac/iOS safari private browsing mode
  801. window.localStorage.setItem('elfstoragecheck', 1);
  802. window.localStorage.removeItem('elfstoragecheck');
  803. }
  804. return self.localStorage;
  805. } else {
  806. return self.cookie;
  807. }
  808. } catch (e) {
  809. return self.cookie;
  810. }
  811. })();
  812. /**
  813. * Set pause page unload check function or Get state
  814. *
  815. * @param Boolean state To set state
  816. * @param Boolean keep Keep disabled
  817. * @return Boolean|void
  818. */
  819. this.pauseUnloadCheck = function(state, keep) {
  820. if (typeof state === 'undefined') {
  821. return diableUnloadCheck;
  822. } else {
  823. diableUnloadCheck = !!state;
  824. if (state && !keep) {
  825. requestAnimationFrame(function() {
  826. diableUnloadCheck = false;
  827. });
  828. }
  829. }
  830. };
  831. /**
  832. * Configuration options
  833. *
  834. * @type Object
  835. **/
  836. //this.options = $.extend(true, {}, this._options, opts);
  837. this.options = Object.assign({}, this._options);
  838. // for old type configuration
  839. if (opts.uiOptions) {
  840. if (opts.uiOptions.toolbar && Array.isArray(opts.uiOptions.toolbar)) {
  841. if ($.isPlainObject(opts.uiOptions.toolbar[opts.uiOptions.toolbar.length - 1])) {
  842. self.options.uiOptions.toolbarExtra = Object.assign(self.options.uiOptions.toolbarExtra || {}, opts.uiOptions.toolbar.pop());
  843. }
  844. }
  845. }
  846. // Overwrite if opts value is an array
  847. (function() {
  848. var arrOv = function(obj, base) {
  849. if ($.isPlainObject(obj)) {
  850. $.each(obj, function(k, v) {
  851. if ($.isPlainObject(v)) {
  852. if (!base[k]) {
  853. base[k] = {};
  854. }
  855. arrOv(v, base[k]);
  856. } else {
  857. base[k] = v;
  858. }
  859. });
  860. }
  861. };
  862. arrOv(opts, self.options);
  863. })();
  864. // join toolbarExtra to toolbar
  865. this.options.uiOptions.toolbar.push(this.options.uiOptions.toolbarExtra);
  866. delete this.options.uiOptions.toolbarExtra;
  867. /**
  868. * Arrays that has to unbind events
  869. *
  870. * @type Object
  871. */
  872. this.toUnbindEvents = {};
  873. /**
  874. * Attach listener to events
  875. * To bind to multiply events at once, separate events names by space
  876. *
  877. * @param String event(s) name(s)
  878. * @param Object event handler or {done: handler}
  879. * @param Boolean priority first
  880. * @return elFinder
  881. */
  882. this.bind = function(event, callback, priorityFirst) {
  883. var i, len;
  884. if (callback && (typeof callback === 'function' || typeof callback.done === 'function')) {
  885. event = ('' + event).toLowerCase().replace(/^\s+|\s+$/g, '').split(/\s+/);
  886. len = event.length;
  887. for (i = 0; i < len; i++) {
  888. if (listeners[event[i]] === void(0)) {
  889. listeners[event[i]] = [];
  890. }
  891. listeners[event[i]][priorityFirst? 'unshift' : 'push'](callback);
  892. }
  893. }
  894. return this;
  895. };
  896. /**
  897. * Remove event listener if exists
  898. * To un-bind to multiply events at once, separate events names by space
  899. *
  900. * @param String event(s) name(s)
  901. * @param Function callback
  902. * @return elFinder
  903. */
  904. this.unbind = function(event, callback) {
  905. var i, len, l, ci;
  906. event = ('' + event).toLowerCase().split(/\s+/);
  907. len = event.length;
  908. for (i = 0; i < len; i++) {
  909. if (l = listeners[event[i]]) {
  910. ci = $.inArray(callback, l);
  911. ci > -1 && l.splice(ci, 1);
  912. }
  913. }
  914. callback = null;
  915. return this;
  916. };
  917. /**
  918. * Fire event - send notification to all event listeners
  919. * In the callback `this` becames an event object
  920. *
  921. * @param String event type
  922. * @param Object data to send across event
  923. * @param Boolean allow modify data (call by reference of data) default: true
  924. * @return elFinder
  925. */
  926. this.trigger = function(evType, data, allowModify) {
  927. var type = evType.toLowerCase(),
  928. isopen = (type === 'open'),
  929. dataIsObj = (typeof data === 'object'),
  930. handlers = listeners[type] || [],
  931. dones = [],
  932. i, l, jst, event;
  933. this.debug('event-'+type, data);
  934. if (! dataIsObj || typeof allowModify === 'undefined') {
  935. allowModify = true;
  936. }
  937. if (l = handlers.length) {
  938. event = $.Event(type);
  939. if (data) {
  940. data._getEvent = function() {
  941. return event;
  942. };
  943. }
  944. if (allowModify) {
  945. event.data = data;
  946. }
  947. for (i = 0; i < l; i++) {
  948. if (! handlers[i]) {
  949. // probably un-binded this handler
  950. continue;
  951. }
  952. // handler is $.Deferred(), call all functions upon completion
  953. if (handlers[i].done) {
  954. dones.push(handlers[i].done);
  955. continue;
  956. }
  957. // set `event.data` only callback has argument
  958. if (handlers[i].length) {
  959. if (!allowModify) {
  960. // to avoid data modifications. remember about "sharing" passing arguments in js :)
  961. if (typeof jst === 'undefined') {
  962. try {
  963. jst = JSON.stringify(data);
  964. } catch(e) {
  965. jst = false;
  966. }
  967. }
  968. event.data = jst? JSON.parse(jst) : data;
  969. }
  970. }
  971. try {
  972. if (handlers[i].call(event, event, this) === false || event.isDefaultPrevented()) {
  973. this.debug('event-stoped', event.type);
  974. break;
  975. }
  976. } catch (ex) {
  977. window.console && window.console.log && window.console.log(ex);
  978. }
  979. }
  980. // call done functions
  981. if (l = dones.length) {
  982. for (i = 0; i < l; i++) {
  983. try {
  984. if (dones[i].call(event, event, this) === false || event.isDefaultPrevented()) {
  985. this.debug('event-stoped', event.type + '(done)');
  986. break;
  987. }
  988. } catch (ex) {
  989. window.console && window.console.log && window.console.log(ex);
  990. }
  991. }
  992. }
  993. if (this.toUnbindEvents[type] && this.toUnbindEvents[type].length) {
  994. $.each(this.toUnbindEvents[type], function(i, v) {
  995. self.unbind(v.type, v.callback);
  996. });
  997. delete this.toUnbindEvents[type];
  998. }
  999. }
  1000. return this;
  1001. };
  1002. /**
  1003. * Get event listeners
  1004. *
  1005. * @param String event type
  1006. * @return Array listed event functions
  1007. */
  1008. this.getListeners = function(event) {
  1009. return event? listeners[event.toLowerCase()] : listeners;
  1010. };
  1011. // set fm.baseUrl
  1012. this.baseUrl = (function() {
  1013. var myTag, base, baseUrl;
  1014. if (self.options.baseUrl) {
  1015. return self.options.baseUrl;
  1016. } else {
  1017. baseUrl = '';
  1018. myTag = null;
  1019. $('head > script').each(function() {
  1020. if (this.src && this.src.match(/js\/elfinder(?:-[a-z0-9_-]+)?\.(?:min|full)\.js$/i)) {
  1021. myTag = $(this);
  1022. return false;
  1023. }
  1024. });
  1025. if (myTag) {
  1026. baseUrl = myTag.attr('src').replace(/js\/[^\/]+$/, '');
  1027. if (! baseUrl.match(/^(https?\/\/|\/)/)) {
  1028. // check <base> tag
  1029. if (base = $('head > base[href]').attr('href')) {
  1030. baseUrl = base.replace(/\/$/, '') + '/' + baseUrl;
  1031. }
  1032. }
  1033. }
  1034. if (baseUrl !== '') {
  1035. self.options.baseUrl = baseUrl;
  1036. } else {
  1037. if (! self.options.baseUrl) {
  1038. self.options.baseUrl = './';
  1039. }
  1040. baseUrl = self.options.baseUrl;
  1041. }
  1042. return baseUrl;
  1043. }
  1044. })();
  1045. this.i18nBaseUrl = (this.options.i18nBaseUrl || this.baseUrl + 'js/i18n').replace(/\/$/, '') + '/';
  1046. this.options.maxErrorDialogs = Math.max(1, parseInt(this.options.maxErrorDialogs || 5));
  1047. // set dispInlineRegex
  1048. cwdOptionsDefault.dispInlineRegex = this.options.dispInlineRegex;
  1049. // auto load required CSS
  1050. if (this.options.cssAutoLoad) {
  1051. (function() {
  1052. var baseUrl = self.baseUrl,
  1053. myCss = $('head > link[href$="css/elfinder.min.css"],link[href$="css/elfinder.full.css"]:first').length,
  1054. rmTag = function() {
  1055. if (node.data('cssautoloadHide')) {
  1056. node.data('cssautoloadHide').remove();
  1057. node.removeData('cssautoloadHide');
  1058. }
  1059. },
  1060. loaded = function() {
  1061. if (!self.cssloaded) {
  1062. rmTag();
  1063. self.cssloaded = true;
  1064. self.trigger('cssloaded');
  1065. }
  1066. };
  1067. if (! myCss) {
  1068. // to request CSS auto loading
  1069. self.cssloaded = null;
  1070. }
  1071. // additional CSS files
  1072. if (Array.isArray(self.options.cssAutoLoad)) {
  1073. if (!self.options.themes.default) {
  1074. // set as default theme
  1075. self.options.themes = Object.assign({
  1076. 'default' : {
  1077. 'name': 'default',
  1078. 'cssurls': self.options.cssAutoLoad
  1079. }
  1080. }, self.options.themes);
  1081. if (!self.options.theme) {
  1082. self.options.theme = 'default';
  1083. }
  1084. } else {
  1085. if (self.cssloaded === true) {
  1086. self.loadCss(self.options.cssAutoLoad);
  1087. } else {
  1088. self.bind('cssloaded', function() {
  1089. self.loadCss(self.options.cssAutoLoad);
  1090. });
  1091. }
  1092. }
  1093. }
  1094. // try to load main css
  1095. if (self.cssloaded === null) {
  1096. // hide elFinder node while css loading
  1097. node.addClass('elfinder')
  1098. .data('cssautoloadHide', $('<style>.elfinder{visibility:hidden;overflow:hidden}</style>'));
  1099. $('head').append(node.data('cssautoloadHide'));
  1100. // set default theme
  1101. if (!self.options.themes.default) {
  1102. self.options.themes = Object.assign({
  1103. 'default' : {
  1104. 'name': 'default',
  1105. 'cssurls': 'css/theme.css',
  1106. 'author': 'elFinder Project',
  1107. 'license': '3-clauses BSD'
  1108. }
  1109. }, self.options.themes);
  1110. if (!self.options.theme) {
  1111. self.options.theme = 'default';
  1112. }
  1113. }
  1114. // Delay 'visibility' check it required for browsers such as Safari
  1115. requestAnimationFrame(function() {
  1116. if (node.css('visibility') === 'hidden') {
  1117. // load CSS
  1118. self.loadCss([baseUrl+'css/elfinder.min.css'], {
  1119. dfd: $.Deferred().done(function() {
  1120. loaded();
  1121. }).fail(function() {
  1122. rmTag();
  1123. if (!self.cssloaded) {
  1124. self.cssloaded = false;
  1125. self.bind('init', function() {
  1126. if (!self.cssloaded) {
  1127. self.error(['errRead', 'CSS (elfinder.min)']);
  1128. }
  1129. });
  1130. }
  1131. })
  1132. });
  1133. } else {
  1134. loaded();
  1135. }
  1136. });
  1137. }
  1138. })();
  1139. }
  1140. // load theme if exists
  1141. (function() {
  1142. var theme,
  1143. themes = self.options.themes,
  1144. ids = Object.keys(themes || {});
  1145. if (ids.length) {
  1146. theme = self.storage('theme') || self.options.theme;
  1147. if (!themes[theme]) {
  1148. theme = ids[0];
  1149. }
  1150. if (self.cssloaded) {
  1151. self.changeTheme(theme);
  1152. } else {
  1153. self.bind('cssloaded', function() {
  1154. self.changeTheme(theme);
  1155. });
  1156. }
  1157. }
  1158. })();
  1159. /**
  1160. * Volume option to set the properties of the root Stat
  1161. *
  1162. * @type Object
  1163. */
  1164. this.optionProperties = {
  1165. icon: void(0),
  1166. csscls: void(0),
  1167. tmbUrl: void(0),
  1168. uiCmdMap: {},
  1169. netkey: void(0),
  1170. disabled: []
  1171. };
  1172. if (! inFrame && ! this.options.enableAlways && $('body').children().length === 2) { // only node and beeper
  1173. this.options.enableAlways = true;
  1174. }
  1175. // make options.debug
  1176. if (this.options.debug === true) {
  1177. this.options.debug = 'all';
  1178. } else if (Array.isArray(this.options.debug)) {
  1179. (function() {
  1180. var d = {};
  1181. $.each(self.options.debug, function() {
  1182. d[this] = true;
  1183. });
  1184. self.options.debug = d;
  1185. })();
  1186. } else {
  1187. this.options.debug = false;
  1188. }
  1189. /**
  1190. * Original functions evacuated by conflict check
  1191. *
  1192. * @type Object
  1193. */
  1194. this.noConflicts = {};
  1195. /**
  1196. * Check and save conflicts with bootstrap etc
  1197. *
  1198. * @type Function
  1199. */
  1200. this.noConflict = function() {
  1201. $.each(conflictChecks, function(i, p) {
  1202. if ($.fn[p] && typeof $.fn[p].noConflict === 'function') {
  1203. self.noConflicts[p] = $.fn[p].noConflict();
  1204. }
  1205. });
  1206. };
  1207. // do check conflict
  1208. this.noConflict();
  1209. /**
  1210. * Is elFinder over CORS
  1211. *
  1212. * @type Boolean
  1213. **/
  1214. this.isCORS = false;
  1215. // configure for CORS
  1216. (function(){
  1217. if (typeof self.options.cors !== 'undefined' && self.options.cors !== null) {
  1218. self.isCORS = self.options.cors? true : false;
  1219. } else {
  1220. var parseUrl = document.createElement('a'),
  1221. parseUploadUrl,
  1222. selfProtocol = window.location.protocol,
  1223. portReg = function(protocol) {
  1224. protocol = (!protocol || protocol === ':')? selfProtocol : protocol;
  1225. return protocol === 'https:'? /\:443$/ : /\:80$/;
  1226. },
  1227. selfHost = window.location.host.replace(portReg(selfProtocol), '');
  1228. parseUrl.href = opts.url;
  1229. if (opts.urlUpload && (opts.urlUpload !== opts.url)) {
  1230. parseUploadUrl = document.createElement('a');
  1231. parseUploadUrl.href = opts.urlUpload;
  1232. }
  1233. if (selfHost !== parseUrl.host.replace(portReg(parseUrl.protocol), '')
  1234. || (parseUrl.protocol !== ':'&& parseUrl.protocol !== '' && (selfProtocol !== parseUrl.protocol))
  1235. || (parseUploadUrl &&
  1236. (selfHost !== parseUploadUrl.host.replace(portReg(parseUploadUrl.protocol), '')
  1237. || (parseUploadUrl.protocol !== ':' && parseUploadUrl.protocol !== '' && (selfProtocol !== parseUploadUrl.protocol))
  1238. )
  1239. )
  1240. ) {
  1241. self.isCORS = true;
  1242. }
  1243. }
  1244. if (self.isCORS) {
  1245. if (!$.isPlainObject(self.options.customHeaders)) {
  1246. self.options.customHeaders = {};
  1247. }
  1248. if (!$.isPlainObject(self.options.xhrFields)) {
  1249. self.options.xhrFields = {};
  1250. }
  1251. self.options.requestType = 'post';
  1252. self.options.customHeaders['X-Requested-With'] = 'XMLHttpRequest';
  1253. self.options.xhrFields['withCredentials'] = true;
  1254. }
  1255. })();
  1256. /**
  1257. * Ajax request type
  1258. *
  1259. * @type String
  1260. * @default "get"
  1261. **/
  1262. this.requestType = /^(get|post)$/i.test(this.options.requestType) ? this.options.requestType.toLowerCase() : 'get';
  1263. // set `requestMaxConn` by option
  1264. requestMaxConn = Math.max(parseInt(this.options.requestMaxConn), 1);
  1265. /**
  1266. * Custom data that given as options
  1267. *
  1268. * @type Object
  1269. * @default {}
  1270. */
  1271. this.optsCustomData = $.isPlainObject(this.options.customData) ? this.options.customData : {};
  1272. /**
  1273. * Any data to send across every ajax request
  1274. *
  1275. * @type Object
  1276. * @default {}
  1277. **/
  1278. this.customData = Object.assign({}, this.optsCustomData);
  1279. /**
  1280. * Previous custom data from connector
  1281. *
  1282. * @type Object|null
  1283. */
  1284. this.prevCustomData = null;
  1285. /**
  1286. * Any custom headers to send across every ajax request
  1287. *
  1288. * @type Object
  1289. * @default {}
  1290. */
  1291. this.customHeaders = $.isPlainObject(this.options.customHeaders) ? this.options.customHeaders : {};
  1292. /**
  1293. * Any custom xhrFields to send across every ajax request
  1294. *
  1295. * @type Object
  1296. * @default {}
  1297. */
  1298. this.xhrFields = $.isPlainObject(this.options.xhrFields) ? this.options.xhrFields : {};
  1299. /**
  1300. * Replace XMLHttpRequest.prototype.send to extended function for 3rd party libs XHR request etc.
  1301. *
  1302. * @type Function
  1303. */
  1304. this.replaceXhrSend = function() {
  1305. if (! savedXhrSend) {
  1306. savedXhrSend = XMLHttpRequest.prototype.send;
  1307. }
  1308. XMLHttpRequest.prototype.send = function() {
  1309. var xhr = this;
  1310. // set request headers
  1311. if (self.customHeaders) {
  1312. $.each(self.customHeaders, function(key) {
  1313. xhr.setRequestHeader(key, this);
  1314. });
  1315. }
  1316. // set xhrFields
  1317. if (self.xhrFields) {
  1318. $.each(self.xhrFields, function(key) {
  1319. if (key in xhr) {
  1320. xhr[key] = this;
  1321. }
  1322. });
  1323. }
  1324. return savedXhrSend.apply(this, arguments);
  1325. };
  1326. };
  1327. /**
  1328. * Restore saved original XMLHttpRequest.prototype.send
  1329. *
  1330. * @type Function
  1331. */
  1332. this.restoreXhrSend = function() {
  1333. savedXhrSend && (XMLHttpRequest.prototype.send = savedXhrSend);
  1334. };
  1335. /**
  1336. * command names for into queue for only cwd requests
  1337. * these commands aborts before `open` request
  1338. *
  1339. * @type Array
  1340. * @default ['tmb', 'parents']
  1341. */
  1342. this.abortCmdsOnOpen = this.options.abortCmdsOnOpen || ['tmb', 'parents'];
  1343. /**
  1344. * ui.nav id prefix
  1345. *
  1346. * @type String
  1347. */
  1348. this.navPrefix = 'nav' + (elFinder.prototype.uniqueid? elFinder.prototype.uniqueid : '') + '-';
  1349. /**
  1350. * ui.cwd id prefix
  1351. *
  1352. * @type String
  1353. */
  1354. this.cwdPrefix = elFinder.prototype.uniqueid? ('cwd' + elFinder.prototype.uniqueid + '-') : '';
  1355. // Increment elFinder.prototype.uniqueid
  1356. ++elFinder.prototype.uniqueid;
  1357. /**
  1358. * URL to upload files
  1359. *
  1360. * @type String
  1361. **/
  1362. this.uploadURL = opts.urlUpload || opts.url;
  1363. /**
  1364. * Events namespace
  1365. *
  1366. * @type String
  1367. **/
  1368. this.namespace = namespace;
  1369. /**
  1370. * Today timestamp
  1371. *
  1372. * @type Number
  1373. **/
  1374. this.today = (new Date(date.getFullYear(), date.getMonth(), date.getDate())).getTime()/1000;
  1375. /**
  1376. * Yesterday timestamp
  1377. *
  1378. * @type Number
  1379. **/
  1380. this.yesterday = this.today - 86400;
  1381. utc = this.options.UTCDate ? 'UTC' : '';
  1382. this.getHours = 'get'+utc+'Hours';
  1383. this.getMinutes = 'get'+utc+'Minutes';
  1384. this.getSeconds = 'get'+utc+'Seconds';
  1385. this.getDate = 'get'+utc+'Date';
  1386. this.getDay = 'get'+utc+'Day';
  1387. this.getMonth = 'get'+utc+'Month';
  1388. this.getFullYear = 'get'+utc+'FullYear';
  1389. /**
  1390. * elFinder node z-index (auto detect on elFinder load)
  1391. *
  1392. * @type null | Number
  1393. **/
  1394. this.zIndex;
  1395. /**
  1396. * Current search status
  1397. *
  1398. * @type Object
  1399. */
  1400. this.searchStatus = {
  1401. state : 0, // 0: search ended, 1: search started, 2: in search result
  1402. query : '',
  1403. target : '',
  1404. mime : '',
  1405. mixed : false, // in multi volumes search: false or Array that target volume ids
  1406. ininc : false // in incremental search
  1407. };
  1408. /**
  1409. * Interface language
  1410. *
  1411. * @type String
  1412. * @default "en"
  1413. **/
  1414. this.lang = this.storage('lang') || this.options.lang;
  1415. if (this.lang === 'jp') {
  1416. this.lang = this.options.lang = 'ja';
  1417. }
  1418. this.viewType = this.storage('view') || this.options.defaultView || 'icons';
  1419. this.sortType = this.storage('sortType') || this.options.sortType || 'name';
  1420. this.sortOrder = this.storage('sortOrder') || this.options.sortOrder || 'asc';
  1421. this.sortStickFolders = this.storage('sortStickFolders');
  1422. if (this.sortStickFolders === null) {
  1423. this.sortStickFolders = !!this.options.sortStickFolders;
  1424. } else {
  1425. this.sortStickFolders = !!this.sortStickFolders;
  1426. }
  1427. this.sortAlsoTreeview = this.storage('sortAlsoTreeview');
  1428. if (this.sortAlsoTreeview === null || this.options.sortAlsoTreeview === null) {
  1429. this.sortAlsoTreeview = !!this.options.sortAlsoTreeview;
  1430. } else {
  1431. this.sortAlsoTreeview = !!this.sortAlsoTreeview;
  1432. }
  1433. this.sortRules = $.extend(true, {}, this._sortRules, this.options.sortRules);
  1434. $.each(this.sortRules, function(name, method) {
  1435. if (typeof method != 'function') {
  1436. delete self.sortRules[name];
  1437. }
  1438. });
  1439. this.compare = $.proxy(this.compare, this);
  1440. /**
  1441. * Delay in ms before open notification dialog
  1442. *
  1443. * @type Number
  1444. * @default 500
  1445. **/
  1446. this.notifyDelay = this.options.notifyDelay > 0 ? parseInt(this.options.notifyDelay) : 500;
  1447. /**
  1448. * Dragging UI Helper object
  1449. *
  1450. * @type jQuery | null
  1451. **/
  1452. this.draggingUiHelper = null;
  1453. /**
  1454. * Base droppable options
  1455. *
  1456. * @type Object
  1457. **/
  1458. this.droppable = {
  1459. greedy : true,
  1460. tolerance : 'pointer',
  1461. accept : '.elfinder-cwd-file-wrapper,.elfinder-navbar-dir,.elfinder-cwd-file,.elfinder-cwd-filename',
  1462. hoverClass : this.res('class', 'adroppable'),
  1463. classes : { // Deprecated hoverClass jQueryUI>=1.12.0
  1464. 'ui-droppable-hover': this.res('class', 'adroppable')
  1465. },
  1466. autoDisable: true, // elFinder original, see jquery.elfinder.js
  1467. drop : function(e, ui) {
  1468. var dst = $(this),
  1469. targets = $.grep(ui.helper.data('files')||[], function(h) { return h? true : false; }),
  1470. result = [],
  1471. dups = [],
  1472. faults = [],
  1473. isCopy = ui.helper.hasClass('elfinder-drag-helper-plus'),
  1474. c = 'class',
  1475. cnt, hash, i, h;
  1476. if (typeof e.button === 'undefined' || ui.helper.data('namespace') !== namespace || ! self.insideWorkzone(e.pageX, e.pageY)) {
  1477. return false;
  1478. }
  1479. if (dst.hasClass(self.res(c, 'cwdfile'))) {
  1480. hash = self.cwdId2Hash(dst.attr('id'));
  1481. } else if (dst.hasClass(self.res(c, 'navdir'))) {
  1482. hash = self.navId2Hash(dst.attr('id'));
  1483. } else {
  1484. hash = cwd;
  1485. }
  1486. cnt = targets.length;
  1487. while (cnt--) {
  1488. h = targets[cnt];
  1489. // ignore drop into itself or in own location
  1490. if (h != hash && files[h].phash != hash) {
  1491. result.push(h);
  1492. } else {
  1493. ((isCopy && h !== hash && files[hash].write)? dups : faults).push(h);
  1494. }
  1495. }
  1496. if (faults.length) {
  1497. return false;
  1498. }
  1499. ui.helper.data('droped', true);
  1500. if (dups.length) {
  1501. ui.helper.hide();
  1502. self.exec('duplicate', dups, {_userAction: true});
  1503. }
  1504. if (result.length) {
  1505. ui.helper.hide();
  1506. self.clipboard(result, !isCopy);
  1507. self.exec('paste', hash, {_userAction: true}, hash).always(function(){
  1508. self.clipboard([]);
  1509. self.trigger('unlockfiles', {files : targets});
  1510. });
  1511. self.trigger('drop', {files : targets});
  1512. }
  1513. }
  1514. };
  1515. /**
  1516. * Return true if filemanager is active
  1517. *
  1518. * @return Boolean
  1519. **/
  1520. this.enabled = function() {
  1521. return enabled && this.visible();
  1522. };
  1523. /**
  1524. * Return true if filemanager is visible
  1525. *
  1526. * @return Boolean
  1527. **/
  1528. this.visible = function() {
  1529. return node[0].elfinder && node.is(':visible');
  1530. };
  1531. /**
  1532. * Return file is root?
  1533. *
  1534. * @param Object target file object
  1535. * @return Boolean
  1536. */
  1537. this.isRoot = function(file) {
  1538. return (file.isroot || ! file.phash)? true : false;
  1539. };
  1540. /**
  1541. * Return root dir hash for current working directory
  1542. *
  1543. * @param String target hash
  1544. * @param Boolean include fake parent (optional)
  1545. * @return String
  1546. */
  1547. this.root = function(hash, fake) {
  1548. hash = hash || cwd;
  1549. var dir, i;
  1550. if (! fake) {
  1551. $.each(self.roots, function(id, rhash) {
  1552. if (hash.indexOf(id) === 0) {
  1553. dir = rhash;
  1554. return false;
  1555. }
  1556. });
  1557. if (dir) {
  1558. return dir;
  1559. }
  1560. }
  1561. dir = files[hash];
  1562. while (dir && dir.phash && (fake || ! dir.isroot)) {
  1563. dir = files[dir.phash];
  1564. }
  1565. if (dir) {
  1566. return dir.hash;
  1567. }
  1568. while (i in files && files.hasOwnProperty(i)) {
  1569. dir = files[i];
  1570. if (dir.mime === 'directory' && !dir.phash && dir.read) {
  1571. return dir.hash;
  1572. }
  1573. }
  1574. return '';
  1575. };
  1576. /**
  1577. * Return current working directory info
  1578. *
  1579. * @return Object
  1580. */
  1581. this.cwd = function() {
  1582. return files[cwd] || {};
  1583. };
  1584. /**
  1585. * Return required cwd option
  1586. *
  1587. * @param String option name
  1588. * @param String target hash (optional)
  1589. * @return mixed
  1590. */
  1591. this.option = function(name, target) {
  1592. var res, item;
  1593. target = target || cwd;
  1594. if (self.optionsByHashes[target] && typeof self.optionsByHashes[target][name] !== 'undefined') {
  1595. return self.optionsByHashes[target][name];
  1596. }
  1597. if (self.hasVolOptions && cwd !== target && (!(item = self.file(target)) || item.phash !== cwd)) {
  1598. res = '';
  1599. $.each(self.volOptions, function(id, opt) {
  1600. if (target.indexOf(id) === 0) {
  1601. res = opt[name] || '';
  1602. return false;
  1603. }
  1604. });
  1605. return res;
  1606. } else {
  1607. return cwdOptions[name] || '';
  1608. }
  1609. };
  1610. /**
  1611. * Return disabled commands by each folder
  1612. *
  1613. * @param Array target hashes
  1614. * @return Array
  1615. */
  1616. this.getDisabledCmds = function(targets, flip) {
  1617. var disabled = {'hidden': true};
  1618. if (! Array.isArray(targets)) {
  1619. targets = [ targets ];
  1620. }
  1621. $.each(targets, function(i, h) {
  1622. var disCmds = self.option('disabledFlip', h);
  1623. if (disCmds) {
  1624. Object.assign(disabled, disCmds);
  1625. }
  1626. });
  1627. return flip? disabled : Object.keys(disabled);
  1628. };
  1629. /**
  1630. * Return file data from current dir or tree by it's hash
  1631. *
  1632. * @param String file hash
  1633. * @return Object
  1634. */
  1635. this.file = function(hash, alsoHidden) {
  1636. return hash? (files[hash] || (alsoHidden? hiddenFiles[hash] : void(0))) : void(0);
  1637. };
  1638. /**
  1639. * Return all cached files
  1640. *
  1641. * @param String parent hash
  1642. * @return Object
  1643. */
  1644. this.files = function(phash) {
  1645. var items = {};
  1646. if (phash) {
  1647. if (!ownFiles[phash]) {
  1648. return {};
  1649. }
  1650. $.each(ownFiles[phash], function(h) {
  1651. if (files[h]) {
  1652. items[h] = files[h];
  1653. } else {
  1654. delete ownFiles[phash][h];
  1655. }
  1656. });
  1657. return Object.assign({}, items);
  1658. }
  1659. return Object.assign({}, files);
  1660. };
  1661. /**
  1662. * Return list of file parents hashes include file hash
  1663. *
  1664. * @param String file hash
  1665. * @return Array
  1666. */
  1667. this.parents = function(hash) {
  1668. var parents = [],
  1669. dir;
  1670. while (hash && (dir = this.file(hash))) {
  1671. parents.unshift(dir.hash);
  1672. hash = dir.phash;
  1673. }
  1674. return parents;
  1675. };
  1676. this.path2array = function(hash, i18) {
  1677. var file,
  1678. path = [];
  1679. while (hash) {
  1680. if ((file = files[hash]) && file.hash) {
  1681. path.unshift(i18 && file.i18 ? file.i18 : file.name);
  1682. hash = file.isroot? null : file.phash;
  1683. } else {
  1684. path = [];
  1685. break;
  1686. }
  1687. }
  1688. return path;
  1689. };
  1690. /**
  1691. * Return file path or Get path async with jQuery.Deferred
  1692. *
  1693. * @param Object file
  1694. * @param Boolean i18
  1695. * @param Object asyncOpt
  1696. * @return String|jQuery.Deferred
  1697. */
  1698. this.path = function(hash, i18, asyncOpt) {
  1699. var path = files[hash] && files[hash].path
  1700. ? files[hash].path
  1701. : this.path2array(hash, i18).join(cwdOptions.separator);
  1702. if (! asyncOpt || ! files[hash]) {
  1703. return path;
  1704. } else {
  1705. asyncOpt = Object.assign({notify: {type : 'parents', cnt : 1, hideCnt : true}}, asyncOpt);
  1706. var dfd = $.Deferred(),
  1707. notify = asyncOpt.notify,
  1708. noreq = false,
  1709. req = function() {
  1710. self.request({
  1711. data : {cmd : 'parents', target : files[hash].phash},
  1712. notify : notify,
  1713. preventFail : true
  1714. })
  1715. .done(done)
  1716. .fail(function() {
  1717. dfd.reject();
  1718. });
  1719. },
  1720. done = function() {
  1721. self.one('parentsdone', function() {
  1722. path = self.path(hash, i18);
  1723. if (path === '' && noreq) {
  1724. //retry with request
  1725. noreq = false;
  1726. req();
  1727. } else {
  1728. if (notify) {
  1729. clearTimeout(ntftm);
  1730. notify.cnt = -(parseInt(notify.cnt || 0));
  1731. self.notify(notify);
  1732. }
  1733. dfd.resolve(path);
  1734. }
  1735. });
  1736. },
  1737. ntftm;
  1738. if (path) {
  1739. return dfd.resolve(path);
  1740. } else {
  1741. if (self.ui['tree']) {
  1742. // try as no request
  1743. if (notify) {
  1744. ntftm = setTimeout(function() {
  1745. self.notify(notify);
  1746. }, self.notifyDelay);
  1747. }
  1748. noreq = true;
  1749. done(true);
  1750. } else {
  1751. req();
  1752. }
  1753. return dfd;
  1754. }
  1755. }
  1756. };
  1757. /**
  1758. * Return file url if set
  1759. *
  1760. * @param String file hash
  1761. * @param Object Options
  1762. * @return String|Object of jQuery Deferred
  1763. */
  1764. this.url = function(hash, o) {
  1765. var file = files[hash],
  1766. opts = o || {},
  1767. async = opts.async || false,
  1768. temp = opts.temporary || false,
  1769. onetm = (opts.onetime && self.option('onetimeUrl', hash)) || false,
  1770. absurl = opts.absurl || false,
  1771. dfrd = (async || onetm)? $.Deferred() : null,
  1772. filter = function(url) {
  1773. if (url && absurl) {
  1774. url = self.convAbsUrl(url);
  1775. }
  1776. return url;
  1777. },
  1778. getUrl = function(url) {
  1779. if (url) {
  1780. return filter(url);
  1781. }
  1782. if (file.url) {
  1783. return filter(file.url);
  1784. }
  1785. if (typeof baseUrl === 'undefined') {
  1786. baseUrl = getBaseUrl();
  1787. }
  1788. if (baseUrl) {
  1789. return filter(baseUrl + $.map(self.path2array(hash), function(n) { return encodeURIComponent(n); }).slice(1).join('/'));
  1790. }
  1791. var params = Object.assign({}, self.customData, {
  1792. cmd: 'file',
  1793. target: file.hash
  1794. });
  1795. if (self.oldAPI) {
  1796. params.cmd = 'open';
  1797. params.current = file.phash;
  1798. }
  1799. return filter(self.options.url + (self.options.url.indexOf('?') === -1 ? '?' : '&') + $.param(params, true));
  1800. },
  1801. getBaseUrl = function() {
  1802. return self.option('url', (!self.isRoot(file) && file.phash) || file.hash);
  1803. },
  1804. baseUrl, res;
  1805. if (!file || !file.read) {
  1806. return async? dfrd.resolve('') : '';
  1807. }
  1808. if (onetm && (!file.url || file.url == '1') && !(baseUrl = getBaseUrl())) {
  1809. async = true;
  1810. this.request({
  1811. data : { cmd : 'url', target : hash, options : { onetime: 1 } },
  1812. preventDefault : true,
  1813. options: {async: async},
  1814. notify: {type : 'file', cnt : 1, hideCnt : true},
  1815. progressBar: opts.progressBar
  1816. }).done(function(data) {
  1817. dfrd.resolve(filter(data.url || ''));
  1818. }).fail(function() {
  1819. dfrd.resolve('');
  1820. });
  1821. } else {
  1822. if (file.url == '1' || (temp && !file.url && !(baseUrl = getBaseUrl()))) {
  1823. this.request({
  1824. data : { cmd : 'url', target : hash, options : { temporary: temp? 1 : 0 } },
  1825. preventDefault : true,
  1826. options: {async: async},
  1827. notify: async? {type : temp? 'file' : 'url', cnt : 1, hideCnt : true} : {},
  1828. progressBar: opts.progressBar
  1829. })
  1830. .done(function(data) {
  1831. file.url = data.url || '';
  1832. })
  1833. .fail(function() {
  1834. file.url = '';
  1835. })
  1836. .always(function() {
  1837. var url;
  1838. if (file.url && temp) {
  1839. url = file.url;
  1840. file.url = '1'; // restore
  1841. }
  1842. if (async) {
  1843. dfrd.resolve(getUrl(url));
  1844. } else {
  1845. return getUrl(url);
  1846. }
  1847. });
  1848. } else {
  1849. if (async) {
  1850. dfrd.resolve(getUrl());
  1851. } else {
  1852. return getUrl();
  1853. }
  1854. }
  1855. }
  1856. if (async) {
  1857. return dfrd;
  1858. }
  1859. };
  1860. /**
  1861. * Return file url for the extarnal service
  1862. *
  1863. * @param String hash The hash
  1864. * @param Object options The options
  1865. * @return Object jQuery Deferred
  1866. */
  1867. this.forExternalUrl = function(hash, options) {
  1868. var onetime = self.option('onetimeUrl', hash),
  1869. opts = {
  1870. async: true,
  1871. absurl: true
  1872. };
  1873. opts[onetime? 'onetime' : 'temporary'] = true;
  1874. return self.url(hash, Object.assign({}, options, opts));
  1875. };
  1876. /**
  1877. * Return file url for open in elFinder
  1878. *
  1879. * @param String file hash
  1880. * @param Boolean for download link
  1881. * @param Object requestOpts The request options
  1882. * @return String
  1883. */
  1884. this.openUrl = function(hash, download, callback, requestOpts) {
  1885. var file = files[hash],
  1886. url = '',
  1887. onetimeSize = (requestOpts || {}).onetimeSize || (5 * 1024 * 1024);
  1888. if (!file || !file.read) {
  1889. return '';
  1890. }
  1891. if (!download || download === 'sameorigin') {
  1892. if (file.url) {
  1893. if (file.url != 1) {
  1894. url = file.url;
  1895. }
  1896. } else if (cwdOptions.url && file.hash.indexOf(self.cwd().volumeid) === 0) {
  1897. url = cwdOptions.url + $.map(this.path2array(hash), function(n) { return encodeURIComponent(n); }).slice(1).join('/');
  1898. }
  1899. if (!download || this.isSameOrigin(url)) {
  1900. if (url) {
  1901. url += (url.match(/\?/)? '&' : '?') + '_'.repeat((url.match(/[\?&](_+)t=/g) || ['&t=']).sort().shift().match(/[\?&](_*)t=/)[1].length + 1) + 't=' + (file.ts || parseInt(+new Date()/1000));
  1902. if (callback) {
  1903. callback(url);
  1904. return;
  1905. } else {
  1906. return url;
  1907. }
  1908. }
  1909. }
  1910. }
  1911. if (callback && this.hasParrotHeaders()) {
  1912. if (!requestOpts) {
  1913. requestOpts = {};
  1914. } else {
  1915. delete requestOpts.onetimeSize;
  1916. }
  1917. if (!requestOpts.onetime && !requestOpts.temporary && file.size > onetimeSize) {
  1918. if (file.mime.match(/^video|audio/)) {
  1919. requestOpts.temporary = true;
  1920. } else {
  1921. requestOpts.onetime = true;
  1922. }
  1923. }
  1924. if (requestOpts.onetime || requestOpts.temporary) {
  1925. return this.url(file.hash, Object.assign({
  1926. async: true
  1927. }, requestOpts)).done(function(url) {
  1928. callback(url);
  1929. }).fail(function() {
  1930. callback('');
  1931. });
  1932. } else {
  1933. return this.getContents(hash, 'blob', requestOpts).done(function(blob){
  1934. url = (window.URL || window.webkitURL).createObjectURL(blob);
  1935. callback(url);
  1936. }).fail(function() {
  1937. callback('');
  1938. });
  1939. }
  1940. } else {
  1941. url = this.options.url;
  1942. url = url + (url.indexOf('?') === -1 ? '?' : '&')
  1943. + (this.oldAPI ? 'cmd=open&current='+file.phash : 'cmd=file')
  1944. + '&target=' + file.hash
  1945. + '&_t=' + (file.ts || parseInt(+new Date()/1000));
  1946. if (download === true) {
  1947. url += '&download=1';
  1948. }
  1949. $.each(this.customData, function(key, val) {
  1950. url += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(val);
  1951. });
  1952. if (callback) {
  1953. callback(url);
  1954. return;
  1955. } else {
  1956. return url;
  1957. }
  1958. }
  1959. };
  1960. /**
  1961. * Return thumbnail url
  1962. *
  1963. * @param Object file object
  1964. * @return String
  1965. */
  1966. this.tmb = function(file) {
  1967. var tmbUrl, tmbCrop,
  1968. cls = 'elfinder-cwd-bgurl',
  1969. url = '',
  1970. cData = {},
  1971. n = 0;
  1972. if ($.isPlainObject(file)) {
  1973. if (self.searchStatus.state && file.hash.indexOf(self.cwd().volumeid) !== 0) {
  1974. tmbUrl = self.option('tmbUrl', file.hash);
  1975. tmbCrop = self.option('tmbCrop', file.hash);
  1976. } else {
  1977. tmbUrl = cwdOptions.tmbUrl;
  1978. tmbCrop = cwdOptions.tmbCrop;
  1979. }
  1980. if (tmbCrop) {
  1981. cls += ' elfinder-cwd-bgurl-crop';
  1982. }
  1983. if (tmbUrl === 'self' && file.mime.indexOf('image/') === 0) {
  1984. url = self.openUrl(file.hash);
  1985. cls += ' elfinder-cwd-bgself';
  1986. } else if ((self.oldAPI || tmbUrl) && file && file.tmb && file.tmb != 1) {
  1987. url = tmbUrl + file.tmb;
  1988. } else if (self.newAPI && file && file.tmb && file.tmb != 1) {
  1989. url = file.tmb;
  1990. }
  1991. if (url) {
  1992. if (tmbUrl !== 'self') {
  1993. if (file.ts) {
  1994. cData._t = file.ts;
  1995. }
  1996. if (cwdOptions.tmbReqCustomData && Object.keys(this.customData).length) {
  1997. cData = Object.assign(cData, this.customData);
  1998. }
  1999. if (Object.keys(cData).length) {
  2000. url += (url.match(/\?/) ? '&' : '?');
  2001. $.each(cData, function (key, val) {
  2002. url += ((n++ === 0)? '' : '&') + encodeURIComponent(key) + '=' + encodeURIComponent(val);
  2003. });
  2004. }
  2005. }
  2006. return { url: url, className: cls };
  2007. }
  2008. }
  2009. return false;
  2010. };
  2011. /**
  2012. * Return selected files hashes
  2013. *
  2014. * @return Array
  2015. **/
  2016. this.selected = function() {
  2017. return selected.slice(0);
  2018. };
  2019. /**
  2020. * Return selected files info
  2021. *
  2022. * @return Array
  2023. */
  2024. this.selectedFiles = function() {
  2025. return $.map(selected, function(hash) { return files[hash] ? Object.assign({}, files[hash]) : null; });
  2026. };
  2027. /**
  2028. * Return true if file with required name existsin required folder
  2029. *
  2030. * @param String file name
  2031. * @param String parent folder hash
  2032. * @return Boolean
  2033. */
  2034. this.fileByName = function(name, phash) {
  2035. var hash;
  2036. for (hash in files) {
  2037. if (files.hasOwnProperty(hash) && files[hash].phash == phash && files[hash].name == name) {
  2038. return files[hash];
  2039. }
  2040. }
  2041. };
  2042. /**
  2043. * Valid data for required command based on rules
  2044. *
  2045. * @param String command name
  2046. * @param Object cammand's data
  2047. * @return Boolean
  2048. */
  2049. this.validResponse = function(cmd, data) {
  2050. return data.error || this.rules[this.rules[cmd] ? cmd : 'defaults'](data);
  2051. };
  2052. /**
  2053. * Return bytes from ini formated size
  2054. *
  2055. * @param String ini formated size
  2056. * @return Integer
  2057. */
  2058. this.returnBytes = function(val) {
  2059. var last;
  2060. if (isNaN(val)) {
  2061. if (! val) {
  2062. val = '';
  2063. }
  2064. // for ex. 1mb, 1KB
  2065. val = val.replace(/b$/i, '');
  2066. last = val.charAt(val.length - 1).toLowerCase();
  2067. val = val.replace(/[tgmk]$/i, '');
  2068. if (last == 't') {
  2069. val = val * 1024 * 1024 * 1024 * 1024;
  2070. } else if (last == 'g') {
  2071. val = val * 1024 * 1024 * 1024;
  2072. } else if (last == 'm') {
  2073. val = val * 1024 * 1024;
  2074. } else if (last == 'k') {
  2075. val = val * 1024;
  2076. }
  2077. val = isNaN(val)? 0 : parseInt(val);
  2078. } else {
  2079. val = parseInt(val);
  2080. if (val < 1) val = 0;
  2081. }
  2082. return val;
  2083. };
  2084. /**
  2085. * Process ajax request.
  2086. * Fired events :
  2087. * @todo
  2088. * @example
  2089. * @todo
  2090. * @return $.Deferred
  2091. */
  2092. this.request = function(opts) {
  2093. var self = this,
  2094. o = this.options,
  2095. dfrd = $.Deferred(),
  2096. // request ID
  2097. reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16),
  2098. // request data
  2099. data = Object.assign({}, self.customData, {mimes : o.onlyMimes}, opts.data || opts),
  2100. // command name
  2101. cmd = data.cmd,
  2102. // request type is binary
  2103. isBinary = (opts.options || {}).dataType === 'binary',
  2104. // current cmd is "open"
  2105. isOpen = (!opts.asNotOpen && cmd === 'open'),
  2106. // call default fail callback (display error dialog) ?
  2107. deffail = !(isBinary || opts.preventDefault || opts.preventFail),
  2108. // call default success callback ?
  2109. defdone = !(isBinary || opts.preventDefault || opts.preventDone),
  2110. // current progress of receive data
  2111. prog = opts.progressVal || 20,
  2112. // timer of fake progress
  2113. progTm = null,
  2114. // whether the notification dialog is currently displayed
  2115. hasNotify= false,
  2116. // options for notify dialog
  2117. notify = !opts.progressBar? (opts.notify? Object.assign({progress: prog * opts.notify.cnt}, opts.notify) : {}) : {},
  2118. // make cancel button
  2119. cancel = !!opts.cancel,
  2120. // do not normalize data - return as is
  2121. raw = isBinary || !!opts.raw,
  2122. // sync files on request fail
  2123. syncOnFail = opts.syncOnFail,
  2124. // use lazy()
  2125. lazy = !!opts.lazy,
  2126. // prepare function before done()
  2127. prepare = opts.prepare,
  2128. // navigate option object when cmd done
  2129. navigate = opts.navigate,
  2130. // open notify dialog timeout
  2131. timeout,
  2132. // use browser cache
  2133. useCache = (opts.options || {}).cache,
  2134. // request options
  2135. options = Object.assign({
  2136. url : o.url,
  2137. async : true,
  2138. type : this.requestType,
  2139. dataType : 'json',
  2140. cache : (self.api >= 2.1029), // api >= 2.1029 has unique request ID
  2141. data : data,
  2142. headers : this.customHeaders,
  2143. xhrFields: this.xhrFields,
  2144. progress : function(e) {
  2145. var p = e.loaded / e.total * 100;
  2146. progTm && clearTimeout(progTm);
  2147. if (opts.progressBar) {
  2148. try {
  2149. opts.progressBar.width(p + '%');
  2150. } catch(e) {}
  2151. } else {
  2152. if (hasNotify && notify.type) {
  2153. p = p * notify.cnt;
  2154. if (prog < p) {
  2155. self.notify({
  2156. type: notify.type,
  2157. progress: p - prog,
  2158. cnt: 0,
  2159. hideCnt: notify.hideCnt
  2160. });
  2161. prog = p;
  2162. }
  2163. }
  2164. }
  2165. if (opts.progress) {
  2166. try {
  2167. opts.progress(e);
  2168. } catch(e) {}
  2169. }
  2170. }
  2171. }, opts.options || {}),
  2172. /**
  2173. * Default success handler.
  2174. * Call default data handlers and fire event with command name.
  2175. *
  2176. * @param Object normalized response data
  2177. * @return void
  2178. **/
  2179. done = function(data) {
  2180. data.warning && self.error(data.warning);
  2181. if (isOpen) {
  2182. open(data);
  2183. } else {
  2184. self.updateCache(data);
  2185. }
  2186. self.lazy(function() {
  2187. // fire some event to update cache/ui
  2188. data.removed && data.removed.length && self.remove(data);
  2189. data.added && data.added.length && self.add(data);
  2190. data.changed && data.changed.length && self.change(data);
  2191. }).then(function() {
  2192. // fire event with command name
  2193. return self.lazy(function() {
  2194. self.trigger(cmd, data, false);
  2195. });
  2196. }).then(function() {
  2197. // fire event with command name + 'done'
  2198. return self.lazy(function() {
  2199. self.trigger(cmd + 'done');
  2200. });
  2201. }).then(function() {
  2202. // make toast message
  2203. if (data.toasts && Array.isArray(data.toasts)) {
  2204. $.each(data.toasts, function() {
  2205. this.msg && self.toast(this);
  2206. });
  2207. }
  2208. // force update content
  2209. data.sync && self.sync();
  2210. });
  2211. },
  2212. /**
  2213. * Request error handler. Reject dfrd with correct error message.
  2214. *
  2215. * @param jqxhr request object
  2216. * @param String request status
  2217. * @return void
  2218. **/
  2219. error = function(xhr, status) {
  2220. var error, data,
  2221. d = self.options.debug;
  2222. switch (status) {
  2223. case 'abort':
  2224. error = xhr.quiet ? '' : ['errConnect', 'errAbort'];
  2225. break;
  2226. case 'timeout':
  2227. error = ['errConnect', 'errTimeout'];
  2228. break;
  2229. case 'parsererror':
  2230. error = ['errResponse', 'errDataNotJSON'];
  2231. if (xhr.responseText) {
  2232. if (! cwd || (d && (d === 'all' || d['backend-error']))) {
  2233. error.push(xhr.responseText);
  2234. }
  2235. }
  2236. break;
  2237. default:
  2238. if (xhr.responseText) {
  2239. // check responseText, Is that JSON?
  2240. try {
  2241. data = JSON.parse(xhr.responseText);
  2242. if (data && data.error) {
  2243. error = data.error;
  2244. }
  2245. } catch(e) {}
  2246. }
  2247. if (! error) {
  2248. if (xhr.status == 403) {
  2249. error = ['errConnect', 'errAccess', 'HTTP error ' + xhr.status];
  2250. } else if (xhr.status == 404) {
  2251. error = ['errConnect', 'errNotFound', 'HTTP error ' + xhr.status];
  2252. } else if (xhr.status >= 500) {
  2253. error = ['errResponse', 'errServerError', 'HTTP error ' + xhr.status];
  2254. } else {
  2255. if (xhr.status == 414 && options.type === 'get') {
  2256. // retry by POST method
  2257. options.type = 'post';
  2258. self.abortXHR(xhr);
  2259. dfrd.xhr = xhr = self.transport.send(options).fail(error).done(success);
  2260. return;
  2261. }
  2262. error = xhr.quiet ? '' : ['errConnect', 'HTTP error ' + xhr.status];
  2263. }
  2264. }
  2265. }
  2266. self.trigger(cmd + 'done');
  2267. dfrd.reject({error: error}, xhr, status);
  2268. },
  2269. /**
  2270. * Request success handler. Valid response data and reject/resolve dfrd.
  2271. *
  2272. * @param Object response data
  2273. * @param String request status
  2274. * @return void
  2275. **/
  2276. success = function(response) {
  2277. // Set currrent request command name
  2278. self.currentReqCmd = cmd;
  2279. response.debug && self.responseDebug(response);
  2280. self.setCustomHeaderByXhr(xhr);
  2281. if (raw) {
  2282. self.abortXHR(xhr);
  2283. response && response.debug && self.debug('backend-debug', response);
  2284. return dfrd.resolve(response);
  2285. }
  2286. if (!response) {
  2287. return dfrd.reject({error :['errResponse', 'errDataEmpty']}, xhr, response);
  2288. } else if (!$.isPlainObject(response)) {
  2289. return dfrd.reject({error :['errResponse', 'errDataNotJSON']}, xhr, response);
  2290. } else if (response.error) {
  2291. if (isOpen) {
  2292. // check leafRoots
  2293. $.each(self.leafRoots, function(phash, roots) {
  2294. self.leafRoots[phash] = $.grep(roots, function(h) { return h !== data.target; });
  2295. });
  2296. }
  2297. return dfrd.reject({error :response.error}, xhr, response);
  2298. }
  2299. var resolve = function() {
  2300. var pushLeafRoots = function(name) {
  2301. if (self.leafRoots[data.target] && response[name]) {
  2302. $.each(self.leafRoots[data.target], function(i, h) {
  2303. var root;
  2304. if (root = self.file(h)) {
  2305. response[name].push(root);
  2306. }
  2307. });
  2308. }
  2309. },
  2310. setTextMimes = function() {
  2311. self.textMimes = {};
  2312. $.each(self.res('mimes', 'text'), function() {
  2313. self.textMimes[this.toLowerCase()] = true;
  2314. });
  2315. },
  2316. actionTarget;
  2317. if (isOpen) {
  2318. pushLeafRoots('files');
  2319. } else if (cmd === 'tree') {
  2320. pushLeafRoots('tree');
  2321. }
  2322. response = self.normalize(response);
  2323. if (!self.validResponse(cmd, response)) {
  2324. return dfrd.reject({error :(response.norError || 'errResponse')}, xhr, response);
  2325. }
  2326. if (isOpen) {
  2327. if (!self.api) {
  2328. self.api = response.api || 1;
  2329. if (self.api == '2.0' && typeof response.options.uploadMaxSize !== 'undefined') {
  2330. self.api = '2.1';
  2331. }
  2332. self.newAPI = self.api >= 2;
  2333. self.oldAPI = !self.newAPI;
  2334. }
  2335. if (response.textMimes && Array.isArray(response.textMimes)) {
  2336. self.resources.mimes.text = response.textMimes;
  2337. setTextMimes();
  2338. }
  2339. !self.textMimes && setTextMimes();
  2340. if (response.options) {
  2341. cwdOptions = Object.assign({}, cwdOptionsDefault, response.options);
  2342. }
  2343. if (response.netDrivers) {
  2344. self.netDrivers = response.netDrivers;
  2345. }
  2346. if (response.maxTargets) {
  2347. self.maxTargets = response.maxTargets;
  2348. }
  2349. if (!!data.init) {
  2350. self.uplMaxSize = self.returnBytes(response.uplMaxSize);
  2351. self.uplMaxFile = !!response.uplMaxFile? Math.min(parseInt(response.uplMaxFile), 50) : 20;
  2352. }
  2353. }
  2354. if (typeof prepare === 'function') {
  2355. prepare(response);
  2356. }
  2357. if (navigate) {
  2358. actionTarget = navigate.target || 'added';
  2359. if (response[actionTarget] && response[actionTarget].length) {
  2360. self.one(cmd + 'done', function() {
  2361. var targets = response[actionTarget],
  2362. newItems = self.findCwdNodes(targets),
  2363. inCwdHashes = function() {
  2364. var cwdHash = self.cwd().hash;
  2365. return $.map(targets, function(f) { return (f.phash && cwdHash === f.phash)? f.hash : null; });
  2366. },
  2367. hashes = inCwdHashes(),
  2368. makeToast = function(t) {
  2369. var node = void(0),
  2370. data = t.action? t.action.data : void(0),
  2371. cmd, msg, done;
  2372. if ((data || hashes.length) && t.action && (msg = t.action.msg) && (cmd = t.action.cmd) && (!t.action.cwdNot || t.action.cwdNot !== self.cwd().hash)) {
  2373. done = t.action.done;
  2374. data = t.action.data;
  2375. node = $('<div></div>')
  2376. .append(
  2377. $('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all elfinder-tabstop"><span class="ui-button-text">'
  2378. +self.i18n(msg)
  2379. +'</span></button>')
  2380. .on('mouseenter mouseleave', function(e) {
  2381. $(this).toggleClass('ui-state-hover', e.type == 'mouseenter');
  2382. })
  2383. .on('click', function() {
  2384. self.exec(cmd, data || hashes, {_userAction: true, _currentType: 'toast', _currentNode: $(this) });
  2385. if (done) {
  2386. self.one(cmd+'done', function() {
  2387. if (typeof done === 'function') {
  2388. done();
  2389. } else if (done === 'select') {
  2390. self.trigger('selectfiles', {files : inCwdHashes()});
  2391. }
  2392. });
  2393. }
  2394. })
  2395. );
  2396. }
  2397. delete t.action;
  2398. t.extNode = node;
  2399. return t;
  2400. };
  2401. if (! navigate.toast) {
  2402. navigate.toast = {};
  2403. }
  2404. !navigate.noselect && self.trigger('selectfiles', {files : self.searchStatus.state > 1 ? $.map(targets, function(f) { return f.hash; }) : hashes});
  2405. if (newItems.length) {
  2406. if (!navigate.noscroll) {
  2407. newItems.first().trigger('scrolltoview', {blink : false});
  2408. self.resources.blink(newItems, 'lookme');
  2409. }
  2410. if ($.isPlainObject(navigate.toast.incwd)) {
  2411. self.toast(makeToast(navigate.toast.incwd));
  2412. }
  2413. } else {
  2414. if ($.isPlainObject(navigate.toast.inbuffer)) {
  2415. self.toast(makeToast(navigate.toast.inbuffer));
  2416. }
  2417. }
  2418. });
  2419. }
  2420. }
  2421. dfrd.resolve(response);
  2422. response.debug && self.debug('backend-debug', response);
  2423. };
  2424. self.abortXHR(xhr);
  2425. lazy? self.lazy(resolve) : resolve();
  2426. },
  2427. xhr, _xhr,
  2428. xhrAbort = function(e) {
  2429. if (xhr && xhr.state() === 'pending') {
  2430. self.abortXHR(xhr, { quiet: true , abort: true });
  2431. if (!e || (e.type !== 'unload' && e.type !== 'destroy')) {
  2432. self.autoSync();
  2433. }
  2434. }
  2435. },
  2436. abort = function(e){
  2437. self.trigger(cmd + 'done');
  2438. if (e.type == 'autosync') {
  2439. if (e.data.action != 'stop') return;
  2440. } else if (e.type != 'unload' && e.type != 'destroy' && e.type != 'openxhrabort') {
  2441. if (!e.data.added || !e.data.added.length) {
  2442. return;
  2443. }
  2444. }
  2445. xhrAbort(e);
  2446. },
  2447. request = function(mode) {
  2448. var queueAbort = function() {
  2449. syncOnFail = false;
  2450. dfrd.reject();
  2451. };
  2452. if (mode) {
  2453. if (mode === 'cmd') {
  2454. return cmd;
  2455. }
  2456. }
  2457. if (isOpen) {
  2458. if (currentOpenCmd && currentOpenCmd.state() === 'pending') {
  2459. if (currentOpenCmd._target === data.target) {
  2460. return dfrd.reject('openabort');
  2461. } else {
  2462. if (currentOpenCmd.xhr) {
  2463. currentOpenCmd.xhr.queueAbort();
  2464. } else {
  2465. currentOpenCmd.reject('openabort');
  2466. }
  2467. }
  2468. }
  2469. currentOpenCmd = dfrd;
  2470. currentOpenCmd._target = data.target;
  2471. }
  2472. dfrd.always(function() {
  2473. delete options.headers['X-elFinderReqid'];
  2474. if (isOpen) {
  2475. currentOpenCmd = null;
  2476. }
  2477. }).fail(function(error, xhr, response) {
  2478. var errData, errMsg;
  2479. if (isOpen && error === 'openabort') {
  2480. error = '';
  2481. syncOnFail = false;
  2482. }
  2483. errData = {
  2484. cmd: cmd,
  2485. err: error,
  2486. xhr: xhr,
  2487. rc: response
  2488. };
  2489. // unset this cmd queue when user canceling
  2490. // see notify : function - `cancel.reject(0);`
  2491. if (error === 0) {
  2492. if (requestQueue.length) {
  2493. requestQueue = $.grep(requestQueue, function(req) {
  2494. return (req('cmd') === cmd) ? false : true;
  2495. });
  2496. }
  2497. }
  2498. // trigger "requestError" event
  2499. self.trigger('requestError', errData);
  2500. if (errData._getEvent && errData._getEvent().isDefaultPrevented()) {
  2501. deffail = false;
  2502. syncOnFail = false;
  2503. if (error) {
  2504. error.error = '';
  2505. }
  2506. }
  2507. // abort xhr
  2508. xhrAbort();
  2509. if (isOpen) {
  2510. openDir = self.file(data.target);
  2511. openDir && openDir.volumeid && self.isRoot(openDir) && delete self.volumeExpires[openDir.volumeid];
  2512. }
  2513. self.trigger(cmd + 'fail', response);
  2514. errMsg = (typeof error === 'object')? error.error : error;
  2515. if (errMsg) {
  2516. deffail ? self.error(errMsg) : self.debug('error', self.i18n(errMsg));
  2517. }
  2518. syncOnFail && self.sync();
  2519. });
  2520. if (!cmd) {
  2521. syncOnFail = false;
  2522. return dfrd.reject({error :'errCmdReq'});
  2523. }
  2524. if (self.maxTargets && data.targets && data.targets.length > self.maxTargets) {
  2525. syncOnFail = false;
  2526. return dfrd.reject({error :['errMaxTargets', self.maxTargets]});
  2527. }
  2528. defdone && dfrd.done(done);
  2529. // quiet abort not completed "open" requests
  2530. if (isOpen) {
  2531. while ((_xhr = queue.pop())) {
  2532. _xhr.queueAbort();
  2533. }
  2534. if (cwd !== data.target) {
  2535. while ((_xhr = cwdQueue.pop())) {
  2536. _xhr.queueAbort();
  2537. }
  2538. }
  2539. }
  2540. // trigger abort autoSync for commands to add the item
  2541. if ($.inArray(cmd, (self.cmdsToAdd + ' autosync').split(' ')) !== -1) {
  2542. if (cmd !== 'autosync') {
  2543. self.autoSync('stop');
  2544. dfrd.always(function() {
  2545. self.autoSync();
  2546. });
  2547. }
  2548. self.trigger('openxhrabort');
  2549. }
  2550. delete options.preventFail;
  2551. if (self.api >= 2.1029) {
  2552. if (useCache) {
  2553. options.headers['X-elFinderReqid'] = reqId;
  2554. } else {
  2555. Object.assign(options.data, { reqid : reqId });
  2556. }
  2557. }
  2558. // function for set value of this syncOnFail
  2559. dfrd.syncOnFail = function(state) {
  2560. syncOnFail = !!state;
  2561. };
  2562. requestCnt++;
  2563. dfrd.xhr = xhr = self.transport.send(options).always(function() {
  2564. // set responseURL from native xhr object
  2565. if (options._xhr && typeof options._xhr.responseURL !== 'undefined') {
  2566. xhr.responseURL = options._xhr.responseURL || '';
  2567. }
  2568. --requestCnt;
  2569. if (requestQueue.length) {
  2570. requestQueue.shift()();
  2571. }
  2572. }).fail(error).done(success);
  2573. if (self.api >= 2.1029) {
  2574. xhr._requestId = reqId;
  2575. }
  2576. if (isOpen || (data.compare && cmd === 'info')) {
  2577. // regist function queueAbort
  2578. xhr.queueAbort = queueAbort;
  2579. // add autoSync xhr into queue
  2580. queue.unshift(xhr);
  2581. // bind abort()
  2582. data.compare && self.bind(self.cmdsToAdd + ' autosync openxhrabort', abort);
  2583. dfrd.always(function() {
  2584. var ndx = $.inArray(xhr, queue);
  2585. data.compare && self.unbind(self.cmdsToAdd + ' autosync openxhrabort', abort);
  2586. ndx !== -1 && queue.splice(ndx, 1);
  2587. });
  2588. } else if ($.inArray(cmd, self.abortCmdsOnOpen) !== -1) {
  2589. // regist function queueAbort
  2590. xhr.queueAbort = queueAbort;
  2591. // add "open" xhr, only cwd xhr into queue
  2592. cwdQueue.unshift(xhr);
  2593. dfrd.always(function() {
  2594. var ndx = $.inArray(xhr, cwdQueue);
  2595. ndx !== -1 && cwdQueue.splice(ndx, 1);
  2596. });
  2597. }
  2598. // abort pending xhr on window unload or elFinder destroy
  2599. self.bind('unload destroy', abort);
  2600. dfrd.always(function() {
  2601. self.unbind('unload destroy', abort);
  2602. });
  2603. return dfrd;
  2604. },
  2605. queueingRequest = function() {
  2606. // show notify
  2607. if (notify.type && notify.cnt) {
  2608. if (cancel) {
  2609. notify.cancel = dfrd;
  2610. opts.eachCancel && (notify.id = +new Date());
  2611. }
  2612. timeout = setTimeout(function() {
  2613. // start fake count up
  2614. progTm = setTimeout(progFakeUp, 1000);
  2615. self.notify(notify);
  2616. hasNotify = true;
  2617. dfrd.always(function() {
  2618. notify.cnt = -(parseInt(notify.cnt)||0);
  2619. self.notify(notify);
  2620. hasNotify = false;
  2621. });
  2622. }, self.notifyDelay);
  2623. dfrd.always(function() {
  2624. clearTimeout(timeout);
  2625. });
  2626. }
  2627. // queueing
  2628. if (requestCnt < requestMaxConn) {
  2629. // do request
  2630. return request();
  2631. } else {
  2632. if (isOpen) {
  2633. requestQueue.unshift(request);
  2634. } else {
  2635. requestQueue.push(request);
  2636. }
  2637. return dfrd;
  2638. }
  2639. },
  2640. progFakeUp = function() {
  2641. var add;
  2642. if (hasNotify && progTm) {
  2643. add = 1 * notify.cnt;
  2644. progTm = null;
  2645. self.notify({
  2646. type: notify.type,
  2647. progress: add,
  2648. cnt: 0,
  2649. hideCnt: notify.hideCnt
  2650. });
  2651. prog += add;
  2652. if ((prog / notify.cnt) < 80) {
  2653. progTm = setTimeout(progFakeUp, 500);
  2654. }
  2655. }
  2656. },
  2657. bindData = {opts: opts, result: true},
  2658. openDir;
  2659. // prevent request initial request is completed
  2660. if (!self.api && !data.init) {
  2661. syncOnFail = false;
  2662. return dfrd.reject();
  2663. }
  2664. // trigger "request.cmd" that callback be able to cancel request by substituting "false" for "event.data.result"
  2665. self.trigger('request.' + cmd, bindData, true);
  2666. if (! bindData.result) {
  2667. self.trigger(cmd + 'done');
  2668. return dfrd.reject();
  2669. } else if (typeof bindData.result === 'object' && bindData.result.promise) {
  2670. bindData.result
  2671. .done(queueingRequest)
  2672. .fail(function() {
  2673. self.trigger(cmd + 'done');
  2674. dfrd.reject();
  2675. });
  2676. return dfrd;
  2677. }
  2678. return queueingRequest();
  2679. };
  2680. /**
  2681. * Call cache()
  2682. * Store info about files/dirs in "files" object.
  2683. *
  2684. * @param Array files
  2685. * @param String type
  2686. * @return void
  2687. */
  2688. this.cache = function(dataArray, type) {
  2689. if (! Array.isArray(dataArray)) {
  2690. dataArray = [ dataArray ];
  2691. }
  2692. cache(dataArray, type);
  2693. };
  2694. /**
  2695. * Update file object caches by respose data object
  2696. *
  2697. * @param Object respose data object
  2698. * @return void
  2699. */
  2700. this.updateCache = function(data) {
  2701. if ($.isPlainObject(data)) {
  2702. data.files && data.files.length && cache(data.files, 'files');
  2703. data.tree && data.tree.length && cache(data.tree, 'tree');
  2704. data.removed && data.removed.length && remove(data.removed);
  2705. data.added && data.added.length && cache(data.added, 'add');
  2706. data.changed && data.changed.length && cache(data.changed, 'change');
  2707. }
  2708. };
  2709. /**
  2710. * Compare current files cache with new files and return diff
  2711. *
  2712. * @param Array new files
  2713. * @param String target folder hash
  2714. * @param Array exclude properties to compare
  2715. * @return Object
  2716. */
  2717. this.diff = function(incoming, onlydir, excludeProps) {
  2718. var raw = {},
  2719. added = [],
  2720. removed = [],
  2721. changed = [],
  2722. excludes = null,
  2723. isChanged = function(hash) {
  2724. var l = changed.length;
  2725. while (l--) {
  2726. if (changed[l].hash == hash) {
  2727. return true;
  2728. }
  2729. }
  2730. };
  2731. $.each(incoming, function(i, f) {
  2732. raw[f.hash] = f;
  2733. });
  2734. // make excludes object
  2735. if (excludeProps && excludeProps.length) {
  2736. excludes = {};
  2737. $.each(excludeProps, function() {
  2738. excludes[this] = true;
  2739. });
  2740. }
  2741. // find removed
  2742. $.each(files, function(hash, f) {
  2743. if (! raw[hash] && (! onlydir || f.phash === onlydir)) {
  2744. removed.push(hash);
  2745. }
  2746. });
  2747. // compare files
  2748. $.each(raw, function(hash, file) {
  2749. var origin = files[hash],
  2750. orgKeys = {},
  2751. chkKeyLen;
  2752. if (!origin) {
  2753. added.push(file);
  2754. } else {
  2755. // make orgKeys object
  2756. $.each(Object.keys(origin), function() {
  2757. orgKeys[this] = true;
  2758. });
  2759. $.each(file, function(prop) {
  2760. delete orgKeys[prop];
  2761. if (! excludes || ! excludes[prop]) {
  2762. if (file[prop] !== origin[prop]) {
  2763. changed.push(file);
  2764. orgKeys = {};
  2765. return false;
  2766. }
  2767. }
  2768. });
  2769. chkKeyLen = Object.keys(orgKeys).length;
  2770. if (chkKeyLen !== 0) {
  2771. if (excludes) {
  2772. $.each(orgKeys, function(prop) {
  2773. if (excludes[prop]) {
  2774. --chkKeyLen;
  2775. }
  2776. });
  2777. }
  2778. (chkKeyLen !== 0) && changed.push(file);
  2779. }
  2780. }
  2781. });
  2782. // parents of removed dirs mark as changed (required for tree correct work)
  2783. $.each(removed, function(i, hash) {
  2784. var file = files[hash],
  2785. phash = file.phash;
  2786. if (phash
  2787. && file.mime == 'directory'
  2788. && $.inArray(phash, removed) === -1
  2789. && raw[phash]
  2790. && !isChanged(phash)) {
  2791. changed.push(raw[phash]);
  2792. }
  2793. });
  2794. return {
  2795. added : added,
  2796. removed : removed,
  2797. changed : changed
  2798. };
  2799. };
  2800. /**
  2801. * Sync content
  2802. *
  2803. * @return jQuery.Deferred
  2804. */
  2805. this.sync = function(onlydir, polling) {
  2806. this.autoSync('stop');
  2807. var self = this,
  2808. compare = function(){
  2809. var c = '', cnt = 0, mtime = 0;
  2810. if (onlydir && polling) {
  2811. $.each(files, function(h, f) {
  2812. if (f.phash && f.phash === onlydir) {
  2813. ++cnt;
  2814. mtime = Math.max(mtime, f.ts);
  2815. }
  2816. c = cnt+':'+mtime;
  2817. });
  2818. }
  2819. return c;
  2820. },
  2821. comp = compare(),
  2822. dfrd = $.Deferred().always(function() { !reqFail && self.trigger('sync'); }),
  2823. opts = [this.request({
  2824. data : {cmd : 'open', reload : 1, target : cwd, tree : (! onlydir && this.ui.tree) ? 1 : 0, compare : comp},
  2825. preventDefault : true
  2826. })],
  2827. exParents = function() {
  2828. var parents = [],
  2829. curRoot = self.file(self.root(cwd)),
  2830. curId = curRoot? curRoot.volumeid : null,
  2831. phash = self.cwd().phash,
  2832. isroot,pdir;
  2833. while(phash) {
  2834. if (pdir = self.file(phash)) {
  2835. if (phash.indexOf(curId) !== 0) {
  2836. parents.push( {target: phash, cmd: 'tree'} );
  2837. if (! self.isRoot(pdir)) {
  2838. parents.push( {target: phash, cmd: 'parents'} );
  2839. }
  2840. curRoot = self.file(self.root(phash));
  2841. curId = curRoot? curRoot.volumeid : null;
  2842. }
  2843. phash = pdir.phash;
  2844. } else {
  2845. phash = null;
  2846. }
  2847. }
  2848. return parents;
  2849. },
  2850. reqFail;
  2851. if (! onlydir && self.api >= 2) {
  2852. (cwd !== this.root()) && opts.push(this.request({
  2853. data : {cmd : 'parents', target : cwd},
  2854. preventDefault : true
  2855. }));
  2856. $.each(exParents(), function(i, data) {
  2857. opts.push(self.request({
  2858. data : {cmd : data.cmd, target : data.target},
  2859. preventDefault : true
  2860. }));
  2861. });
  2862. }
  2863. $.when.apply($, opts)
  2864. .fail(function(error, xhr) {
  2865. reqFail = (xhr && xhr.status != 200);
  2866. if (! polling || $.inArray('errOpen', error) !== -1) {
  2867. dfrd.reject(error);
  2868. self.parseError(error) && self.request({
  2869. data : {cmd : 'open', target : (self.lastDir('') || self.root()), tree : 1, init : 1},
  2870. notify : {type : 'open', cnt : 1, hideCnt : true}
  2871. });
  2872. } else {
  2873. dfrd.reject((error && xhr.status != 0)? error : void 0);
  2874. }
  2875. })
  2876. .done(function(odata) {
  2877. var pdata, argLen, i;
  2878. if (odata.cwd.compare) {
  2879. if (comp === odata.cwd.compare) {
  2880. return dfrd.reject();
  2881. }
  2882. }
  2883. // for 2nd and more requests
  2884. pdata = {tree : []};
  2885. // results marge of 2nd and more requests
  2886. argLen = arguments.length;
  2887. if (argLen > 1) {
  2888. for(i = 1; i < argLen; i++) {
  2889. if (arguments[i].tree && arguments[i].tree.length) {
  2890. pdata.tree.push.apply(pdata.tree, arguments[i].tree);
  2891. }
  2892. }
  2893. }
  2894. if (self.api < 2.1) {
  2895. if (! pdata.tree) {
  2896. pdata.tree = [];
  2897. }
  2898. pdata.tree.push(odata.cwd);
  2899. }
  2900. // data normalize
  2901. odata = self.normalize(odata);
  2902. if (!self.validResponse('open', odata)) {
  2903. return dfrd.reject((odata.norError || 'errResponse'));
  2904. }
  2905. pdata = self.normalize(pdata);
  2906. if (!self.validResponse('tree', pdata)) {
  2907. return dfrd.reject((pdata.norError || 'errResponse'));
  2908. }
  2909. var diff = self.diff(odata.files.concat(pdata && pdata.tree ? pdata.tree : []), onlydir);
  2910. diff.added.push(odata.cwd);
  2911. self.updateCache(diff);
  2912. // trigger events
  2913. diff.removed.length && self.remove(diff);
  2914. diff.added.length && self.add(diff);
  2915. diff.changed.length && self.change(diff);
  2916. return dfrd.resolve(diff);
  2917. })
  2918. .always(function() {
  2919. self.autoSync();
  2920. });
  2921. return dfrd;
  2922. };
  2923. this.upload = function(files) {
  2924. return this.transport.upload(files, this);
  2925. };
  2926. /**
  2927. * Bind keybord shortcut to keydown event
  2928. *
  2929. * @example
  2930. * elfinder.shortcut({
  2931. * pattern : 'ctrl+a',
  2932. * description : 'Select all files',
  2933. * callback : function(e) { ... },
  2934. * keypress : true|false (bind to keypress instead of keydown)
  2935. * })
  2936. *
  2937. * @param Object shortcut config
  2938. * @return elFinder
  2939. */
  2940. this.shortcut = function(s) {
  2941. var patterns, pattern, code, i, parts;
  2942. if (this.options.allowShortcuts && s.pattern && $.isFunction(s.callback)) {
  2943. patterns = s.pattern.toUpperCase().split(/\s+/);
  2944. for (i= 0; i < patterns.length; i++) {
  2945. pattern = patterns[i];
  2946. parts = pattern.split('+');
  2947. code = (code = parts.pop()).length == 1
  2948. ? (code > 0 ? code : code.charCodeAt(0))
  2949. : (code > 0 ? code : $.ui.keyCode[code]);
  2950. if (code && !shortcuts[pattern]) {
  2951. shortcuts[pattern] = {
  2952. keyCode : code,
  2953. altKey : $.inArray('ALT', parts) != -1,
  2954. ctrlKey : $.inArray('CTRL', parts) != -1,
  2955. shiftKey : $.inArray('SHIFT', parts) != -1,
  2956. type : s.type || 'keydown',
  2957. callback : s.callback,
  2958. description : s.description,
  2959. pattern : pattern
  2960. };
  2961. }
  2962. }
  2963. }
  2964. return this;
  2965. };
  2966. /**
  2967. * Registered shortcuts
  2968. *
  2969. * @type Object
  2970. **/
  2971. this.shortcuts = function() {
  2972. var ret = [];
  2973. $.each(shortcuts, function(i, s) {
  2974. ret.push([s.pattern, self.i18n(s.description)]);
  2975. });
  2976. return ret;
  2977. };
  2978. /**
  2979. * Get/set clipboard content.
  2980. * Return new clipboard content.
  2981. *
  2982. * @example
  2983. * this.clipboard([]) - clean clipboard
  2984. * this.clipboard([{...}, {...}], true) - put 2 files in clipboard and mark it as cutted
  2985. *
  2986. * @param Array new files hashes
  2987. * @param Boolean cut files?
  2988. * @return Array
  2989. */
  2990. this.clipboard = function(hashes, cut) {
  2991. var map = function() { return $.map(clipboard, function(f) { return f.hash; }); };
  2992. if (hashes !== void(0)) {
  2993. clipboard.length && this.trigger('unlockfiles', {files : map()});
  2994. remember = {};
  2995. clipboard = $.map(hashes||[], function(hash) {
  2996. var file = files[hash];
  2997. if (file) {
  2998. remember[hash] = true;
  2999. return {
  3000. hash : hash,
  3001. phash : file.phash,
  3002. name : file.name,
  3003. mime : file.mime,
  3004. read : file.read,
  3005. locked : file.locked,
  3006. cut : !!cut
  3007. };
  3008. }
  3009. return null;
  3010. });
  3011. this.trigger('changeclipboard', {clipboard : clipboard.slice(0, clipboard.length)});
  3012. cut && this.trigger('lockfiles', {files : map()});
  3013. }
  3014. // return copy of clipboard instead of refrence
  3015. return clipboard.slice(0, clipboard.length);
  3016. };
  3017. /**
  3018. * Return true if command enabled
  3019. *
  3020. * @param String command name
  3021. * @param String|void hash for check of own volume's disabled cmds
  3022. * @return Boolean
  3023. */
  3024. this.isCommandEnabled = function(name, dstHash) {
  3025. var disabled, cmd,
  3026. cvid = self.cwd().volumeid || '';
  3027. // In serach results use selected item hash to check
  3028. if (!dstHash && self.searchStatus.state > 1 && self.selected().length) {
  3029. dstHash = self.selected()[0];
  3030. }
  3031. if (dstHash && (! cvid || dstHash.indexOf(cvid) !== 0)) {
  3032. disabled = self.option('disabledFlip', dstHash);
  3033. //if (! disabled) {
  3034. // disabled = {};
  3035. //}
  3036. } else {
  3037. disabled = cwdOptions.disabledFlip/* || {}*/;
  3038. }
  3039. cmd = this._commands[name];
  3040. return cmd ? (cmd.alwaysEnabled || !disabled[name]) : false;
  3041. };
  3042. /**
  3043. * Exec command and return result;
  3044. *
  3045. * @param String command name
  3046. * @param String|Array usualy files hashes
  3047. * @param String|Array command options
  3048. * @param String|void hash for enabled check of own volume's disabled cmds
  3049. * @return $.Deferred
  3050. */
  3051. this.exec = function(cmd, files, opts, dstHash) {
  3052. var dfrd, resType;
  3053. // apply commandMap for keyboard shortcut
  3054. if (!dstHash && this.commandMap[cmd] && this.commandMap[cmd] !== 'hidden') {
  3055. cmd = this.commandMap[cmd];
  3056. }
  3057. if (cmd === 'open') {
  3058. if (this.searchStatus.state || this.searchStatus.ininc) {
  3059. this.trigger('searchend', { noupdate: true });
  3060. }
  3061. this.autoSync('stop');
  3062. }
  3063. if (!dstHash && files) {
  3064. if ($.isArray(files)) {
  3065. if (files.length) {
  3066. dstHash = files[0];
  3067. }
  3068. } else {
  3069. dstHash = files;
  3070. }
  3071. }
  3072. dfrd = this._commands[cmd] && this.isCommandEnabled(cmd, dstHash)
  3073. ? this._commands[cmd].exec(files, opts)
  3074. : $.Deferred().reject('errUnknownCmd');
  3075. resType = typeof dfrd;
  3076. if (!(resType === 'object' && dfrd.promise)) {
  3077. self.debug('warning', '"cmd.exec()" should be returned "$.Deferred" but cmd "' + cmd + '" returned "' + resType + '"');
  3078. dfrd = $.Deferred().resolve();
  3079. }
  3080. this.trigger('exec', { dfrd : dfrd, cmd : cmd, files : files, opts : opts, dstHash : dstHash });
  3081. return dfrd;
  3082. };
  3083. /**
  3084. * Create and return dialog.
  3085. *
  3086. * @param String|DOMElement dialog content
  3087. * @param Object dialog options
  3088. * @return jQuery
  3089. */
  3090. this.dialog = function(content, options) {
  3091. var dialog = $('<div></div>').append(content).appendTo(node).elfinderdialog(options, self),
  3092. dnode = dialog.closest('.ui-dialog'),
  3093. resize = function(){
  3094. ! dialog.data('draged') && dialog.is(':visible') && dialog.elfinderdialog('posInit');
  3095. };
  3096. if (dnode.length) {
  3097. self.bind('resize', resize);
  3098. dnode.on('remove', function() {
  3099. self.unbind('resize', resize);
  3100. });
  3101. }
  3102. return dialog;
  3103. };
  3104. /**
  3105. * Create and return toast.
  3106. *
  3107. * @param Object toast options - see ui/toast.js
  3108. * @return jQuery
  3109. */
  3110. this.toast = function(options) {
  3111. return $('<div class="ui-front"></div>').appendTo(this.ui.toast).elfindertoast(options || {}, this);
  3112. };
  3113. /**
  3114. * Return UI widget or node
  3115. *
  3116. * @param String ui name
  3117. * @return jQuery
  3118. */
  3119. this.getUI = function(ui) {
  3120. return ui? (this.ui[ui] || $()) : node;
  3121. };
  3122. /**
  3123. * Return elFinder.command instance or instances array
  3124. *
  3125. * @param String command name
  3126. * @return Object | Array
  3127. */
  3128. this.getCommand = function(name) {
  3129. return name === void(0) ? this._commands : this._commands[name];
  3130. };
  3131. /**
  3132. * Resize elfinder node
  3133. *
  3134. * @param String|Number width
  3135. * @param String|Number height
  3136. * @return void
  3137. */
  3138. this.resize = function(w, h) {
  3139. var getMargin = function() {
  3140. var m = node.outerHeight(true) - node.innerHeight(),
  3141. p = node;
  3142. while(p.get(0) !== heightBase.get(0)) {
  3143. p = p.parent();
  3144. m += p.outerHeight(true) - p.innerHeight();
  3145. if (! p.parent().length) {
  3146. // reached the document
  3147. break;
  3148. }
  3149. }
  3150. return m;
  3151. },
  3152. fit = ! node.hasClass('ui-resizable'),
  3153. prv = node.data('resizeSize') || {w: 0, h: 0},
  3154. mt, size = {};
  3155. if (heightBase && heightBase.data('resizeTm')) {
  3156. clearTimeout(heightBase.data('resizeTm'));
  3157. }
  3158. if (typeof h === 'string') {
  3159. if (mt = h.match(/^([0-9.]+)%$/)) {
  3160. // setup heightBase
  3161. if (! heightBase || ! heightBase.length) {
  3162. heightBase = $(window);
  3163. }
  3164. if (! heightBase.data('marginToMyNode')) {
  3165. heightBase.data('marginToMyNode', getMargin());
  3166. }
  3167. if (! heightBase.data('fitToBaseFunc')) {
  3168. heightBase.data('fitToBaseFunc', function(e) {
  3169. var tm = heightBase.data('resizeTm');
  3170. e.preventDefault();
  3171. e.stopPropagation();
  3172. tm && cancelAnimationFrame(tm);
  3173. if (! node.hasClass('elfinder-fullscreen') && (!self.UA.Mobile || heightBase.data('rotated') !== self.UA.Rotated)) {
  3174. heightBase.data('rotated', self.UA.Rotated);
  3175. heightBase.data('resizeTm', requestAnimationFrame(function() {
  3176. self.restoreSize();
  3177. }));
  3178. }
  3179. });
  3180. }
  3181. if (typeof heightBase.data('rotated') === 'undefined') {
  3182. heightBase.data('rotated', self.UA.Rotated);
  3183. }
  3184. h = heightBase.height() * (mt[1] / 100) - heightBase.data('marginToMyNode');
  3185. heightBase.off('resize.' + self.namespace, heightBase.data('fitToBaseFunc'));
  3186. fit && heightBase.on('resize.' + self.namespace, heightBase.data('fitToBaseFunc'));
  3187. }
  3188. }
  3189. node.css({ width : w, height : parseInt(h) });
  3190. size.w = Math.round(node.width());
  3191. size.h = Math.round(node.height());
  3192. node.data('resizeSize', size);
  3193. if (size.w !== prv.w || size.h !== prv.h) {
  3194. node.trigger('resize');
  3195. this.trigger('resize', {width : size.w, height : size.h});
  3196. }
  3197. };
  3198. /**
  3199. * Restore elfinder node size
  3200. *
  3201. * @return elFinder
  3202. */
  3203. this.restoreSize = function() {
  3204. this.resize(width, height);
  3205. };
  3206. this.show = function() {
  3207. node.show();
  3208. this.enable().trigger('show');
  3209. };
  3210. this.hide = function() {
  3211. if (this.options.enableAlways) {
  3212. prevEnabled = enabled;
  3213. enabled = false;
  3214. }
  3215. this.disable();
  3216. this.trigger('hide');
  3217. node.hide();
  3218. };
  3219. /**
  3220. * Lazy execution function
  3221. *
  3222. * @param Object function
  3223. * @param Number delay
  3224. * @param Object options
  3225. * @return Object jQuery.Deferred
  3226. */
  3227. this.lazy = function(func, delay, opts) {
  3228. var busy = function(state) {
  3229. var cnt = node.data('lazycnt'),
  3230. repaint;
  3231. if (state) {
  3232. repaint = node.data('lazyrepaint')? false : opts.repaint;
  3233. if (! cnt) {
  3234. node.data('lazycnt', 1)
  3235. .addClass('elfinder-processing');
  3236. } else {
  3237. node.data('lazycnt', ++cnt);
  3238. }
  3239. if (repaint) {
  3240. node.data('lazyrepaint', true).css('display'); // force repaint
  3241. }
  3242. } else {
  3243. if (cnt && cnt > 1) {
  3244. node.data('lazycnt', --cnt);
  3245. } else {
  3246. repaint = node.data('lazyrepaint');
  3247. node.data('lazycnt', 0)
  3248. .removeData('lazyrepaint')
  3249. .removeClass('elfinder-processing');
  3250. repaint && node.css('display'); // force repaint;
  3251. self.trigger('lazydone');
  3252. }
  3253. }
  3254. },
  3255. dfd = $.Deferred(),
  3256. callFunc = function() {
  3257. dfd.resolve(func.call(dfd));
  3258. busy(false);
  3259. };
  3260. delay = delay || 0;
  3261. opts = opts || {};
  3262. busy(true);
  3263. if (delay) {
  3264. setTimeout(callFunc, delay);
  3265. } else {
  3266. requestAnimationFrame(callFunc);
  3267. }
  3268. return dfd;
  3269. };
  3270. /**
  3271. * Destroy this elFinder instance
  3272. *
  3273. * @return void
  3274. **/
  3275. this.destroy = function() {
  3276. if (node && node[0].elfinder) {
  3277. node.hasClass('elfinder-fullscreen') && self.toggleFullscreen(node);
  3278. this.options.syncStart = false;
  3279. this.autoSync('forcestop');
  3280. this.trigger('destroy').disable();
  3281. clipboard = [];
  3282. selected = [];
  3283. listeners = {};
  3284. shortcuts = {};
  3285. $(window).off('.' + namespace);
  3286. $(document).off('.' + namespace);
  3287. self.trigger = function(){};
  3288. $(beeper).remove();
  3289. node.off()
  3290. .removeData()
  3291. .empty()
  3292. .append(prevContent.contents())
  3293. .attr('class', prevContent.attr('class'))
  3294. .attr('style', prevContent.attr('style'));
  3295. delete node[0].elfinder;
  3296. // restore kept events
  3297. $.each(prevEvents, function(n, arr) {
  3298. $.each(arr, function(i, o) {
  3299. node.on(o.type + (o.namespace? '.'+o.namespace : ''), o.selector, o.handler);
  3300. });
  3301. });
  3302. }
  3303. };
  3304. /**
  3305. * Start or stop auto sync
  3306. *
  3307. * @param String|Bool stop
  3308. * @return void
  3309. */
  3310. this.autoSync = function(mode) {
  3311. var sync;
  3312. if (self.options.sync >= 1000) {
  3313. if (syncInterval) {
  3314. clearTimeout(syncInterval);
  3315. syncInterval = null;
  3316. self.trigger('autosync', {action : 'stop'});
  3317. }
  3318. if (mode === 'stop') {
  3319. ++autoSyncStop;
  3320. } else {
  3321. autoSyncStop = Math.max(0, --autoSyncStop);
  3322. }
  3323. if (autoSyncStop || mode === 'forcestop' || ! self.options.syncStart) {
  3324. return;
  3325. }
  3326. // run interval sync
  3327. sync = function(start){
  3328. var timeout;
  3329. if (cwdOptions.syncMinMs && (start || syncInterval)) {
  3330. start && self.trigger('autosync', {action : 'start'});
  3331. timeout = Math.max(self.options.sync, cwdOptions.syncMinMs);
  3332. syncInterval && clearTimeout(syncInterval);
  3333. syncInterval = setTimeout(function() {
  3334. var dosync = true, hash = cwd, cts;
  3335. if (cwdOptions.syncChkAsTs && files[hash] && (cts = files[hash].ts)) {
  3336. self.request({
  3337. data : {cmd : 'info', targets : [hash], compare : cts, reload : 1},
  3338. preventDefault : true
  3339. })
  3340. .done(function(data){
  3341. var ts;
  3342. dosync = true;
  3343. if (data.compare) {
  3344. ts = data.compare;
  3345. if (ts == cts) {
  3346. dosync = false;
  3347. }
  3348. }
  3349. if (dosync) {
  3350. self.sync(hash).always(function(){
  3351. if (ts) {
  3352. // update ts for cache clear etc.
  3353. files[hash].ts = ts;
  3354. }
  3355. sync();
  3356. });
  3357. } else {
  3358. sync();
  3359. }
  3360. })
  3361. .fail(function(error, xhr){
  3362. var err = self.parseError(error);
  3363. if (err && xhr.status != 0) {
  3364. self.error(err);
  3365. if (Array.isArray(err) && $.inArray('errOpen', err) !== -1) {
  3366. self.request({
  3367. data : {cmd : 'open', target : (self.lastDir('') || self.root()), tree : 1, init : 1},
  3368. notify : {type : 'open', cnt : 1, hideCnt : true}
  3369. });
  3370. }
  3371. } else {
  3372. syncInterval = setTimeout(function() {
  3373. sync();
  3374. }, timeout);
  3375. }
  3376. });
  3377. } else {
  3378. self.sync(cwd, true).always(function(){
  3379. sync();
  3380. });
  3381. }
  3382. }, timeout);
  3383. }
  3384. };
  3385. sync(true);
  3386. }
  3387. };
  3388. /**
  3389. * Return bool is inside work zone of specific point
  3390. *
  3391. * @param Number event.pageX
  3392. * @param Number event.pageY
  3393. * @return Bool
  3394. */
  3395. this.insideWorkzone = function(x, y, margin) {
  3396. var rectangle = this.getUI('workzone').data('rectangle');
  3397. margin = margin || 1;
  3398. if (x < rectangle.left + margin
  3399. || x > rectangle.left + rectangle.width + margin
  3400. || y < rectangle.top + margin
  3401. || y > rectangle.top + rectangle.height + margin) {
  3402. return false;
  3403. }
  3404. return true;
  3405. };
  3406. /**
  3407. * Target ui node move to last of children of elFinder node fot to show front
  3408. *
  3409. * @param Object target Target jQuery node object
  3410. */
  3411. this.toFront = function(target) {
  3412. var nodes = node.children('.ui-front').removeClass('elfinder-frontmost'),
  3413. lastnode = nodes.last();
  3414. nodes.css('z-index', '');
  3415. $(target).addClass('ui-front elfinder-frontmost').css('z-index', lastnode.css('z-index') + 1);
  3416. };
  3417. /**
  3418. * Remove class 'elfinder-frontmost' and hide() to target ui node
  3419. *
  3420. * @param Object target Target jQuery node object
  3421. * @param Boolean nohide Do not hide
  3422. */
  3423. this.toHide =function(target, nohide) {
  3424. var tgt = $(target),
  3425. last;
  3426. !nohide && tgt.hide();
  3427. if (tgt.hasClass('elfinder-frontmost')) {
  3428. tgt.removeClass('elfinder-frontmost');
  3429. last = node.children('.ui-front:visible:not(.elfinder-frontmost)').last();
  3430. if (last.length) {
  3431. requestAnimationFrame(function() {
  3432. if (!node.children('.elfinder-frontmost:visible').length) {
  3433. self.toFront(last);
  3434. last.trigger('frontmost');
  3435. }
  3436. });
  3437. }
  3438. }
  3439. };
  3440. /**
  3441. * Return css object for maximize
  3442. *
  3443. * @return Object
  3444. */
  3445. this.getMaximizeCss = function() {
  3446. return {
  3447. width : '100%',
  3448. height : '100%',
  3449. margin : 0,
  3450. top : 0,
  3451. left : 0,
  3452. display : 'block',
  3453. position: 'fixed',
  3454. zIndex : Math.max(self.zIndex? (self.zIndex + 1) : 0 , 1000),
  3455. maxWidth : '',
  3456. maxHeight: ''
  3457. };
  3458. };
  3459. // Closure for togglefullscreen
  3460. (function() {
  3461. // check is in iframe
  3462. if (inFrame && self.UA.Fullscreen) {
  3463. self.UA.Fullscreen = false;
  3464. if (parentIframe && typeof parentIframe.attr('allowfullscreen') !== 'undefined') {
  3465. self.UA.Fullscreen = true;
  3466. }
  3467. }
  3468. var orgStyle, bodyOvf, resizeTm, fullElm, exitFull, toFull, funcObj,
  3469. cls = 'elfinder-fullscreen',
  3470. clsN = 'elfinder-fullscreen-native',
  3471. checkDialog = function() {
  3472. var t = 0,
  3473. l = 0;
  3474. $.each(node.children('.ui-dialog,.ui-draggable'), function(i, d) {
  3475. var $d = $(d),
  3476. pos = $d.position();
  3477. if (pos.top < 0) {
  3478. $d.css('top', t);
  3479. t += 20;
  3480. }
  3481. if (pos.left < 0) {
  3482. $d.css('left', l);
  3483. l += 20;
  3484. }
  3485. });
  3486. },
  3487. setFuncObj = function() {
  3488. var useFullscreen = self.storage('useFullscreen');
  3489. funcObj = self.UA.Fullscreen && (useFullscreen? useFullscreen > 0 : self.options.commandsOptions.fullscreen.mode === 'screen') ? {
  3490. // native full screen mode
  3491. fullElm: function() {
  3492. return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement || null;
  3493. },
  3494. exitFull: function() {
  3495. if (document.exitFullscreen) {
  3496. return document.exitFullscreen();
  3497. } else if (document.webkitExitFullscreen) {
  3498. return document.webkitExitFullscreen();
  3499. } else if (document.mozCancelFullScreen) {
  3500. return document.mozCancelFullScreen();
  3501. } else if (document.msExitFullscreen) {
  3502. return document.msExitFullscreen();
  3503. }
  3504. },
  3505. toFull: function(elem) {
  3506. if (elem.requestFullscreen) {
  3507. return elem.requestFullscreen();
  3508. } else if (elem.webkitRequestFullscreen) {
  3509. return elem.webkitRequestFullscreen();
  3510. } else if (elem.mozRequestFullScreen) {
  3511. return elem.mozRequestFullScreen();
  3512. } else if (elem.msRequestFullscreen) {
  3513. return elem.msRequestFullscreen();
  3514. }
  3515. return false;
  3516. }
  3517. } : {
  3518. // node element maximize mode
  3519. fullElm: function() {
  3520. var full;
  3521. if (node.hasClass(cls)) {
  3522. return node.get(0);
  3523. } else {
  3524. full = node.find('.' + cls);
  3525. if (full.length) {
  3526. return full.get(0);
  3527. }
  3528. }
  3529. return null;
  3530. },
  3531. exitFull: function() {
  3532. var elm;
  3533. $(window).off('resize.' + namespace, resize);
  3534. if (bodyOvf !== void(0)) {
  3535. $('body').css('overflow', bodyOvf);
  3536. }
  3537. bodyOvf = void(0);
  3538. if (orgStyle) {
  3539. elm = orgStyle.elm;
  3540. restoreStyle(elm);
  3541. $(elm).trigger('resize', {fullscreen: 'off'});
  3542. }
  3543. $(window).trigger('resize');
  3544. },
  3545. toFull: function(elem) {
  3546. bodyOvf = $('body').css('overflow') || '';
  3547. $('body').css('overflow', 'hidden');
  3548. $(elem).css(self.getMaximizeCss())
  3549. .addClass(cls)
  3550. .trigger('resize', {fullscreen: 'on'});
  3551. checkDialog();
  3552. $(window).on('resize.' + namespace, resize).trigger('resize');
  3553. return true;
  3554. }
  3555. };
  3556. },
  3557. restoreStyle = function(elem) {
  3558. if (orgStyle && orgStyle.elm == elem) {
  3559. $(elem).removeClass(cls + ' ' + clsN).attr('style', orgStyle.style);
  3560. orgStyle = null;
  3561. }
  3562. },
  3563. resize = function(e) {
  3564. var elm;
  3565. if (e.target === window) {
  3566. resizeTm && cancelAnimationFrame(resizeTm);
  3567. resizeTm = requestAnimationFrame(function() {
  3568. if (elm = funcObj.fullElm()) {
  3569. $(elm).trigger('resize', {fullscreen: 'on'});
  3570. }
  3571. });
  3572. }
  3573. };
  3574. setFuncObj();
  3575. $(document).on('fullscreenchange.' + namespace + ' webkitfullscreenchange.' + namespace + ' mozfullscreenchange.' + namespace + ' MSFullscreenChange.' + namespace, function(e){
  3576. if (self.UA.Fullscreen) {
  3577. var elm = funcObj.fullElm(),
  3578. win = $(window);
  3579. resizeTm && cancelAnimationFrame(resizeTm);
  3580. if (elm === null) {
  3581. win.off('resize.' + namespace, resize);
  3582. if (orgStyle) {
  3583. elm = orgStyle.elm;
  3584. restoreStyle(elm);
  3585. $(elm).trigger('resize', {fullscreen: 'off'});
  3586. }
  3587. } else {
  3588. $(elm).addClass(cls + ' ' + clsN)
  3589. .attr('style', 'width:100%; height:100%; margin:0; padding:0;')
  3590. .trigger('resize', {fullscreen: 'on'});
  3591. win.on('resize.' + namespace, resize);
  3592. checkDialog();
  3593. }
  3594. win.trigger('resize');
  3595. }
  3596. });
  3597. /**
  3598. * Toggle Full Scrren Mode
  3599. *
  3600. * @param Object target
  3601. * @param Bool full
  3602. * @return Object | Null DOM node object of current full scrren
  3603. */
  3604. self.toggleFullscreen = function(target, full) {
  3605. var elm = $(target).get(0),
  3606. curElm = null;
  3607. curElm = funcObj.fullElm();
  3608. if (curElm) {
  3609. if (curElm == elm) {
  3610. if (full === true) {
  3611. return curElm;
  3612. }
  3613. } else {
  3614. if (full === false) {
  3615. return curElm;
  3616. }
  3617. }
  3618. funcObj.exitFull();
  3619. return null;
  3620. } else {
  3621. if (full === false) {
  3622. return null;
  3623. }
  3624. }
  3625. setFuncObj();
  3626. orgStyle = {elm: elm, style: $(elm).attr('style')};
  3627. if (funcObj.toFull(elm) !== false) {
  3628. return elm;
  3629. } else {
  3630. orgStyle = null;
  3631. return null;
  3632. }
  3633. };
  3634. })();
  3635. // Closure for toggleMaximize
  3636. (function(){
  3637. var cls = 'elfinder-maximized',
  3638. resizeTm,
  3639. resize = function(e) {
  3640. if (e.target === window && e.data && e.data.elm) {
  3641. var elm = e.data.elm;
  3642. resizeTm && cancelAnimationFrame(resizeTm);
  3643. resizeTm = requestAnimationFrame(function() {
  3644. elm.trigger('resize', {maximize: 'on'});
  3645. });
  3646. }
  3647. },
  3648. exitMax = function(elm) {
  3649. $(window).off('resize.' + namespace, resize);
  3650. $('body').css('overflow', elm.data('bodyOvf'));
  3651. elm.removeClass(cls)
  3652. .attr('style', elm.data('orgStyle'))
  3653. .removeData('bodyOvf')
  3654. .removeData('orgStyle');
  3655. elm.trigger('resize', {maximize: 'off'});
  3656. },
  3657. toMax = function(elm) {
  3658. elm.data('bodyOvf', $('body').css('overflow') || '')
  3659. .data('orgStyle', elm.attr('style'))
  3660. .addClass(cls)
  3661. .css(self.getMaximizeCss());
  3662. $('body').css('overflow', 'hidden');
  3663. $(window).on('resize.' + namespace, {elm: elm}, resize);
  3664. elm.trigger('resize', {maximize: 'on'});
  3665. };
  3666. /**
  3667. * Toggle Maximize target node
  3668. *
  3669. * @param Object target
  3670. * @param Bool max
  3671. * @return void
  3672. */
  3673. self.toggleMaximize = function(target, max) {
  3674. var elm = $(target),
  3675. maximized = elm.hasClass(cls);
  3676. if (maximized) {
  3677. if (max === true) {
  3678. return;
  3679. }
  3680. exitMax(elm);
  3681. } else {
  3682. if (max === false) {
  3683. return;
  3684. }
  3685. toMax(elm);
  3686. }
  3687. };
  3688. })();
  3689. /************* init stuffs ****************/
  3690. Object.assign($.ui.keyCode, {
  3691. 'F1' : 112,
  3692. 'F2' : 113,
  3693. 'F3' : 114,
  3694. 'F4' : 115,
  3695. 'F5' : 116,
  3696. 'F6' : 117,
  3697. 'F7' : 118,
  3698. 'F8' : 119,
  3699. 'F9' : 120,
  3700. 'F10' : 121,
  3701. 'F11' : 122,
  3702. 'F12' : 123,
  3703. 'DIG0' : 48,
  3704. 'DIG1' : 49,
  3705. 'DIG2' : 50,
  3706. 'DIG3' : 51,
  3707. 'DIG4' : 52,
  3708. 'DIG5' : 53,
  3709. 'DIG6' : 54,
  3710. 'DIG7' : 55,
  3711. 'DIG8' : 56,
  3712. 'DIG9' : 57,
  3713. 'NUM0' : 96,
  3714. 'NUM1' : 97,
  3715. 'NUM2' : 98,
  3716. 'NUM3' : 99,
  3717. 'NUM4' : 100,
  3718. 'NUM5' : 101,
  3719. 'NUM6' : 102,
  3720. 'NUM7' : 103,
  3721. 'NUM8' : 104,
  3722. 'NUM9' : 105,
  3723. 'CONTEXTMENU' : 93,
  3724. 'DOT' : 190
  3725. });
  3726. this.dragUpload = false;
  3727. this.xhrUpload = (typeof XMLHttpRequestUpload != 'undefined' || typeof XMLHttpRequestEventTarget != 'undefined') && typeof File != 'undefined' && typeof FormData != 'undefined';
  3728. // configure transport object
  3729. this.transport = {};
  3730. if (typeof(this.options.transport) == 'object') {
  3731. this.transport = this.options.transport;
  3732. if (typeof(this.transport.init) == 'function') {
  3733. this.transport.init(this);
  3734. }
  3735. }
  3736. if (typeof(this.transport.send) != 'function') {
  3737. this.transport.send = function(opts) {
  3738. if (!self.UA.IE) {
  3739. // keep native xhr object for handling property responseURL
  3740. opts._xhr = new XMLHttpRequest();
  3741. opts.xhr = function() {
  3742. if (opts.progress) {
  3743. opts._xhr.addEventListener('progress', opts.progress);
  3744. }
  3745. return opts._xhr;
  3746. };
  3747. }
  3748. return $.ajax(opts);
  3749. };
  3750. }
  3751. if (this.transport.upload == 'iframe') {
  3752. this.transport.upload = $.proxy(this.uploads.iframe, this);
  3753. } else if (typeof(this.transport.upload) == 'function') {
  3754. this.dragUpload = !!this.options.dragUploadAllow;
  3755. } else if (this.xhrUpload && !!this.options.dragUploadAllow) {
  3756. this.transport.upload = $.proxy(this.uploads.xhr, this);
  3757. this.dragUpload = true;
  3758. } else {
  3759. this.transport.upload = $.proxy(this.uploads.iframe, this);
  3760. }
  3761. /**
  3762. * Decoding 'raw' string converted to unicode
  3763. *
  3764. * @param String str
  3765. * @return String
  3766. */
  3767. this.decodeRawString = function(str) {
  3768. var charCodes = function(str) {
  3769. var i, len, arr;
  3770. for (i=0,len=str.length,arr=[]; i<len; i++) {
  3771. arr.push(str.charCodeAt(i));
  3772. }
  3773. return arr;
  3774. },
  3775. scalarValues = function(arr) {
  3776. var scalars = [], i, len, c;
  3777. if (typeof arr === 'string') {arr = charCodes(arr);}
  3778. for (i=0,len=arr.length; c=arr[i],i<len; i++) {
  3779. if (c >= 0xd800 && c <= 0xdbff) {
  3780. scalars.push((c & 1023) + 64 << 10 | arr[++i] & 1023);
  3781. } else {
  3782. scalars.push(c);
  3783. }
  3784. }
  3785. return scalars;
  3786. },
  3787. decodeUTF8 = function(arr) {
  3788. var i, len, c, str, char = String.fromCharCode;
  3789. for (i=0,len=arr.length,str=""; c=arr[i],i<len; i++) {
  3790. if (c <= 0x7f) {
  3791. str += char(c);
  3792. } else if (c <= 0xdf && c >= 0xc2) {
  3793. str += char((c&31)<<6 | arr[++i]&63);
  3794. } else if (c <= 0xef && c >= 0xe0) {
  3795. str += char((c&15)<<12 | (arr[++i]&63)<<6 | arr[++i]&63);
  3796. } else if (c <= 0xf7 && c >= 0xf0) {
  3797. str += char(
  3798. 0xd800 | ((c&7)<<8 | (arr[++i]&63)<<2 | arr[++i]>>>4&3) - 64,
  3799. 0xdc00 | (arr[i++]&15)<<6 | arr[i]&63
  3800. );
  3801. } else {
  3802. str += char(0xfffd);
  3803. }
  3804. }
  3805. return str;
  3806. };
  3807. return decodeUTF8(scalarValues(str));
  3808. };
  3809. /**
  3810. * Gets target file contents by file.hash
  3811. *
  3812. * @param String hash The hash
  3813. * @param String responseType 'blob' or 'arraybuffer' (default)
  3814. * @param Object requestOpts The request options
  3815. * @return arraybuffer|blob The contents.
  3816. */
  3817. this.getContents = function(hash, responseType, requestOpts) {
  3818. var self = this,
  3819. dfd = $.Deferred(),
  3820. type = responseType || 'arraybuffer',
  3821. url, req;
  3822. dfd.fail(function() {
  3823. req && req.state() === 'pending' && req.reject();
  3824. });
  3825. url = self.openUrl(hash);
  3826. if (!self.isSameOrigin(url)) {
  3827. url = self.openUrl(hash, true);
  3828. }
  3829. req = self.request(Object.assign({
  3830. data : {cmd : 'get'},
  3831. options : {
  3832. url: url,
  3833. type: 'get',
  3834. cache : true,
  3835. dataType : 'binary',
  3836. responseType : type,
  3837. processData: false
  3838. },
  3839. notify : {
  3840. type: 'file',
  3841. cnt: 1,
  3842. hideCnt: true
  3843. },
  3844. cancel : true
  3845. }, requestOpts || {}))
  3846. .fail(function() {
  3847. dfd.reject();
  3848. })
  3849. .done(function(data) {
  3850. dfd.resolve(data);
  3851. });
  3852. return dfd;
  3853. };
  3854. /**
  3855. * Gets the binary by url.
  3856. *
  3857. * @param {Object} opts The options
  3858. * @param {Function} callback The callback
  3859. * @param {Object} requestOpts The request options
  3860. * @return arraybuffer|blob The contents.
  3861. */
  3862. this.getBinaryByUrl = function(opts, callback, requestOpts) {
  3863. var self = this,
  3864. dfd = $.Deferred(),
  3865. url, req;
  3866. dfd.fail(function() {
  3867. req && req.state() === 'pending' && req.reject();
  3868. });
  3869. req = self.request(Object.assign({
  3870. data : {cmd : 'get'},
  3871. options : Object.assign({
  3872. type: 'get',
  3873. cache : true,
  3874. dataType : 'binary',
  3875. responseType : 'blob',
  3876. processData: false
  3877. }, opts)
  3878. }, requestOpts || {}))
  3879. .fail(function() {
  3880. dfd.reject();
  3881. })
  3882. .done(function(data) {
  3883. callback && callback(data);
  3884. dfd.resolve(data);
  3885. });
  3886. return dfd;
  3887. };
  3888. /**
  3889. * Gets the mimetype.
  3890. *
  3891. * @param {string} name The name
  3892. * @param {string} orgMime The organization mime
  3893. * @return {string} The mimetype.
  3894. */
  3895. this.getMimetype = function(name, orgMime) {
  3896. var mime = orgMime,
  3897. ext, m;
  3898. m = (name + '').match(/\.([^.]+)$/);
  3899. if (m && (ext = m[1])) {
  3900. if (!extToMimeTable) {
  3901. extToMimeTable = self.arrayFlip(self.mimeTypes);
  3902. }
  3903. if (!(mime = extToMimeTable[ext.toLowerCase()])) {
  3904. mime = orgMime;
  3905. }
  3906. }
  3907. return mime;
  3908. };
  3909. /**
  3910. * Supported check hash algorisms
  3911. *
  3912. * @type Array
  3913. */
  3914. self.hashCheckers = [];
  3915. /**
  3916. * Closure of getContentsHashes()
  3917. */
  3918. (function(self) {
  3919. var hashLibs = {};
  3920. if (window.Worker && window.ArrayBuffer) {
  3921. // make fm.hashCheckers
  3922. if (self.options.cdns.sparkmd5) {
  3923. hashLibs.SparkMD5 = true;
  3924. self.hashCheckers.push('md5');
  3925. }
  3926. if (self.options.cdns.jssha) {
  3927. hashLibs.jsSHA = true;
  3928. self.hashCheckers = self.hashCheckers.concat(['sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'shake128', 'shake256']);
  3929. }
  3930. }
  3931. /**
  3932. * Gets the contents hashes.
  3933. *
  3934. * @param String target target file.hash
  3935. * @param Object needHashes need hash lib names
  3936. * @param Object requestOpts The request options
  3937. * @return Object hashes with lib name as key
  3938. */
  3939. self.getContentsHashes = function(target, needHashes, hashOpts, requestOpts) {
  3940. var dfd = $.Deferred(),
  3941. needs = self.arrayFlip(needHashes || ['md5'], true),
  3942. libs = [],
  3943. jobs = [],
  3944. res = {},
  3945. opts = hashOpts? hashOpts : {
  3946. shake128len : 256,
  3947. shake256len : 512
  3948. },
  3949. req;
  3950. dfd.fail(function() {
  3951. req && req.reject();
  3952. });
  3953. if (Object.keys(hashLibs).length) {
  3954. req = self.getContents(target, 'arraybuffer', requestOpts).done(function(arrayBuffer) {
  3955. if (needs.md5 && hashLibs.SparkMD5) {
  3956. jobs.push((function() {
  3957. var job = $.Deferred();
  3958. try {
  3959. var wk = self.getWorker();
  3960. job.fail(function() {
  3961. wk && wk.terminate();
  3962. });
  3963. wk.onmessage = function(ans) {
  3964. wk && wk.terminate();
  3965. if (ans.data.hash) {
  3966. var f;
  3967. res.md5 = ans.data.hash;
  3968. if (f = self.file(target)) {
  3969. f.md5 = res.md5;
  3970. }
  3971. } else if (ans.data.error) {
  3972. res.md5 = ans.data.error;
  3973. }
  3974. dfd.notify(res);
  3975. job.resolve();
  3976. };
  3977. wk.onerror = function(e) {
  3978. job.reject();
  3979. };
  3980. wk.postMessage({
  3981. scripts: [self.options.cdns.sparkmd5, self.getWorkerUrl('calcfilehash.js')],
  3982. data: { type: 'md5', bin: arrayBuffer }
  3983. });
  3984. dfd.fail(function() {
  3985. job.reject();
  3986. });
  3987. } catch(e) {
  3988. job.reject();
  3989. delete hashLibs.SparkMD5;
  3990. }
  3991. return job;
  3992. })());
  3993. }
  3994. if (hashLibs.jsSHA) {
  3995. $.each(['1', '224', '256', '384', '512', '3-224', '3-256', '3-384', '3-512', 'ke128', 'ke256'], function(i, v) {
  3996. if (needs['sha' + v]) {
  3997. jobs.push((function() {
  3998. var job = $.Deferred();
  3999. try {
  4000. var wk = self.getWorker();
  4001. job.fail(function() {
  4002. wk && wk.terminate();
  4003. });
  4004. wk.onmessage = function(ans) {
  4005. wk && wk.terminate();
  4006. if (ans.data.hash) {
  4007. var f;
  4008. res['sha' + v] = ans.data.hash;
  4009. if (f = self.file(target)) {
  4010. f['sha' + v] = res['sha' + v];
  4011. }
  4012. } else if (ans.data.error) {
  4013. res['sha' + v] = ans.data.error;
  4014. }
  4015. dfd.notify(res);
  4016. job.resolve();
  4017. };
  4018. wk.onerror = function(e) {
  4019. job.reject();
  4020. };
  4021. wk.postMessage({
  4022. scripts: [self.options.cdns.jssha, self.getWorkerUrl('calcfilehash.js')],
  4023. data: { type: v, bin: arrayBuffer, hashOpts: opts }
  4024. });
  4025. dfd.fail(function() {
  4026. job.reject();
  4027. });
  4028. } catch(e) {
  4029. job.reject();
  4030. delete hashLibs.jsSHA;
  4031. }
  4032. return job;
  4033. })());
  4034. }
  4035. });
  4036. }
  4037. if (jobs.length) {
  4038. $.when.apply(null, jobs).always(function() {
  4039. dfd.resolve(res);
  4040. });
  4041. } else {
  4042. dfd.reject();
  4043. }
  4044. }).fail(function() {
  4045. dfd.reject();
  4046. });
  4047. } else {
  4048. dfd.reject();
  4049. }
  4050. return dfd;
  4051. };
  4052. })(this);
  4053. /**
  4054. * Parse error value to display
  4055. *
  4056. * @param Mixed error
  4057. * @return Mixed parsed error
  4058. */
  4059. this.parseError = function(error) {
  4060. var arg = error;
  4061. if ($.isPlainObject(arg)) {
  4062. arg = arg.error;
  4063. }
  4064. return arg;
  4065. };
  4066. /**
  4067. * Alias for this.trigger('error', {error : 'message'})
  4068. *
  4069. * @param String error message
  4070. * @return elFinder
  4071. **/
  4072. this.error = function() {
  4073. var arg = arguments[0],
  4074. opts = arguments[1] || null,
  4075. err;
  4076. if (arguments.length == 1 && typeof(arg) === 'function') {
  4077. return self.bind('error', arg);
  4078. } else {
  4079. err = this.parseError(arg);
  4080. return (err === true || !err)? this : self.trigger('error', {error: err, opts : opts});
  4081. }
  4082. };
  4083. // create bind/trigger aliases for build-in events
  4084. $.each(events, function(i, name) {
  4085. self[name] = function() {
  4086. var arg = arguments[0];
  4087. return arguments.length == 1 && typeof(arg) == 'function'
  4088. ? self.bind(name, arg)
  4089. : self.trigger(name, $.isPlainObject(arg) ? arg : {});
  4090. };
  4091. });
  4092. // bind core event handlers
  4093. this
  4094. .enable(function() {
  4095. if (!enabled && self.api && self.visible() && self.ui.overlay.is(':hidden') && ! node.children('.elfinder-dialog.' + self.res('class', 'editing') + ':visible').length) {
  4096. enabled = true;
  4097. document.activeElement && document.activeElement.blur();
  4098. node.removeClass('elfinder-disabled');
  4099. }
  4100. })
  4101. .disable(function() {
  4102. prevEnabled = enabled;
  4103. enabled = false;
  4104. node.addClass('elfinder-disabled');
  4105. })
  4106. .open(function() {
  4107. selected = [];
  4108. })
  4109. .select(function(e) {
  4110. var cnt = 0,
  4111. unselects = [];
  4112. selected = $.grep(e.data.selected || e.data.value|| [], function(hash) {
  4113. if (unselects.length || (self.maxTargets && ++cnt > self.maxTargets)) {
  4114. unselects.push(hash);
  4115. return false;
  4116. } else {
  4117. return files[hash] ? true : false;
  4118. }
  4119. });
  4120. if (unselects.length) {
  4121. self.trigger('unselectfiles', {files: unselects, inselect: true});
  4122. self.toast({mode: 'warning', msg: self.i18n(['errMaxTargets', self.maxTargets])});
  4123. }
  4124. })
  4125. .error(function(e) {
  4126. var opts = {
  4127. cssClass : 'elfinder-dialog-error',
  4128. title : self.i18n('error'),
  4129. resizable : false,
  4130. destroyOnClose : true,
  4131. buttons : {}
  4132. },
  4133. node = self.getUI(),
  4134. cnt = node.children('.elfinder-dialog-error').length,
  4135. last, counter;
  4136. if (cnt < self.options.maxErrorDialogs) {
  4137. opts.buttons[self.i18n(self.i18n('btnClose'))] = function() { $(this).elfinderdialog('close'); };
  4138. if (e.data.opts && $.isPlainObject(e.data.opts)) {
  4139. Object.assign(opts, e.data.opts);
  4140. }
  4141. self.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-error"></span>'+self.i18n(e.data.error), opts);
  4142. } else {
  4143. last = node.children('.elfinder-dialog-error:last').children('.ui-dialog-content:first');
  4144. counter = last.children('.elfinder-error-counter');
  4145. if (counter.length) {
  4146. counter.data('cnt', parseInt(counter.data('cnt')) + 1).html(self.i18n(['moreErrors', counter.data('cnt')]));
  4147. } else {
  4148. counter = $('<span class="elfinder-error-counter">'+ self.i18n(['moreErrors', 1]) +'</span>').data('cnt', 1);
  4149. last.append('<br/>', counter);
  4150. }
  4151. }
  4152. })
  4153. .bind('tmb', function(e) {
  4154. $.each(e.data.images||[], function(hash, tmb) {
  4155. if (files[hash]) {
  4156. files[hash].tmb = tmb;
  4157. }
  4158. });
  4159. })
  4160. .bind('searchstart', function(e) {
  4161. Object.assign(self.searchStatus, e.data);
  4162. self.searchStatus.state = 1;
  4163. })
  4164. .bind('search', function(e) {
  4165. self.searchStatus.state = 2;
  4166. })
  4167. .bind('searchend', function() {
  4168. self.searchStatus.state = 0;
  4169. self.searchStatus.ininc = false;
  4170. self.searchStatus.mixed = false;
  4171. })
  4172. .bind('canMakeEmptyFile', function(e) {
  4173. var data = e.data,
  4174. obj = {};
  4175. if (data && Array.isArray(data.mimes)) {
  4176. if (!data.unshift) {
  4177. obj = self.mimesCanMakeEmpty;
  4178. }
  4179. $.each(data.mimes, function() {
  4180. if (!obj[this]) {
  4181. obj[this] = self.mimeTypes[this];
  4182. }
  4183. });
  4184. if (data.unshift) {
  4185. self.mimesCanMakeEmpty = Object.assign(obj, self.mimesCanMakeEmpty);
  4186. }
  4187. }
  4188. })
  4189. .bind('themechange', function() {
  4190. requestAnimationFrame(function() {
  4191. self.trigger('uiresize');
  4192. });
  4193. })
  4194. ;
  4195. // We listen and emit a sound on delete according to option
  4196. if (true === this.options.sound) {
  4197. this.bind('playsound', function(e) {
  4198. var play = beeper.canPlayType && beeper.canPlayType('audio/wav; codecs="1"'),
  4199. file = e.data && e.data.soundFile;
  4200. play && file && play != '' && play != 'no' && $(beeper).html('<source src="' + soundPath + file + '" type="audio/wav">')[0].play();
  4201. });
  4202. }
  4203. // bind external event handlers
  4204. $.each(this.options.handlers, function(event, callback) {
  4205. self.bind(event, callback);
  4206. });
  4207. /**
  4208. * History object. Store visited folders
  4209. *
  4210. * @type Object
  4211. **/
  4212. this.history = new this.history(this);
  4213. /**
  4214. * Root hashed
  4215. *
  4216. * @type Object
  4217. */
  4218. this.roots = {};
  4219. /**
  4220. * leaf roots
  4221. *
  4222. * @type Object
  4223. */
  4224. this.leafRoots = {};
  4225. this.volumeExpires = {};
  4226. /**
  4227. * Loaded commands
  4228. *
  4229. * @type Object
  4230. **/
  4231. this._commands = {};
  4232. if (!Array.isArray(this.options.commands)) {
  4233. this.options.commands = [];
  4234. }
  4235. if ($.inArray('*', this.options.commands) !== -1) {
  4236. this.options.commands = Object.keys(this.commands);
  4237. }
  4238. /**
  4239. * UI command map of cwd volume ( That volume driver option `uiCmdMap` )
  4240. *
  4241. * @type Object
  4242. **/
  4243. this.commandMap = {};
  4244. /**
  4245. * cwd options of each volume
  4246. * key: volumeid
  4247. * val: options object
  4248. *
  4249. * @type Object
  4250. */
  4251. this.volOptions = {};
  4252. /**
  4253. * Has volOptions data
  4254. *
  4255. * @type Boolean
  4256. */
  4257. this.hasVolOptions = false;
  4258. /**
  4259. * Hash of trash holders
  4260. * key: trash folder hash
  4261. * val: source volume hash
  4262. *
  4263. * @type Object
  4264. */
  4265. this.trashes = {};
  4266. /**
  4267. * cwd options of each folder/file
  4268. * key: hash
  4269. * val: options object
  4270. *
  4271. * @type Object
  4272. */
  4273. this.optionsByHashes = {};
  4274. /**
  4275. * UI Auto Hide Functions
  4276. * Each auto hide function mast be call to `fm.trigger('uiautohide')` at end of process
  4277. *
  4278. * @type Array
  4279. **/
  4280. this.uiAutoHide = [];
  4281. // trigger `uiautohide`
  4282. this.one('open', function() {
  4283. if (self.uiAutoHide.length) {
  4284. setTimeout(function() {
  4285. self.trigger('uiautohide');
  4286. }, 500);
  4287. }
  4288. });
  4289. // Auto Hide Functions sequential processing start
  4290. this.bind('uiautohide', function() {
  4291. if (self.uiAutoHide.length) {
  4292. self.uiAutoHide.shift()();
  4293. }
  4294. });
  4295. if (this.options.width) {
  4296. width = this.options.width;
  4297. }
  4298. if (this.options.height) {
  4299. height = this.options.height;
  4300. }
  4301. if (this.options.heightBase) {
  4302. heightBase = $(this.options.heightBase);
  4303. }
  4304. if (this.options.soundPath) {
  4305. soundPath = this.options.soundPath.replace(/\/+$/, '') + '/';
  4306. } else {
  4307. soundPath = this.baseUrl + soundPath;
  4308. }
  4309. if (this.options.parrotHeaders && Array.isArray(this.options.parrotHeaders) && this.options.parrotHeaders.length) {
  4310. this.parrotHeaders = this.options.parrotHeaders;
  4311. // check sessionStorage
  4312. $.each(this.parrotHeaders, function(i, h) {
  4313. var v = self.sessionStorage('core-ph:' + h);
  4314. if (v) {
  4315. self.customHeaders[h] = v;
  4316. }
  4317. });
  4318. } else {
  4319. this.parrotHeaders = [];
  4320. }
  4321. self.one('opendone', function() {
  4322. var tm;
  4323. // attach events to document
  4324. $(document)
  4325. // disable elfinder on click outside elfinder
  4326. .on('click.'+namespace, function(e) { enabled && ! self.options.enableAlways && !$(e.target).closest(node).length && self.disable(); })
  4327. // exec shortcuts
  4328. .on(keydown+' '+keypress+' '+keyup+' '+mousedown, execShortcut);
  4329. // attach events to window
  4330. self.options.useBrowserHistory && $(window)
  4331. .on('popstate.' + namespace, function(ev) {
  4332. var state = ev.originalEvent.state || {},
  4333. hasThash = state.thash? true : false,
  4334. dialog = node.find('.elfinder-frontmost:visible'),
  4335. input = node.find('.elfinder-navbar-dir,.elfinder-cwd-filename').find('input,textarea'),
  4336. onOpen, toast;
  4337. if (!hasThash) {
  4338. state = { thash: self.cwd().hash };
  4339. // scroll to elFinder node
  4340. $('html,body').animate({ scrollTop: node.offset().top });
  4341. }
  4342. if (dialog.length || input.length) {
  4343. history.pushState(state, null, location.pathname + location.search + '#elf_' + state.thash);
  4344. if (dialog.length) {
  4345. if (!dialog.hasClass(self.res('class', 'preventback'))) {
  4346. if (dialog.hasClass('elfinder-contextmenu')) {
  4347. $(document).trigger($.Event('keydown', { keyCode: $.ui.keyCode.ESCAPE, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false }));
  4348. } else if (dialog.hasClass('elfinder-dialog')) {
  4349. dialog.elfinderdialog('close');
  4350. } else {
  4351. dialog.trigger('close');
  4352. }
  4353. }
  4354. } else {
  4355. input.trigger($.Event('keydown', { keyCode: $.ui.keyCode.ESCAPE, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false }));
  4356. }
  4357. } else {
  4358. if (hasThash) {
  4359. !$.isEmptyObject(self.files()) && self.request({
  4360. data : {cmd : 'open', target : state.thash, onhistory : 1},
  4361. notify : {type : 'open', cnt : 1, hideCnt : true},
  4362. syncOnFail : true
  4363. });
  4364. } else {
  4365. onOpen = function() {
  4366. toast.trigger('click');
  4367. };
  4368. self.one('open', onOpen, true);
  4369. toast = self.toast({
  4370. msg: self.i18n('pressAgainToExit'),
  4371. onHidden: function() {
  4372. self.unbind('open', onOpen);
  4373. history.pushState(state, null, location.pathname + location.search + '#elf_' + state.thash);
  4374. }
  4375. });
  4376. }
  4377. }
  4378. });
  4379. $(window).on('resize.' + namespace, function(e){
  4380. if (e.target === this) {
  4381. tm && cancelAnimationFrame(tm);
  4382. tm = requestAnimationFrame(function() {
  4383. var prv = node.data('resizeSize') || {w: 0, h: 0},
  4384. size = {w: Math.round(node.width()), h: Math.round(node.height())};
  4385. node.data('resizeSize', size);
  4386. if (size.w !== prv.w || size.h !== prv.h) {
  4387. node.trigger('resize');
  4388. self.trigger('resize', {width : size.w, height : size.h});
  4389. }
  4390. });
  4391. }
  4392. })
  4393. .on('beforeunload.' + namespace,function(e){
  4394. var msg, cnt;
  4395. if (!self.pauseUnloadCheck()) {
  4396. if (node.is(':visible')) {
  4397. if (self.ui.notify.children().length && $.inArray('hasNotifyDialog', self.options.windowCloseConfirm) !== -1) {
  4398. msg = self.i18n('ntfsmth');
  4399. } else if (node.find('.'+self.res('class', 'editing')).length && $.inArray('editingFile', self.options.windowCloseConfirm) !== -1) {
  4400. msg = self.i18n('editingFile');
  4401. } else if ((cnt = Object.keys(self.selected()).length) && $.inArray('hasSelectedItem', self.options.windowCloseConfirm) !== -1) {
  4402. msg = self.i18n('hasSelected', ''+cnt);
  4403. } else if ((cnt = Object.keys(self.clipboard()).length) && $.inArray('hasClipboardData', self.options.windowCloseConfirm) !== -1) {
  4404. msg = self.i18n('hasClipboard', ''+cnt);
  4405. }
  4406. if (msg) {
  4407. e.returnValue = msg;
  4408. return msg;
  4409. }
  4410. }
  4411. self.trigger('unload');
  4412. }
  4413. });
  4414. // bind window onmessage for CORS
  4415. $(window).on('message.' + namespace, function(e){
  4416. var res = e.originalEvent || null,
  4417. obj, data;
  4418. if (res && (self.convAbsUrl(self.options.url).indexOf(res.origin) === 0 || self.convAbsUrl(self.uploadURL).indexOf(res.origin) === 0)) {
  4419. try {
  4420. obj = JSON.parse(res.data);
  4421. data = obj.data || null;
  4422. if (data) {
  4423. if (data.error) {
  4424. if (obj.bind) {
  4425. self.trigger(obj.bind+'fail', data);
  4426. }
  4427. self.error(data.error);
  4428. } else {
  4429. data.warning && self.error(data.warning);
  4430. self.updateCache(data);
  4431. data.removed && data.removed.length && self.remove(data);
  4432. data.added && data.added.length && self.add(data);
  4433. data.changed && data.changed.length && self.change(data);
  4434. if (obj.bind) {
  4435. self.trigger(obj.bind, data);
  4436. self.trigger(obj.bind+'done');
  4437. }
  4438. data.sync && self.sync();
  4439. }
  4440. }
  4441. } catch (e) {
  4442. self.sync();
  4443. }
  4444. }
  4445. });
  4446. // elFinder enable always
  4447. if (self.options.enableAlways) {
  4448. $(window).on('focus.' + namespace, function(e){
  4449. (e.target === this) && self.enable();
  4450. });
  4451. if (inFrame) {
  4452. $(window.top).on('focus.' + namespace, function() {
  4453. if (self.enable() && (! parentIframe || parentIframe.is(':visible'))) {
  4454. requestAnimationFrame(function() {
  4455. $(window).trigger('focus');
  4456. });
  4457. }
  4458. });
  4459. }
  4460. } else if (inFrame) {
  4461. $(window).on('blur.' + namespace, function(e){
  4462. enabled && e.target === this && self.disable();
  4463. });
  4464. }
  4465. // return focus to the window on click (elFInder in the frame)
  4466. if (inFrame) {
  4467. node.on('click', function(e) {
  4468. $(window).trigger('focus');
  4469. });
  4470. }
  4471. // elFinder to enable by mouse over
  4472. if (self.options.enableByMouseOver) {
  4473. node.on('mouseenter touchstart', function(e) {
  4474. (inFrame) && $(window).trigger('focus');
  4475. ! self.enabled() && self.enable();
  4476. });
  4477. }
  4478. // When the browser tab turn to foreground/background
  4479. $(window).on('visibilitychange.' + namespace, function(e) {
  4480. var background = document.hidden || document.webkitHidden || document.msHidden;
  4481. // AutoSync turn On/Off
  4482. if (self.options.syncStart) {
  4483. self.autoSync(background? 'stop' : void(0));
  4484. }
  4485. });
  4486. });
  4487. // store instance in node
  4488. node[0].elfinder = this;
  4489. // auto load language file
  4490. dfrdsBeforeBootup.push((function() {
  4491. var lang = self.lang,
  4492. langJs = self.i18nBaseUrl + 'elfinder.' + lang + '.js',
  4493. dfd = $.Deferred().done(function() {
  4494. if (self.i18[lang]) {
  4495. self.lang = lang;
  4496. }
  4497. self.trigger('i18load');
  4498. i18n = self.lang === 'en'
  4499. ? self.i18['en']
  4500. : $.extend(true, {}, self.i18['en'], self.i18[self.lang]);
  4501. });
  4502. if (!self.i18[lang]) {
  4503. self.lang = 'en';
  4504. if (self.hasRequire) {
  4505. require([langJs], function() {
  4506. dfd.resolve();
  4507. }, function() {
  4508. dfd.resolve();
  4509. });
  4510. } else {
  4511. self.loadScript([langJs], function() {
  4512. dfd.resolve();
  4513. }, {
  4514. loadType: 'tag',
  4515. error : function() {
  4516. dfd.resolve();
  4517. }
  4518. });
  4519. }
  4520. } else {
  4521. dfd.resolve();
  4522. }
  4523. return dfd;
  4524. })());
  4525. // elFinder boot up function
  4526. bootUp = function() {
  4527. var columnNames;
  4528. /**
  4529. * i18 messages
  4530. *
  4531. * @type Object
  4532. **/
  4533. self.messages = i18n.messages;
  4534. // check jquery ui
  4535. if (!($.fn.selectable && $.fn.draggable && $.fn.droppable && $.fn.resizable && $.fn.button && $.fn.slider)) {
  4536. return alert(self.i18n('errJqui'));
  4537. }
  4538. // check node
  4539. if (!node.length) {
  4540. return alert(self.i18n('errNode'));
  4541. }
  4542. // check connector url
  4543. if (!self.options.url) {
  4544. return alert(self.i18n('errURL'));
  4545. }
  4546. // column key/name map for fm.getColumnName()
  4547. columnNames = Object.assign({
  4548. name : self.i18n('name'),
  4549. perm : self.i18n('perms'),
  4550. date : self.i18n('modify'),
  4551. size : self.i18n('size'),
  4552. kind : self.i18n('kind'),
  4553. modestr : self.i18n('mode'),
  4554. modeoct : self.i18n('mode'),
  4555. modeboth : self.i18n('mode')
  4556. }, self.options.uiOptions.cwd.listView.columnsCustomName);
  4557. /**
  4558. * Gets the column name of cwd list view
  4559. *
  4560. * @param String key The key
  4561. * @return String The column name.
  4562. */
  4563. self.getColumnName = function(key) {
  4564. return columnNames[key] || self.i18n(key);
  4565. };
  4566. /**
  4567. * Interface direction
  4568. *
  4569. * @type String
  4570. * @default "ltr"
  4571. **/
  4572. self.direction = i18n.direction;
  4573. /**
  4574. * Date/time format
  4575. *
  4576. * @type String
  4577. * @default "m.d.Y"
  4578. **/
  4579. self.dateFormat = self.options.dateFormat || i18n.dateFormat;
  4580. /**
  4581. * Date format like "Yesterday 10:20:12"
  4582. *
  4583. * @type String
  4584. * @default "{day} {time}"
  4585. **/
  4586. self.fancyFormat = self.options.fancyDateFormat || i18n.fancyDateFormat;
  4587. /**
  4588. * Date format for if upload file has not original unique name
  4589. * e.g. Clipboard image data, Image data taken with iOS
  4590. *
  4591. * @type String
  4592. * @default "ymd-His"
  4593. **/
  4594. self.nonameDateFormat = (self.options.nonameDateFormat || i18n.nonameDateFormat).replace(/[\/\\]/g, '_');
  4595. /**
  4596. * Css classes
  4597. *
  4598. * @type String
  4599. **/
  4600. self.cssClass = 'ui-helper-reset ui-helper-clearfix ui-widget ui-widget-content ui-corner-all elfinder elfinder-'
  4601. +(self.direction == 'rtl' ? 'rtl' : 'ltr')
  4602. +(self.UA.Touch? (' elfinder-touch' + (self.options.resizable ? ' touch-punch' : '')) : '')
  4603. +(self.UA.Mobile? ' elfinder-mobile' : '')
  4604. +(self.UA.iOS? ' elfinder-ios' : '')
  4605. +' '+self.options.cssClass;
  4606. // prepare node
  4607. node.addClass(self.cssClass)
  4608. .on(mousedown, function() {
  4609. !enabled && self.enable();
  4610. });
  4611. // draggable closure
  4612. (function() {
  4613. var ltr, wzRect, wzBottom, wzBottom2, nodeStyle,
  4614. keyEvt = keydown + 'draggable' + ' keyup.' + namespace + 'draggable';
  4615. /**
  4616. * Base draggable options
  4617. *
  4618. * @type Object
  4619. **/
  4620. self.draggable = {
  4621. appendTo : node,
  4622. addClasses : false,
  4623. distance : 4,
  4624. revert : true,
  4625. refreshPositions : false,
  4626. cursor : 'crosshair',
  4627. cursorAt : {left : 50, top : 47},
  4628. scroll : false,
  4629. start : function(e, ui) {
  4630. var helper = ui.helper,
  4631. targets = $.grep(helper.data('files')||[], function(h) {
  4632. if (h) {
  4633. remember[h] = true;
  4634. return true;
  4635. }
  4636. return false;
  4637. }),
  4638. locked = false,
  4639. cnt, h;
  4640. // fix node size
  4641. nodeStyle = node.attr('style');
  4642. node.width(node.width()).height(node.height());
  4643. // set var for drag()
  4644. ltr = (self.direction === 'ltr');
  4645. wzRect = self.getUI('workzone').data('rectangle');
  4646. wzBottom = wzRect.top + wzRect.height;
  4647. wzBottom2 = wzBottom - self.getUI('navdock').outerHeight(true);
  4648. self.draggingUiHelper = helper;
  4649. cnt = targets.length;
  4650. while (cnt--) {
  4651. h = targets[cnt];
  4652. if (files[h].locked) {
  4653. locked = true;
  4654. helper.data('locked', true);
  4655. break;
  4656. }
  4657. }
  4658. !locked && self.trigger('lockfiles', {files : targets});
  4659. helper.data('autoScrTm', setInterval(function() {
  4660. if (helper.data('autoScr')) {
  4661. self.autoScroll[helper.data('autoScr')](helper.data('autoScrVal'));
  4662. }
  4663. }, 50));
  4664. },
  4665. drag : function(e, ui) {
  4666. var helper = ui.helper,
  4667. autoScr, autoUp, bottom;
  4668. if ((autoUp = wzRect.top > e.pageY) || wzBottom2 < e.pageY) {
  4669. if (wzRect.cwdEdge > e.pageX) {
  4670. autoScr = (ltr? 'navbar' : 'cwd') + (autoUp? 'Up' : 'Down');
  4671. } else {
  4672. autoScr = (ltr? 'cwd' : 'navbar') + (autoUp? 'Up' : 'Down');
  4673. }
  4674. if (!autoUp) {
  4675. if (autoScr.substr(0, 3) === 'cwd') {
  4676. if (wzBottom < e.pageY) {
  4677. bottom = wzBottom;
  4678. } else {
  4679. autoScr = null;
  4680. }
  4681. } else {
  4682. bottom = wzBottom2;
  4683. }
  4684. }
  4685. if (autoScr) {
  4686. helper.data('autoScr', autoScr);
  4687. helper.data('autoScrVal', Math.pow((autoUp? wzRect.top - e.pageY : e.pageY - bottom), 1.3));
  4688. }
  4689. }
  4690. if (! autoScr) {
  4691. if (helper.data('autoScr')) {
  4692. helper.data('refreshPositions', 1).data('autoScr', null);
  4693. }
  4694. }
  4695. if (helper.data('refreshPositions') && $(this).elfUiWidgetInstance('draggable')) {
  4696. if (helper.data('refreshPositions') > 0) {
  4697. $(this).draggable('option', { refreshPositions : true, elfRefresh : true });
  4698. helper.data('refreshPositions', -1);
  4699. } else {
  4700. $(this).draggable('option', { refreshPositions : false, elfRefresh : false });
  4701. helper.data('refreshPositions', null);
  4702. }
  4703. }
  4704. },
  4705. stop : function(e, ui) {
  4706. var helper = ui.helper,
  4707. files;
  4708. $(document).off(keyEvt);
  4709. $(this).elfUiWidgetInstance('draggable') && $(this).draggable('option', { refreshPositions : false });
  4710. self.draggingUiHelper = null;
  4711. self.trigger('focus').trigger('dragstop');
  4712. if (! helper.data('droped')) {
  4713. files = $.grep(helper.data('files')||[], function(h) { return h? true : false ;});
  4714. self.trigger('unlockfiles', {files : files});
  4715. self.trigger('selectfiles', {files : self.selected()});
  4716. }
  4717. self.enable();
  4718. // restore node style
  4719. node.attr('style', nodeStyle);
  4720. helper.data('autoScrTm') && clearInterval(helper.data('autoScrTm'));
  4721. },
  4722. helper : function(e, ui) {
  4723. var element = this.id ? $(this) : $(this).parents('[id]:first'),
  4724. helper = $('<div class="elfinder-drag-helper"><span class="elfinder-drag-helper-icon-status"></span></div>'),
  4725. icon = function(f) {
  4726. var mime = f.mime, i, tmb = self.tmb(f);
  4727. i = '<div class="elfinder-cwd-icon elfinder-cwd-icon-drag '+self.mime2class(mime)+' ui-corner-all"></div>';
  4728. if (tmb) {
  4729. i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML;
  4730. } else if (f.icon) {
  4731. i = $(i).css(self.getIconStyle(f, true)).get(0).outerHTML;
  4732. }
  4733. if (f.csscls) {
  4734. i = '<div class="'+f.csscls+'">' + i + '</div>';
  4735. }
  4736. return i;
  4737. },
  4738. hashes, l, ctr;
  4739. self.draggingUiHelper && self.draggingUiHelper.stop(true, true);
  4740. self.trigger('dragstart', {target : element[0], originalEvent : e}, true);
  4741. hashes = element.hasClass(self.res('class', 'cwdfile'))
  4742. ? self.selected()
  4743. : [self.navId2Hash(element.attr('id'))];
  4744. helper.append(icon(files[hashes[0]])).data('files', hashes).data('locked', false).data('droped', false).data('namespace', namespace).data('dropover', 0);
  4745. if ((l = hashes.length) > 1) {
  4746. helper.append(icon(files[hashes[l-1]]) + '<span class="elfinder-drag-num">'+l+'</span>');
  4747. }
  4748. $(document).on(keyEvt, function(e){
  4749. var chk = (e.shiftKey||e.ctrlKey||e.metaKey);
  4750. if (ctr !== chk) {
  4751. ctr = chk;
  4752. if (helper.is(':visible') && helper.data('dropover') && ! helper.data('droped')) {
  4753. helper.toggleClass('elfinder-drag-helper-plus', helper.data('locked')? true : ctr);
  4754. self.trigger(ctr? 'unlockfiles' : 'lockfiles', {files : hashes, helper: helper});
  4755. }
  4756. }
  4757. });
  4758. return helper;
  4759. }
  4760. };
  4761. })();
  4762. // in getFileCallback set - change default actions on double click/enter/ctrl+enter
  4763. if (self.commands.getfile) {
  4764. if (typeof(self.options.getFileCallback) == 'function') {
  4765. self.bind('dblclick', function(e) {
  4766. e.preventDefault();
  4767. self.exec('getfile').fail(function() {
  4768. self.exec('open', e.data && e.data.file? [ e.data.file ]: void(0));
  4769. });
  4770. });
  4771. self.shortcut({
  4772. pattern : 'enter',
  4773. description : self.i18n('cmdgetfile'),
  4774. callback : function() { self.exec('getfile').fail(function() { self.exec(self.OS == 'mac' ? 'rename' : 'open'); }); }
  4775. })
  4776. .shortcut({
  4777. pattern : 'ctrl+enter',
  4778. description : self.i18n(self.OS == 'mac' ? 'cmdrename' : 'cmdopen'),
  4779. callback : function() { self.exec(self.OS == 'mac' ? 'rename' : 'open'); }
  4780. });
  4781. } else {
  4782. self.options.getFileCallback = null;
  4783. }
  4784. }
  4785. // load commands
  4786. $.each(self.commands, function(name, cmd) {
  4787. var proto = Object.assign({}, cmd.prototype),
  4788. extendsCmd, opts;
  4789. if ($.isFunction(cmd) && !self._commands[name] && (cmd.prototype.forceLoad || $.inArray(name, self.options.commands) !== -1)) {
  4790. extendsCmd = cmd.prototype.extendsCmd || '';
  4791. if (extendsCmd) {
  4792. if ($.isFunction(self.commands[extendsCmd])) {
  4793. cmd.prototype = Object.assign({}, base, new self.commands[extendsCmd](), cmd.prototype);
  4794. } else {
  4795. return true;
  4796. }
  4797. } else {
  4798. cmd.prototype = Object.assign({}, base, cmd.prototype);
  4799. }
  4800. self._commands[name] = new cmd();
  4801. cmd.prototype = proto;
  4802. opts = self.options.commandsOptions[name] || {};
  4803. if (extendsCmd && self.options.commandsOptions[extendsCmd]) {
  4804. opts = $.extend(true, {}, self.options.commandsOptions[extendsCmd], opts);
  4805. }
  4806. self._commands[name].setup(name, opts);
  4807. // setup linked commands
  4808. if (self._commands[name].linkedCmds.length) {
  4809. $.each(self._commands[name].linkedCmds, function(i, n) {
  4810. var lcmd = self.commands[n];
  4811. if ($.isFunction(lcmd) && !self._commands[n]) {
  4812. lcmd.prototype = base;
  4813. self._commands[n] = new lcmd();
  4814. self._commands[n].setup(n, self.options.commandsOptions[n]||{});
  4815. }
  4816. });
  4817. }
  4818. }
  4819. });
  4820. /**
  4821. * UI nodes
  4822. *
  4823. * @type Object
  4824. **/
  4825. self.ui = {
  4826. // container for nav panel and current folder container
  4827. workzone : $('<div></div>').appendTo(node).elfinderworkzone(self),
  4828. // contaainer for folders tree / places
  4829. navbar : $('<div></div>').appendTo(node).elfindernavbar(self, self.options.uiOptions.navbar || {}),
  4830. // container for for preview etc at below the navbar
  4831. navdock : $('<div></div>').appendTo(node).elfindernavdock(self, self.options.uiOptions.navdock || {}),
  4832. // contextmenu
  4833. contextmenu : $('<div></div>').appendTo(node).elfindercontextmenu(self),
  4834. // overlay
  4835. overlay : $('<div></div>').appendTo(node).elfinderoverlay({
  4836. show : function() { self.disable(); },
  4837. hide : function() { prevEnabled && self.enable(); }
  4838. }),
  4839. // current folder container
  4840. cwd : $('<div></div>').appendTo(node).elfindercwd(self, self.options.uiOptions.cwd || {}),
  4841. // notification dialog window
  4842. notify : self.dialog('', {
  4843. cssClass : 'elfinder-dialog-notify' + (self.options.notifyDialog.canClose? '' : ' elfinder-titlebar-button-hide'),
  4844. position : self.options.notifyDialog.position,
  4845. absolute : true,
  4846. resizable : false,
  4847. autoOpen : false,
  4848. allowMinimize : true,
  4849. closeOnEscape : self.options.notifyDialog.canClose? true : false,
  4850. title : '&nbsp;',
  4851. width : self.options.notifyDialog.width? parseInt(self.options.notifyDialog.width) : null,
  4852. minHeight : null,
  4853. minimize : function() { self.ui.notify.trigger('minimize'); }
  4854. }),
  4855. statusbar : $('<div class="ui-widget-header ui-helper-clearfix ui-corner-bottom elfinder-statusbar"></div>').hide().appendTo(node),
  4856. toast : $('<div class="elfinder-toast"></div>').appendTo(node),
  4857. bottomtray : $('<div class="elfinder-bottomtray">').appendTo(node),
  4858. progressbar : $('<div class="elfinder-ui-progressbar">').appendTo(node)
  4859. };
  4860. self.trigger('uiready');
  4861. // load required ui
  4862. $.each(self.options.ui || [], function(i, ui) {
  4863. var name = 'elfinder'+ui,
  4864. opts = self.options.uiOptions[ui] || {};
  4865. if (!self.ui[ui] && $.fn[name]) {
  4866. // regist to self.ui before make instance
  4867. self.ui[ui] = $('<'+(opts.tag || 'div')+'/>').appendTo(node);
  4868. self.ui[ui][name](self, opts);
  4869. }
  4870. });
  4871. self.ui.progressbar.appendTo(self.ui.workzone);
  4872. self.ui.notify.prev('.ui-dialog-titlebar').append('<div class="elfinder-ui-progressbar"></div>');
  4873. // update size
  4874. self.resize(width, height);
  4875. // make node resizable
  4876. if (self.options.resizable) {
  4877. node.resizable({
  4878. resize : function(e, ui) {
  4879. self.resize(ui.size.width, ui.size.height);
  4880. },
  4881. handles : 'se',
  4882. minWidth : 300,
  4883. minHeight : 200
  4884. });
  4885. if (self.UA.Touch) {
  4886. node.addClass('touch-punch');
  4887. }
  4888. }
  4889. (function() {
  4890. var navbar = self.getUI('navbar'),
  4891. cwd = self.getUI('cwd').parent();
  4892. self.autoScroll = {
  4893. navbarUp : function(v) {
  4894. navbar.scrollTop(Math.max(0, navbar.scrollTop() - v));
  4895. },
  4896. navbarDown : function(v) {
  4897. navbar.scrollTop(navbar.scrollTop() + v);
  4898. },
  4899. cwdUp : function(v) {
  4900. cwd.scrollTop(Math.max(0, cwd.scrollTop() - v));
  4901. },
  4902. cwdDown : function(v) {
  4903. cwd.scrollTop(cwd.scrollTop() + v);
  4904. }
  4905. };
  4906. })();
  4907. // Swipe on the touch devices to show/hide of toolbar or navbar
  4908. if (self.UA.Touch) {
  4909. (function() {
  4910. var lastX, lastY, nodeOffset, nodeWidth, nodeTop, navbarW, toolbarH,
  4911. navbar = self.getUI('navbar'),
  4912. toolbar = self.getUI('toolbar'),
  4913. moveEv = 'touchmove.stopscroll',
  4914. moveTm,
  4915. moveUpOn = function(e) {
  4916. var touches = e.originalEvent.touches || [{}],
  4917. y = touches[0].pageY || null;
  4918. if (!lastY || y < lastY) {
  4919. e.preventDefault();
  4920. moveTm && clearTimeout(moveTm);
  4921. }
  4922. },
  4923. moveDownOn = function(e) {
  4924. e.preventDefault();
  4925. moveTm && clearTimeout(moveTm);
  4926. },
  4927. moveOff = function() {
  4928. moveTm = setTimeout(function() {
  4929. node.off(moveEv);
  4930. }, 100);
  4931. },
  4932. handleW, handleH = 50;
  4933. navbar = navbar.children().length? navbar : null;
  4934. toolbar = toolbar.length? toolbar : null;
  4935. node.on('touchstart touchmove touchend', function(e) {
  4936. if (e.type === 'touchend') {
  4937. lastX = false;
  4938. lastY = false;
  4939. moveOff();
  4940. return;
  4941. }
  4942. var touches = e.originalEvent.touches || [{}],
  4943. x = touches[0].pageX || null,
  4944. y = touches[0].pageY || null,
  4945. ltr = (self.direction === 'ltr'),
  4946. navbarMode, treeWidth, swipeX, moveX, toolbarT, mode;
  4947. if (x === null || y === null || (e.type === 'touchstart' && touches.length > 1)) {
  4948. return;
  4949. }
  4950. if (e.type === 'touchstart') {
  4951. nodeOffset = node.offset();
  4952. nodeWidth = node.width();
  4953. if (navbar) {
  4954. lastX = false;
  4955. if (navbar.is(':hidden')) {
  4956. if (! handleW) {
  4957. handleW = Math.max(50, nodeWidth / 10);
  4958. }
  4959. if ((ltr? (x - nodeOffset.left) : (nodeWidth + nodeOffset.left - x)) < handleW) {
  4960. lastX = x;
  4961. }
  4962. } else if (! e.originalEvent._preventSwipeX) {
  4963. navbarW = navbar.width();
  4964. if (ltr) {
  4965. swipeX = (x < nodeOffset.left + navbarW);
  4966. } else {
  4967. swipeX = (x > nodeOffset.left + nodeWidth - navbarW);
  4968. }
  4969. if (swipeX) {
  4970. handleW = Math.max(50, nodeWidth / 10);
  4971. lastX = x;
  4972. } else {
  4973. lastX = false;
  4974. }
  4975. }
  4976. }
  4977. if (toolbar) {
  4978. lastY = false;
  4979. if (! e.originalEvent._preventSwipeY) {
  4980. toolbarH = toolbar.height();
  4981. nodeTop = nodeOffset.top;
  4982. if (y - nodeTop < (toolbar.is(':hidden')? handleH : (toolbarH + 30))) {
  4983. lastY = y;
  4984. node.on(moveEv, toolbar.is(':hidden')? moveDownOn: moveUpOn);
  4985. }
  4986. }
  4987. }
  4988. } else {
  4989. if (navbar && lastX !== false) {
  4990. navbarMode = (ltr? (lastX > x) : (lastX < x))? 'navhide' : 'navshow';
  4991. moveX = Math.abs(lastX - x);
  4992. if (navbarMode === 'navhide' && moveX > navbarW * 0.6
  4993. || (moveX > (navbarMode === 'navhide'? navbarW / 3 : 45)
  4994. && (navbarMode === 'navshow'
  4995. || (ltr? x < nodeOffset.left + 20 : x > nodeOffset.left + nodeWidth - 20)
  4996. ))
  4997. ) {
  4998. self.getUI('navbar').trigger(navbarMode, {handleW: handleW});
  4999. lastX = false;
  5000. }
  5001. }
  5002. if (toolbar && lastY !== false ) {
  5003. toolbarT = toolbar.offset().top;
  5004. if (Math.abs(lastY - y) > Math.min(45, toolbarH / 3)) {
  5005. mode = (lastY > y)? 'slideUp' : 'slideDown';
  5006. if (mode === 'slideDown' || toolbarT + 20 > y) {
  5007. if (toolbar.is(mode === 'slideDown' ? ':hidden' : ':visible')) {
  5008. toolbar.stop(true, true).trigger('toggle', {duration: 100, handleH: handleH});
  5009. }
  5010. lastY = false;
  5011. }
  5012. }
  5013. }
  5014. }
  5015. });
  5016. })();
  5017. }
  5018. if (self.dragUpload) {
  5019. // add event listener for HTML5 DnD upload
  5020. (function() {
  5021. var isin = function(e) {
  5022. return (e.target.nodeName !== 'TEXTAREA' && e.target.nodeName !== 'INPUT' && $(e.target).closest('div.ui-dialog-content').length === 0);
  5023. },
  5024. ent = 'native-drag-enter',
  5025. disable = 'native-drag-disable',
  5026. c = 'class',
  5027. navdir = self.res(c, 'navdir'),
  5028. droppable = self.res(c, 'droppable'),
  5029. dropover = self.res(c, 'adroppable'),
  5030. arrow = self.res(c, 'navarrow'),
  5031. clDropActive = self.res(c, 'adroppable'),
  5032. wz = self.getUI('workzone'),
  5033. ltr = (self.direction === 'ltr'),
  5034. clearTm = function() {
  5035. autoScrTm && cancelAnimationFrame(autoScrTm);
  5036. autoScrTm = null;
  5037. },
  5038. wzRect, autoScrFn, autoScrTm;
  5039. node.on('dragenter', function(e) {
  5040. clearTm();
  5041. if (isin(e)) {
  5042. e.preventDefault();
  5043. e.stopPropagation();
  5044. wzRect = wz.data('rectangle');
  5045. }
  5046. })
  5047. .on('dragleave', function(e) {
  5048. clearTm();
  5049. if (isin(e)) {
  5050. e.preventDefault();
  5051. e.stopPropagation();
  5052. }
  5053. })
  5054. .on('dragover', function(e) {
  5055. var autoUp;
  5056. if (isin(e)) {
  5057. e.preventDefault();
  5058. e.stopPropagation();
  5059. e.originalEvent.dataTransfer.dropEffect = 'none';
  5060. if (! autoScrTm) {
  5061. autoScrTm = requestAnimationFrame(function() {
  5062. var wzBottom = wzRect.top + wzRect.height,
  5063. wzBottom2 = wzBottom - self.getUI('navdock').outerHeight(true),
  5064. fn;
  5065. if ((autoUp = e.pageY < wzRect.top) || e.pageY > wzBottom2 ) {
  5066. if (wzRect.cwdEdge > e.pageX) {
  5067. fn = (ltr? 'navbar' : 'cwd') + (autoUp? 'Up' : 'Down');
  5068. } else {
  5069. fn = (ltr? 'cwd' : 'navbar') + (autoUp? 'Up' : 'Down');
  5070. }
  5071. if (!autoUp) {
  5072. if (fn.substr(0, 3) === 'cwd') {
  5073. if (wzBottom < e.pageY) {
  5074. wzBottom2 = wzBottom;
  5075. } else {
  5076. fn = '';
  5077. }
  5078. }
  5079. }
  5080. fn && self.autoScroll[fn](Math.pow((autoUp? wzRect.top - e.pageY : e.pageY - wzBottom2), 1.3));
  5081. }
  5082. autoScrTm = null;
  5083. });
  5084. }
  5085. } else {
  5086. clearTm();
  5087. }
  5088. })
  5089. .on('drop', function(e) {
  5090. clearTm();
  5091. if (isin(e)) {
  5092. e.stopPropagation();
  5093. e.preventDefault();
  5094. }
  5095. });
  5096. node.on('dragenter', '.native-droppable', function(e){
  5097. if (e.originalEvent.dataTransfer) {
  5098. var $elm = $(e.currentTarget),
  5099. id = e.currentTarget.id || null,
  5100. cwd = null,
  5101. elfFrom;
  5102. if (!id) { // target is cwd
  5103. cwd = self.cwd();
  5104. $elm.data(disable, false);
  5105. try {
  5106. $.each(e.originalEvent.dataTransfer.types, function(i, v){
  5107. if (v.substr(0, 13) === 'elfinderfrom:') {
  5108. elfFrom = v.substr(13).toLowerCase();
  5109. }
  5110. });
  5111. } catch(e) {}
  5112. }
  5113. if (!cwd || (cwd.write && (!elfFrom || elfFrom !== (window.location.href + cwd.hash).toLowerCase()))) {
  5114. e.preventDefault();
  5115. e.stopPropagation();
  5116. $elm.data(ent, true);
  5117. $elm.addClass(clDropActive);
  5118. } else {
  5119. $elm.data(disable, true);
  5120. }
  5121. }
  5122. })
  5123. .on('dragleave', '.native-droppable', function(e){
  5124. if (e.originalEvent.dataTransfer) {
  5125. var $elm = $(e.currentTarget);
  5126. e.preventDefault();
  5127. e.stopPropagation();
  5128. if ($elm.data(ent)) {
  5129. $elm.data(ent, false);
  5130. } else {
  5131. $elm.removeClass(clDropActive);
  5132. }
  5133. }
  5134. })
  5135. .on('dragover', '.native-droppable', function(e){
  5136. if (e.originalEvent.dataTransfer) {
  5137. var $elm = $(e.currentTarget);
  5138. e.preventDefault();
  5139. e.stopPropagation();
  5140. e.originalEvent.dataTransfer.dropEffect = $elm.data(disable)? 'none' : 'copy';
  5141. $elm.data(ent, false);
  5142. }
  5143. })
  5144. .on('drop', '.native-droppable', function(e){
  5145. if (e.originalEvent && e.originalEvent.dataTransfer) {
  5146. var $elm = $(e.currentTarget),
  5147. id;
  5148. e.preventDefault();
  5149. e.stopPropagation();
  5150. $elm.removeClass(clDropActive);
  5151. if (e.currentTarget.id) {
  5152. id = $elm.hasClass(navdir)? self.navId2Hash(e.currentTarget.id) : self.cwdId2Hash(e.currentTarget.id);
  5153. } else {
  5154. id = self.cwd().hash;
  5155. }
  5156. e.originalEvent._target = id;
  5157. self.exec('upload', {dropEvt: e.originalEvent, target: id}, void 0, id);
  5158. }
  5159. });
  5160. })();
  5161. }
  5162. // trigger event cssloaded if cssAutoLoad disabled
  5163. if (self.cssloaded === false) {
  5164. self.cssloaded = true;
  5165. self.trigger('cssloaded');
  5166. }
  5167. // calculate elFinder node z-index
  5168. self.zIndexCalc();
  5169. // send initial request and start to pray >_<
  5170. self.trigger('init')
  5171. .request({
  5172. data : {cmd : 'open', target : self.startDir(), init : 1, tree : 1},
  5173. preventDone : true,
  5174. notify : {type : 'open', cnt : 1, hideCnt : true},
  5175. freeze : true
  5176. })
  5177. .fail(function() {
  5178. self.trigger('fail').disable().lastDir('');
  5179. listeners = {};
  5180. shortcuts = {};
  5181. $(document).add(node).off('.'+namespace);
  5182. self.trigger = function() { };
  5183. })
  5184. .done(function(data) {
  5185. var trashDisable = function(th) {
  5186. var src = self.file(self.trashes[th]),
  5187. d = self.options.debug,
  5188. error;
  5189. if (src && src.volumeid) {
  5190. delete self.volOptions[src.volumeid].trashHash;
  5191. }
  5192. self.trashes[th] = false;
  5193. self.debug('backend-error', 'Trash hash "'+th+'" was not found or not writable.');
  5194. },
  5195. toChkTh = {};
  5196. // regist rawStringDecoder
  5197. if (self.options.rawStringDecoder) {
  5198. self.registRawStringDecoder(self.options.rawStringDecoder);
  5199. }
  5200. // re-calculate elFinder node z-index
  5201. self.zIndexCalc();
  5202. self.load().debug('api', self.api);
  5203. // update ui's size after init
  5204. node.trigger('resize');
  5205. // initial open
  5206. open(data);
  5207. self.trigger('open', data, false);
  5208. self.trigger('opendone');
  5209. if (inFrame && self.options.enableAlways) {
  5210. $(window).trigger('focus');
  5211. }
  5212. // check self.trashes
  5213. $.each(self.trashes, function(th) {
  5214. var dir = self.file(th),
  5215. src;
  5216. if (! dir) {
  5217. toChkTh[th] = true;
  5218. } else if (dir.mime !== 'directory' || ! dir.write) {
  5219. trashDisable(th);
  5220. }
  5221. });
  5222. if (Object.keys(toChkTh).length) {
  5223. self.request({
  5224. data : {cmd : 'info', targets : Object.keys(toChkTh)},
  5225. preventDefault : true
  5226. }).done(function(data) {
  5227. if (data && data.files) {
  5228. $.each(data.files, function(i, dir) {
  5229. if (dir.mime === 'directory' && dir.write) {
  5230. delete toChkTh[dir.hash];
  5231. }
  5232. });
  5233. }
  5234. }).always(function() {
  5235. $.each(toChkTh, trashDisable);
  5236. });
  5237. }
  5238. // to enable / disable
  5239. self[self.options.enableAlways? 'enable' : 'disable']();
  5240. });
  5241. // self.timeEnd('load');
  5242. // End of bootUp()
  5243. };
  5244. // call bootCallback function with elFinder instance, extraObject - { dfrdsBeforeBootup: dfrdsBeforeBootup }
  5245. if (bootCallback && typeof bootCallback === 'function') {
  5246. self.bootCallback = bootCallback;
  5247. bootCallback.call(node.get(0), self, { dfrdsBeforeBootup: dfrdsBeforeBootup });
  5248. }
  5249. // call dfrdsBeforeBootup functions then boot up elFinder
  5250. $.when.apply(null, dfrdsBeforeBootup).done(function() {
  5251. bootUp();
  5252. }).fail(function(error) {
  5253. self.error(error);
  5254. });
  5255. };
  5256. //register elFinder to global scope
  5257. if (typeof toGlobal === 'undefined' || toGlobal) {
  5258. window.elFinder = elFinder;
  5259. }
  5260. /**
  5261. * Prototype
  5262. *
  5263. * @type Object
  5264. */
  5265. elFinder.prototype = {
  5266. uniqueid : 0,
  5267. res : function(type, id) {
  5268. return this.resources[type] && this.resources[type][id];
  5269. },
  5270. /**
  5271. * User os. Required to bind native shortcuts for open/rename
  5272. *
  5273. * @type String
  5274. **/
  5275. OS : navigator.userAgent.indexOf('Mac') !== -1 ? 'mac' : navigator.userAgent.indexOf('Win') !== -1 ? 'win' : 'other',
  5276. /**
  5277. * User browser UA.
  5278. * jQuery.browser: version deprecated: 1.3, removed: 1.9
  5279. *
  5280. * @type Object
  5281. **/
  5282. UA : (function(){
  5283. var self = this,
  5284. webkit = !document.unqueID && !window.opera && !window.sidebar && 'localStorage' in window && 'WebkitAppearance' in document.documentElement.style,
  5285. chrome = webkit && window.chrome,
  5286. /*setRotated = function() {
  5287. var a = ((screen && screen.orientation && screen.orientation.angle) || window.orientation || 0) + 0;
  5288. if (a === -90) {
  5289. a = 270;
  5290. }
  5291. UA.Angle = a;
  5292. UA.Rotated = a % 180 === 0? false : true;
  5293. },*/
  5294. UA = {
  5295. // Browser IE <= IE 6
  5296. ltIE6 : typeof window.addEventListener == "undefined" && typeof document.documentElement.style.maxHeight == "undefined",
  5297. // Browser IE <= IE 7
  5298. ltIE7 : typeof window.addEventListener == "undefined" && typeof document.querySelectorAll == "undefined",
  5299. // Browser IE <= IE 8
  5300. ltIE8 : typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined",
  5301. // Browser IE <= IE 9
  5302. ltIE9 : document.uniqueID && document.documentMode <= 9,
  5303. // Browser IE <= IE 10
  5304. ltIE10 : document.uniqueID && document.documentMode <= 10,
  5305. // Browser IE >= IE 11
  5306. gtIE11 : document.uniqueID && document.documentMode >= 11,
  5307. IE : document.uniqueID,
  5308. Firefox : window.sidebar,
  5309. Opera : window.opera,
  5310. Webkit : webkit,
  5311. Chrome : chrome,
  5312. Edge : (chrome && window.msCredentials)? true : false,
  5313. Safari : webkit && !window.chrome,
  5314. Mobile : typeof window.orientation != "undefined",
  5315. Touch : typeof window.ontouchstart != "undefined",
  5316. iOS : navigator.platform.match(/^iP(?:[ao]d|hone)/),
  5317. Mac : navigator.platform.match(/^Mac/),
  5318. Fullscreen : (typeof (document.exitFullscreen || document.webkitExitFullscreen || document.mozCancelFullScreen || document.msExitFullscreen) !== 'undefined'),
  5319. Angle : 0,
  5320. Rotated : false,
  5321. CSS : (function() {
  5322. var aStyle = document.createElement('a').style,
  5323. pStyle = document.createElement('p').style,
  5324. css;
  5325. css = 'position:sticky;position:-webkit-sticky;';
  5326. css += 'width:-webkit-max-content;width:-moz-max-content;width:-ms-max-content;width:max-content;';
  5327. aStyle.cssText = css;
  5328. return {
  5329. positionSticky : aStyle.position.indexOf('sticky')!==-1,
  5330. widthMaxContent : aStyle.width.indexOf('max-content')!==-1,
  5331. flex : typeof pStyle.flex !== 'undefined'
  5332. };
  5333. })()
  5334. };
  5335. return UA;
  5336. })(),
  5337. /**
  5338. * Is cookie enabled
  5339. *
  5340. * @type Boolean
  5341. */
  5342. cookieEnabled : (function() {
  5343. var res = false,
  5344. test = 'elftest=';
  5345. document.cookie = test + '1';
  5346. res = document.cookie.split(test).length === 2;
  5347. document.cookie = test + ';max-age=0';
  5348. return res;
  5349. })(),
  5350. /**
  5351. * Has RequireJS?
  5352. *
  5353. * @type Boolean
  5354. */
  5355. hasRequire : (typeof define === 'function' && define.amd),
  5356. /**
  5357. * Current request command
  5358. *
  5359. * @type String
  5360. */
  5361. currentReqCmd : '',
  5362. /**
  5363. * Current keyboard state
  5364. *
  5365. * @type Object
  5366. */
  5367. keyState : {},
  5368. /**
  5369. * Internationalization object
  5370. *
  5371. * @type Object
  5372. */
  5373. i18 : {
  5374. en : {
  5375. translator : '',
  5376. language : 'English',
  5377. direction : 'ltr',
  5378. dateFormat : 'd.m.Y H:i',
  5379. fancyDateFormat : '$1 H:i',
  5380. nonameDateFormat : 'ymd-His',
  5381. messages : {}
  5382. },
  5383. months : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
  5384. monthsShort : ['msJan', 'msFeb', 'msMar', 'msApr', 'msMay', 'msJun', 'msJul', 'msAug', 'msSep', 'msOct', 'msNov', 'msDec'],
  5385. days : ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
  5386. daysShort : ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
  5387. },
  5388. /**
  5389. * File mimetype to kind mapping
  5390. *
  5391. * @type Object
  5392. */
  5393. kinds : {
  5394. 'unknown' : 'Unknown',
  5395. 'directory' : 'Folder',
  5396. 'group' : 'Selects',
  5397. 'symlink' : 'Alias',
  5398. 'symlink-broken' : 'AliasBroken',
  5399. 'application/x-empty' : 'TextPlain',
  5400. 'application/postscript' : 'Postscript',
  5401. 'application/vnd.ms-office' : 'MsOffice',
  5402. 'application/msword' : 'MsWord',
  5403. 'application/vnd.ms-word' : 'MsWord',
  5404. 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' : 'MsWord',
  5405. 'application/vnd.ms-word.document.macroEnabled.12' : 'MsWord',
  5406. 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' : 'MsWord',
  5407. 'application/vnd.ms-word.template.macroEnabled.12' : 'MsWord',
  5408. 'application/vnd.ms-excel' : 'MsExcel',
  5409. 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' : 'MsExcel',
  5410. 'application/vnd.ms-excel.sheet.macroEnabled.12' : 'MsExcel',
  5411. 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' : 'MsExcel',
  5412. 'application/vnd.ms-excel.template.macroEnabled.12' : 'MsExcel',
  5413. 'application/vnd.ms-excel.sheet.binary.macroEnabled.12' : 'MsExcel',
  5414. 'application/vnd.ms-excel.addin.macroEnabled.12' : 'MsExcel',
  5415. 'application/vnd.ms-powerpoint' : 'MsPP',
  5416. 'application/vnd.openxmlformats-officedocument.presentationml.presentation' : 'MsPP',
  5417. 'application/vnd.ms-powerpoint.presentation.macroEnabled.12' : 'MsPP',
  5418. 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' : 'MsPP',
  5419. 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' : 'MsPP',
  5420. 'application/vnd.openxmlformats-officedocument.presentationml.template' : 'MsPP',
  5421. 'application/vnd.ms-powerpoint.template.macroEnabled.12' : 'MsPP',
  5422. 'application/vnd.ms-powerpoint.addin.macroEnabled.12' : 'MsPP',
  5423. 'application/vnd.openxmlformats-officedocument.presentationml.slide' : 'MsPP',
  5424. 'application/vnd.ms-powerpoint.slide.macroEnabled.12' : 'MsPP',
  5425. 'application/pdf' : 'PDF',
  5426. 'application/xml' : 'XML',
  5427. 'application/vnd.oasis.opendocument.text' : 'OO',
  5428. 'application/vnd.oasis.opendocument.text-template' : 'OO',
  5429. 'application/vnd.oasis.opendocument.text-web' : 'OO',
  5430. 'application/vnd.oasis.opendocument.text-master' : 'OO',
  5431. 'application/vnd.oasis.opendocument.graphics' : 'OO',
  5432. 'application/vnd.oasis.opendocument.graphics-template' : 'OO',
  5433. 'application/vnd.oasis.opendocument.presentation' : 'OO',
  5434. 'application/vnd.oasis.opendocument.presentation-template' : 'OO',
  5435. 'application/vnd.oasis.opendocument.spreadsheet' : 'OO',
  5436. 'application/vnd.oasis.opendocument.spreadsheet-template' : 'OO',
  5437. 'application/vnd.oasis.opendocument.chart' : 'OO',
  5438. 'application/vnd.oasis.opendocument.formula' : 'OO',
  5439. 'application/vnd.oasis.opendocument.database' : 'OO',
  5440. 'application/vnd.oasis.opendocument.image' : 'OO',
  5441. 'application/vnd.openofficeorg.extension' : 'OO',
  5442. 'application/x-shockwave-flash' : 'AppFlash',
  5443. 'application/flash-video' : 'Flash video',
  5444. 'application/x-bittorrent' : 'Torrent',
  5445. 'application/javascript' : 'JS',
  5446. 'application/rtf' : 'RTF',
  5447. 'application/rtfd' : 'RTF',
  5448. 'application/x-font-ttf' : 'TTF',
  5449. 'application/x-font-otf' : 'OTF',
  5450. 'application/x-rpm' : 'RPM',
  5451. 'application/x-web-config' : 'TextPlain',
  5452. 'application/xhtml+xml' : 'HTML',
  5453. 'application/docbook+xml' : 'DOCBOOK',
  5454. 'application/x-awk' : 'AWK',
  5455. 'application/x-gzip' : 'GZIP',
  5456. 'application/x-bzip2' : 'BZIP',
  5457. 'application/x-xz' : 'XZ',
  5458. 'application/zip' : 'ZIP',
  5459. 'application/x-zip' : 'ZIP',
  5460. 'application/x-rar' : 'RAR',
  5461. 'application/x-tar' : 'TAR',
  5462. 'application/x-7z-compressed' : '7z',
  5463. 'application/x-jar' : 'JAR',
  5464. 'text/plain' : 'TextPlain',
  5465. 'text/x-php' : 'PHP',
  5466. 'text/html' : 'HTML',
  5467. 'text/javascript' : 'JS',
  5468. 'text/css' : 'CSS',
  5469. 'text/rtf' : 'RTF',
  5470. 'text/rtfd' : 'RTF',
  5471. 'text/x-c' : 'C',
  5472. 'text/x-csrc' : 'C',
  5473. 'text/x-chdr' : 'CHeader',
  5474. 'text/x-c++' : 'CPP',
  5475. 'text/x-c++src' : 'CPP',
  5476. 'text/x-c++hdr' : 'CPPHeader',
  5477. 'text/x-shellscript' : 'Shell',
  5478. 'application/x-csh' : 'Shell',
  5479. 'text/x-python' : 'Python',
  5480. 'text/x-java' : 'Java',
  5481. 'text/x-java-source' : 'Java',
  5482. 'text/x-ruby' : 'Ruby',
  5483. 'text/x-perl' : 'Perl',
  5484. 'text/x-sql' : 'SQL',
  5485. 'text/xml' : 'XML',
  5486. 'text/x-comma-separated-values' : 'CSV',
  5487. 'text/x-markdown' : 'Markdown',
  5488. 'image/x-ms-bmp' : 'BMP',
  5489. 'image/jpeg' : 'JPEG',
  5490. 'image/gif' : 'GIF',
  5491. 'image/png' : 'PNG',
  5492. 'image/tiff' : 'TIFF',
  5493. 'image/x-targa' : 'TGA',
  5494. 'image/vnd.adobe.photoshop' : 'PSD',
  5495. 'image/xbm' : 'XBITMAP',
  5496. 'image/pxm' : 'PXM',
  5497. 'audio/mpeg' : 'AudioMPEG',
  5498. 'audio/midi' : 'AudioMIDI',
  5499. 'audio/ogg' : 'AudioOGG',
  5500. 'audio/mp4' : 'AudioMPEG4',
  5501. 'audio/x-m4a' : 'AudioMPEG4',
  5502. 'audio/wav' : 'AudioWAV',
  5503. 'audio/x-mp3-playlist' : 'AudioPlaylist',
  5504. 'video/x-dv' : 'VideoDV',
  5505. 'video/mp4' : 'VideoMPEG4',
  5506. 'video/mpeg' : 'VideoMPEG',
  5507. 'video/x-msvideo' : 'VideoAVI',
  5508. 'video/quicktime' : 'VideoMOV',
  5509. 'video/x-ms-wmv' : 'VideoWM',
  5510. 'video/x-flv' : 'VideoFlash',
  5511. 'video/x-matroska' : 'VideoMKV',
  5512. 'video/ogg' : 'VideoOGG'
  5513. },
  5514. /**
  5515. * File mimetype to file extention mapping
  5516. *
  5517. * @type Object
  5518. * @see elFinder.mimetypes.js
  5519. */
  5520. mimeTypes : {},
  5521. /**
  5522. * Ajax request data validation rules
  5523. *
  5524. * @type Object
  5525. */
  5526. rules : {
  5527. defaults : function(data) {
  5528. if (!data
  5529. || (data.added && !Array.isArray(data.added))
  5530. || (data.removed && !Array.isArray(data.removed))
  5531. || (data.changed && !Array.isArray(data.changed))) {
  5532. return false;
  5533. }
  5534. return true;
  5535. },
  5536. open : function(data) { return data && data.cwd && data.files && $.isPlainObject(data.cwd) && Array.isArray(data.files); },
  5537. tree : function(data) { return data && data.tree && Array.isArray(data.tree); },
  5538. parents : function(data) { return data && data.tree && Array.isArray(data.tree); },
  5539. tmb : function(data) { return data && data.images && ($.isPlainObject(data.images) || Array.isArray(data.images)); },
  5540. upload : function(data) { return data && ($.isPlainObject(data.added) || Array.isArray(data.added));},
  5541. search : function(data) { return data && data.files && Array.isArray(data.files); }
  5542. },
  5543. /**
  5544. * Commands costructors
  5545. *
  5546. * @type Object
  5547. */
  5548. commands : {},
  5549. /**
  5550. * Commands to add the item (space delimited)
  5551. *
  5552. * @type String
  5553. */
  5554. cmdsToAdd : 'archive duplicate extract mkdir mkfile paste rm upload',
  5555. parseUploadData : function(text) {
  5556. var self = this,
  5557. data;
  5558. if (!$.trim(text)) {
  5559. return {error : ['errResponse', 'errDataEmpty']};
  5560. }
  5561. try {
  5562. data = JSON.parse(text);
  5563. } catch (e) {
  5564. return {error : ['errResponse', 'errDataNotJSON']};
  5565. }
  5566. data = self.normalize(data);
  5567. if (!self.validResponse('upload', data)) {
  5568. return {error : (data.norError || ['errResponse'])};
  5569. }
  5570. data.removed = $.merge((data.removed || []), $.map(data.added || [], function(f) { return self.file(f.hash)? f.hash : null; }));
  5571. return data;
  5572. },
  5573. iframeCnt : 0,
  5574. uploads : {
  5575. // xhr muiti uploading flag
  5576. xhrUploading: false,
  5577. // Timer of request fail to sync
  5578. failSyncTm: null,
  5579. // current chunkfail requesting chunk
  5580. chunkfailReq: {},
  5581. // check file/dir exists
  5582. checkExists: function(files, target, fm, isDir) {
  5583. var dfrd = $.Deferred(),
  5584. names, renames = [], hashes = {}, chkFiles = [],
  5585. cancel = function() {
  5586. var i = files.length;
  5587. while (--i > -1) {
  5588. files[i]._remove = true;
  5589. }
  5590. },
  5591. resolve = function() {
  5592. dfrd.resolve(renames, hashes);
  5593. },
  5594. check = function() {
  5595. var existed = [], exists = [], i, c,
  5596. pathStr = target !== fm.cwd().hash? fm.path(target, true) + fm.option('separator', target) : '',
  5597. confirm = function(ndx) {
  5598. var last = ndx == exists.length-1,
  5599. opts = {
  5600. cssClass : 'elfinder-confirm-upload',
  5601. title : fm.i18n('cmdupload'),
  5602. text : ['errExists', pathStr + exists[ndx].name, 'confirmRepl'],
  5603. all : !last,
  5604. accept : {
  5605. label : 'btnYes',
  5606. callback : function(all) {
  5607. !last && !all
  5608. ? confirm(++ndx)
  5609. : resolve();
  5610. }
  5611. },
  5612. reject : {
  5613. label : 'btnNo',
  5614. callback : function(all) {
  5615. var i;
  5616. if (all) {
  5617. i = exists.length;
  5618. while (ndx < i--) {
  5619. files[exists[i].i]._remove = true;
  5620. }
  5621. } else {
  5622. files[exists[ndx].i]._remove = true;
  5623. }
  5624. !last && !all
  5625. ? confirm(++ndx)
  5626. : resolve();
  5627. }
  5628. },
  5629. cancel : {
  5630. label : 'btnCancel',
  5631. callback : function() {
  5632. cancel();
  5633. resolve();
  5634. }
  5635. },
  5636. buttons : [
  5637. {
  5638. label : 'btnBackup',
  5639. cssClass : 'elfinder-confirm-btn-backup',
  5640. callback : function(all) {
  5641. var i;
  5642. if (all) {
  5643. i = exists.length;
  5644. while (ndx < i--) {
  5645. renames.push(exists[i].name);
  5646. }
  5647. } else {
  5648. renames.push(exists[ndx].name);
  5649. }
  5650. !last && !all
  5651. ? confirm(++ndx)
  5652. : resolve();
  5653. }
  5654. }
  5655. ]
  5656. };
  5657. if (!isDir) {
  5658. opts.buttons.push({
  5659. label : 'btnRename' + (last? '' : 'All'),
  5660. cssClass : 'elfinder-confirm-btn-rename',
  5661. callback : function() {
  5662. renames = null;
  5663. resolve();
  5664. }
  5665. });
  5666. }
  5667. if (fm.iframeCnt > 0) {
  5668. delete opts.reject;
  5669. }
  5670. fm.confirm(opts);
  5671. };
  5672. if (! fm.file(target).read) {
  5673. // for dropbox type
  5674. resolve();
  5675. return;
  5676. }
  5677. names = $.map(files, function(file, i) { return file.name && (!fm.UA.iOS || file.name !== 'image.jpg')? {i: i, name: file.name} : null ;});
  5678. fm.request({
  5679. data : {cmd : 'ls', target : target, intersect : $.map(names, function(item) { return item.name;})},
  5680. notify : {type : 'preupload', cnt : 1, hideCnt : true},
  5681. preventDefault : true
  5682. })
  5683. .done(function(data) {
  5684. var existedArr, cwdItems;
  5685. if (data) {
  5686. if (data.error) {
  5687. cancel();
  5688. } else {
  5689. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  5690. if (data.list) {
  5691. if (Array.isArray(data.list)) {
  5692. existed = data.list || [];
  5693. } else {
  5694. existedArr = [];
  5695. existed = $.map(data.list, function(n) {
  5696. if (typeof n === 'string') {
  5697. return n;
  5698. } else {
  5699. // support to >=2.1.11 plugin Normalizer, Sanitizer
  5700. existedArr = existedArr.concat(n);
  5701. return false;
  5702. }
  5703. });
  5704. if (existedArr.length) {
  5705. existed = existed.concat(existedArr);
  5706. }
  5707. hashes = data.list;
  5708. }
  5709. exists = $.grep(names, function(name){
  5710. return $.inArray(name.name, existed) !== -1 ? true : false ;
  5711. });
  5712. if (exists.length && existed.length && target == fm.cwd().hash) {
  5713. cwdItems = $.map(fm.files(target), function(file) { return file.name; } );
  5714. if ($.grep(existed, function(n) {
  5715. return $.inArray(n, cwdItems) === -1? true : false;
  5716. }).length){
  5717. fm.sync();
  5718. }
  5719. }
  5720. }
  5721. }
  5722. }
  5723. }
  5724. if (exists.length > 0) {
  5725. confirm(0);
  5726. } else {
  5727. resolve();
  5728. }
  5729. })
  5730. .fail(function(error) {
  5731. cancel();
  5732. resolve();
  5733. error && fm.error(error);
  5734. });
  5735. };
  5736. if (fm.api >= 2.1 && typeof files[0] == 'object') {
  5737. check();
  5738. } else {
  5739. resolve();
  5740. }
  5741. return dfrd;
  5742. },
  5743. // check droped contents
  5744. checkFile : function(data, fm, target) {
  5745. if (!!data.checked || data.type == 'files') {
  5746. return data.files;
  5747. } else if (data.type == 'data') {
  5748. var dfrd = $.Deferred(),
  5749. scanDfd = $.Deferred(),
  5750. files = [],
  5751. paths = [],
  5752. dirctorys = [],
  5753. processing = 0,
  5754. items,
  5755. mkdirs = [],
  5756. cancel = false,
  5757. toArray = function(list) {
  5758. return Array.prototype.slice.call(list || [], 0);
  5759. },
  5760. doScan = function(items) {
  5761. var entry, readEntries,
  5762. excludes = fm.options.folderUploadExclude[fm.OS] || null,
  5763. length = items.length,
  5764. check = function() {
  5765. if (--processing < 1 && scanDfd.state() === 'pending') {
  5766. scanDfd.resolve();
  5767. }
  5768. },
  5769. pushItem = function(file) {
  5770. if (! excludes || ! file.name.match(excludes)) {
  5771. paths.push(entry.fullPath || '');
  5772. files.push(file);
  5773. }
  5774. check();
  5775. },
  5776. readEntries = function(dirReader) {
  5777. var entries = [],
  5778. read = function() {
  5779. dirReader.readEntries(function(results) {
  5780. if (cancel || !results.length) {
  5781. for (var i = 0; i < entries.length; i++) {
  5782. if (cancel) {
  5783. scanDfd.reject();
  5784. break;
  5785. }
  5786. doScan([entries[i]]);
  5787. }
  5788. check();
  5789. } else {
  5790. entries = entries.concat(toArray(results));
  5791. read();
  5792. }
  5793. }, check);
  5794. };
  5795. read();
  5796. };
  5797. processing++;
  5798. for (var i = 0; i < length; i++) {
  5799. if (cancel) {
  5800. scanDfd.reject();
  5801. break;
  5802. }
  5803. entry = items[i];
  5804. if (entry) {
  5805. if (entry.isFile) {
  5806. processing++;
  5807. entry.file(pushItem, check);
  5808. } else if (entry.isDirectory) {
  5809. if (fm.api >= 2.1) {
  5810. processing++;
  5811. mkdirs.push(entry.fullPath);
  5812. readEntries(entry.createReader()); // Start reading dirs.
  5813. }
  5814. }
  5815. }
  5816. }
  5817. check();
  5818. return scanDfd;
  5819. }, hasDirs;
  5820. items = $.map(data.files.items, function(item){
  5821. return item.getAsEntry? item.getAsEntry() : item.webkitGetAsEntry();
  5822. });
  5823. $.each(items, function(i, item) {
  5824. if (item.isDirectory) {
  5825. hasDirs = true;
  5826. return false;
  5827. }
  5828. });
  5829. if (items.length > 0) {
  5830. fm.uploads.checkExists(items, target, fm, hasDirs).done(function(renames, hashes){
  5831. var dfds = [];
  5832. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  5833. if (renames === null) {
  5834. data.overwrite = 0;
  5835. renames = [];
  5836. }
  5837. items = $.grep(items, function(item){
  5838. var i, bak, hash, dfd, hi;
  5839. if (item.isDirectory && renames.length) {
  5840. i = $.inArray(item.name, renames);
  5841. if (i !== -1) {
  5842. renames.splice(i, 1);
  5843. bak = fm.uniqueName(item.name + fm.options.backupSuffix , null, '');
  5844. $.each(hashes, function(h, name) {
  5845. if (item.name == name) {
  5846. hash = h;
  5847. return false;
  5848. }
  5849. });
  5850. if (! hash) {
  5851. hash = fm.fileByName(item.name, target).hash;
  5852. }
  5853. fm.lockfiles({files : [hash]});
  5854. dfd = fm.request({
  5855. data : {cmd : 'rename', target : hash, name : bak},
  5856. notify : {type : 'rename', cnt : 1}
  5857. })
  5858. .fail(function() {
  5859. item._remove = true;
  5860. fm.sync();
  5861. })
  5862. .always(function() {
  5863. fm.unlockfiles({files : [hash]});
  5864. });
  5865. dfds.push(dfd);
  5866. }
  5867. }
  5868. return !item._remove? true : false;
  5869. });
  5870. }
  5871. $.when.apply($, dfds).done(function(){
  5872. var notifyto, msg,
  5873. id = +new Date();
  5874. if (items.length > 0) {
  5875. msg = fm.escape(items[0].name);
  5876. if (items.length > 1) {
  5877. msg += ' ... ' + items.length + fm.i18n('items');
  5878. }
  5879. notifyto = setTimeout(function() {
  5880. fm.notify({
  5881. type : 'readdir',
  5882. id : id,
  5883. cnt : 1,
  5884. hideCnt: true,
  5885. msg : fm.i18n('ntfreaddir') + ' (' + msg + ')',
  5886. cancel: function() {
  5887. cancel = true;
  5888. }
  5889. });
  5890. }, fm.options.notifyDelay);
  5891. doScan(items).done(function() {
  5892. notifyto && clearTimeout(notifyto);
  5893. fm.notify({type : 'readdir', id: id, cnt : -1});
  5894. if (cancel) {
  5895. dfrd.reject();
  5896. } else {
  5897. dfrd.resolve([files, paths, renames, hashes, mkdirs]);
  5898. }
  5899. }).fail(function() {
  5900. dfrd.reject();
  5901. });
  5902. } else {
  5903. dfrd.reject();
  5904. }
  5905. });
  5906. });
  5907. return dfrd.promise();
  5908. } else {
  5909. return dfrd.reject();
  5910. }
  5911. } else {
  5912. var ret = [];
  5913. var check = [];
  5914. var str = data.files[0];
  5915. if (data.type == 'html') {
  5916. var tmp = $("<html></html>").append($.parseHTML(str.replace(/ src=/ig, ' _elfsrc='))),
  5917. atag;
  5918. $('img[_elfsrc]', tmp).each(function(){
  5919. var url, purl,
  5920. self = $(this),
  5921. pa = self.closest('a');
  5922. if (pa && pa.attr('href') && pa.attr('href').match(/\.(?:jpe?g|gif|bmp|png)/i)) {
  5923. purl = pa.attr('href');
  5924. }
  5925. url = self.attr('_elfsrc');
  5926. if (url) {
  5927. if (purl) {
  5928. $.inArray(purl, ret) == -1 && ret.push(purl);
  5929. $.inArray(url, check) == -1 && check.push(url);
  5930. } else {
  5931. $.inArray(url, ret) == -1 && ret.push(url);
  5932. }
  5933. }
  5934. // Probably it's clipboard data
  5935. if (ret.length === 1 && ret[0].match(/^data:image\/png/)) {
  5936. data.clipdata = true;
  5937. }
  5938. });
  5939. atag = $('a[href]', tmp);
  5940. atag.each(function(){
  5941. var text, loc,
  5942. parseUrl = function(url) {
  5943. var a = document.createElement('a');
  5944. a.href = url;
  5945. return a;
  5946. };
  5947. if (text = $(this).text()) {
  5948. loc = parseUrl($(this).attr('href'));
  5949. if (loc.href && loc.href.match(/^(?:ht|f)tp/i) && (atag.length === 1 || ! loc.pathname.match(/(?:\.html?|\/[^\/.]*)$/i) || $.trim(text).match(/\.[a-z0-9-]{1,10}$/i))) {
  5950. if ($.inArray(loc.href, ret) == -1 && $.inArray(loc.href, check) == -1) ret.push(loc.href);
  5951. }
  5952. }
  5953. });
  5954. } else {
  5955. var regex, m, url;
  5956. regex = /((?:ht|f)tps?:\/\/[-_.!~*\'()a-z0-9;/?:\@&=+\$,%#\*\[\]]+)/ig;
  5957. while (m = regex.exec(str)) {
  5958. url = m[1].replace(/&amp;/g, '&');
  5959. if ($.inArray(url, ret) == -1) ret.push(url);
  5960. }
  5961. }
  5962. return ret;
  5963. }
  5964. },
  5965. // upload transport using XMLHttpRequest
  5966. xhr : function(data, fm) {
  5967. var self = fm ? fm : this,
  5968. node = self.getUI(),
  5969. xhr = new XMLHttpRequest(),
  5970. notifyto = null,
  5971. notifyto1 = null,
  5972. notifyto2 = null,
  5973. dataChecked = data.checked,
  5974. isDataType = (data.isDataType || data.type == 'data'),
  5975. target = (data.target || self.cwd().hash),
  5976. dropEvt = (data.dropEvt || null),
  5977. extraData = data.extraData || null,
  5978. chunkEnable = (self.option('uploadMaxConn', target) != -1),
  5979. multiMax = Math.min(5, Math.max(1, self.option('uploadMaxConn', target))),
  5980. retryWait = 10000, // 10 sec
  5981. retryMax = 30, // 10 sec * 30 = 300 secs (Max 5 mins)
  5982. retry = 0,
  5983. getFile = function(files) {
  5984. var dfd = $.Deferred(),
  5985. file;
  5986. if (files.promise) {
  5987. files.always(function(f) {
  5988. dfd.resolve(Array.isArray(f) && f.length? (isDataType? f[0][0] : f[0]) : {});
  5989. });
  5990. } else {
  5991. dfd.resolve(files.length? (isDataType? files[0][0] : files[0]) : {});
  5992. }
  5993. return dfd;
  5994. },
  5995. dfrd = $.Deferred()
  5996. .fail(function(err) {
  5997. var error = self.parseError(err),
  5998. userAbort;
  5999. if (error === 'userabort') {
  6000. userAbort = true;
  6001. error = void 0;
  6002. }
  6003. if (files && (self.uploads.xhrUploading || userAbort)) {
  6004. // send request om fail
  6005. getFile(files).done(function(file) {
  6006. if (!userAbort) {
  6007. triggerError(error, file);
  6008. }
  6009. if (! file._cid) {
  6010. // send sync request
  6011. self.uploads.failSyncTm && clearTimeout(self.uploads.failSyncTm);
  6012. self.uploads.failSyncTm = setTimeout(function() {
  6013. self.sync(target);
  6014. }, 1000);
  6015. } else if (! self.uploads.chunkfailReq[file._cid]) {
  6016. // send chunkfail request
  6017. self.uploads.chunkfailReq[file._cid] = true;
  6018. setTimeout(function() {
  6019. fm.request({
  6020. data : {
  6021. cmd: 'upload',
  6022. target: target,
  6023. chunk: file._chunk,
  6024. cid: file._cid,
  6025. upload: ['chunkfail'],
  6026. mimes: 'chunkfail'
  6027. },
  6028. options : {
  6029. type: 'post',
  6030. url: self.uploadURL
  6031. },
  6032. preventDefault: true
  6033. }).always(function() {
  6034. delete self.uploads.chunkfailReq[file._chunk];
  6035. });
  6036. }, 1000);
  6037. }
  6038. });
  6039. } else {
  6040. triggerError(error);
  6041. }
  6042. !userAbort && self.sync();
  6043. self.uploads.xhrUploading = false;
  6044. files = null;
  6045. })
  6046. .done(function(data) {
  6047. self.uploads.xhrUploading = false;
  6048. files = null;
  6049. if (data) {
  6050. self.currentReqCmd = 'upload';
  6051. data.warning && triggerError(data.warning);
  6052. self.updateCache(data);
  6053. data.removed && data.removed.length && self.remove(data);
  6054. data.added && data.added.length && self.add(data);
  6055. data.changed && data.changed.length && self.change(data);
  6056. self.trigger('upload', data, false);
  6057. self.trigger('uploaddone');
  6058. if (data.toasts && Array.isArray(data.toasts)) {
  6059. $.each(data.toasts, function() {
  6060. this.msg && self.toast(this);
  6061. });
  6062. }
  6063. data.sync && self.sync();
  6064. if (data.debug) {
  6065. self.responseDebug(data);
  6066. fm.debug('backend-debug', data);
  6067. }
  6068. }
  6069. })
  6070. .always(function() {
  6071. self.abortXHR(xhr);
  6072. // unregist fnAbort function
  6073. node.off('uploadabort', fnAbort);
  6074. $(window).off('unload', fnAbort);
  6075. notifyto && clearTimeout(notifyto);
  6076. notifyto1 && clearTimeout(notifyto1);
  6077. notifyto2 && clearTimeout(notifyto2);
  6078. dataChecked && !data.multiupload && checkNotify() && self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
  6079. notifyto1 && uploadedNtf && self.notify({type : 'chunkmerge', cnt : -cnt});
  6080. chunkMerge && notifyElm.children('.elfinder-notify-chunkmerge').length && self.notify({type : 'chunkmerge', cnt : -1});
  6081. }),
  6082. formData = new FormData(),
  6083. files = data.input ? data.input.files : self.uploads.checkFile(data, self, target),
  6084. cnt = data.checked? (isDataType? files[0].length : files.length) : files.length,
  6085. isChunked = false,
  6086. loaded = 0,
  6087. prev = 0,
  6088. filesize = 0,
  6089. notify = false,
  6090. notifyElm = self.ui.notify,
  6091. cancelBtn = true,
  6092. uploadedNtf = false,
  6093. abort = false,
  6094. checkNotify = function() {
  6095. if (!notify && (ntfUpload = notifyElm.children('.elfinder-notify-upload')).length) {
  6096. notify = true;
  6097. }
  6098. return notify;
  6099. },
  6100. fnAbort = function(e, error) {
  6101. abort = true;
  6102. self.abortXHR(xhr, { quiet: true, abort: true });
  6103. dfrd.reject(error);
  6104. if (checkNotify()) {
  6105. self.notify({type : 'upload', cnt : ntfUpload.data('cnt') * -1, progress : 0, size : 0});
  6106. }
  6107. },
  6108. cancelToggle = function(show, hasChunk) {
  6109. ntfUpload.children('.elfinder-notify-cancel')[show? 'show':'hide']();
  6110. cancelBtn = show;
  6111. },
  6112. startNotify = function(size) {
  6113. if (!size) size = filesize;
  6114. return setTimeout(function() {
  6115. notify = true;
  6116. self.notify({type : 'upload', cnt : cnt, progress : loaded - prev, size : size,
  6117. cancel: function() {
  6118. node.trigger('uploadabort', 'userabort');
  6119. }
  6120. });
  6121. ntfUpload = notifyElm.children('.elfinder-notify-upload');
  6122. prev = loaded;
  6123. if (data.multiupload) {
  6124. cancelBtn && cancelToggle(true);
  6125. } else {
  6126. cancelToggle(cancelBtn && loaded < size);
  6127. }
  6128. }, self.options.notifyDelay);
  6129. },
  6130. doRetry = function() {
  6131. if (retry++ <= retryMax) {
  6132. if (checkNotify() && prev) {
  6133. self.notify({type : 'upload', cnt : 0, progress : 0, size : prev});
  6134. }
  6135. self.abortXHR(xhr, { quiet: true });
  6136. prev = loaded = 0;
  6137. setTimeout(function() {
  6138. var reqId;
  6139. if (! abort) {
  6140. xhr.open('POST', self.uploadURL, true);
  6141. if (self.api >= 2.1029) {
  6142. reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16);
  6143. (typeof formData['delete'] === 'function') && formData['delete']('reqid');
  6144. formData.append('reqid', reqId);
  6145. xhr._requestId = reqId;
  6146. }
  6147. xhr.send(formData);
  6148. }
  6149. }, retryWait);
  6150. } else {
  6151. node.trigger('uploadabort', ['errAbort', 'errTimeout']);
  6152. }
  6153. },
  6154. progress = function() {
  6155. var node;
  6156. if (notify) {
  6157. dfrd.notifyWith(ntfUpload, [{
  6158. cnt: ntfUpload.data('cnt'),
  6159. progress: ntfUpload.data('progress'),
  6160. total: ntfUpload.data('total')
  6161. }]);
  6162. }
  6163. },
  6164. triggerError = function(err, file, unite) {
  6165. err && self.trigger('xhruploadfail', { error: err, file: file });
  6166. if (unite) {
  6167. if (err) {
  6168. if (errCnt < self.options.maxErrorDialogs) {
  6169. if (Array.isArray(err)) {
  6170. errors = errors.concat(err);
  6171. } else {
  6172. errors.push(err);
  6173. }
  6174. }
  6175. errCnt++;
  6176. }
  6177. } else {
  6178. if (err) {
  6179. self.error(err);
  6180. } else {
  6181. if (errors.length) {
  6182. if (errCnt >= self.options.maxErrorDialogs) {
  6183. errors = errors.concat('moreErrors', errCnt - self.options.maxErrorDialogs);
  6184. }
  6185. self.error(errors);
  6186. }
  6187. errors = [];
  6188. errCnt = 0;
  6189. }
  6190. }
  6191. },
  6192. errors = [],
  6193. errCnt = 0,
  6194. renames = (data.renames || null),
  6195. hashes = (data.hashes || null),
  6196. chunkMerge = false,
  6197. ntfUpload = $();
  6198. // regist fnAbort function
  6199. node.one('uploadabort', fnAbort);
  6200. $(window).one('unload.' + fm.namespace, fnAbort);
  6201. !chunkMerge && (prev = loaded);
  6202. if (!isDataType && !cnt) {
  6203. return dfrd.reject(['errUploadNoFiles']);
  6204. }
  6205. xhr.addEventListener('error', function() {
  6206. if (xhr.status == 0) {
  6207. if (abort) {
  6208. dfrd.reject();
  6209. } else {
  6210. // ff bug while send zero sized file
  6211. // for safari - send directory
  6212. if (!isDataType && data.files && $.grep(data.files, function(f){return ! f.type && f.size === (self.UA.Safari? 1802 : 0)? true : false;}).length) {
  6213. dfrd.reject(['errAbort', 'errFolderUpload']);
  6214. } else if (data.input && $.grep(data.input.files, function(f){return ! f.type && f.size === (self.UA.Safari? 1802 : 0)? true : false;}).length) {
  6215. dfrd.reject(['errUploadNoFiles']);
  6216. } else {
  6217. doRetry();
  6218. }
  6219. }
  6220. } else {
  6221. node.trigger('uploadabort', 'errConnect');
  6222. }
  6223. }, false);
  6224. xhr.addEventListener('load', function(e) {
  6225. var status = xhr.status, res, curr = 0, error = '', errData, errObj;
  6226. self.setCustomHeaderByXhr(xhr);
  6227. if (status >= 400) {
  6228. if (status > 500) {
  6229. error = 'errResponse';
  6230. } else {
  6231. error = ['errResponse', 'errServerError'];
  6232. }
  6233. } else {
  6234. if (!xhr.responseText) {
  6235. error = ['errResponse', 'errDataEmpty'];
  6236. }
  6237. }
  6238. if (error) {
  6239. node.trigger('uploadabort');
  6240. getFile(files || {}).done(function(file) {
  6241. return dfrd.reject(file._cid? null : error);
  6242. });
  6243. }
  6244. loaded = filesize;
  6245. if (checkNotify() && (curr = loaded - prev)) {
  6246. self.notify({type : 'upload', cnt : 0, progress : curr, size : 0});
  6247. progress();
  6248. }
  6249. res = self.parseUploadData(xhr.responseText);
  6250. // chunked upload commit
  6251. if (res._chunkmerged) {
  6252. formData = new FormData();
  6253. var _file = [{_chunkmerged: res._chunkmerged, _name: res._name, _mtime: res._mtime}];
  6254. chunkMerge = true;
  6255. node.off('uploadabort', fnAbort);
  6256. notifyto2 = setTimeout(function() {
  6257. self.notify({type : 'chunkmerge', cnt : 1});
  6258. }, self.options.notifyDelay);
  6259. isDataType? send(_file, files[1]) : send(_file);
  6260. return;
  6261. }
  6262. res._multiupload = data.multiupload? true : false;
  6263. if (res.error) {
  6264. errData = {
  6265. cmd: 'upload',
  6266. err: res,
  6267. xhr: xhr,
  6268. rc: xhr.status
  6269. };
  6270. self.trigger('uploadfail', res);
  6271. // trigger "requestError" event
  6272. self.trigger('requestError', errData);
  6273. if (errData._getEvent && errData._getEvent().isDefaultPrevented()) {
  6274. res.error = '';
  6275. }
  6276. if (res._chunkfailure || res._multiupload) {
  6277. abort = true;
  6278. self.uploads.xhrUploading = false;
  6279. notifyto && clearTimeout(notifyto);
  6280. if (ntfUpload.length) {
  6281. self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
  6282. dfrd.reject(res);
  6283. } else {
  6284. // for multi connection
  6285. dfrd.reject();
  6286. }
  6287. } else {
  6288. dfrd.reject(res);
  6289. }
  6290. } else {
  6291. dfrd.resolve(res);
  6292. }
  6293. }, false);
  6294. xhr.upload.addEventListener('loadstart', function(e) {
  6295. if (!chunkMerge && e.lengthComputable) {
  6296. loaded = e.loaded;
  6297. retry && (loaded = 0);
  6298. filesize = e.total;
  6299. if (!loaded) {
  6300. loaded = parseInt(filesize * 0.05);
  6301. }
  6302. if (checkNotify()) {
  6303. self.notify({type : 'upload', cnt : 0, progress : loaded - prev, size : data.multiupload? 0 : filesize});
  6304. prev = loaded;
  6305. progress();
  6306. }
  6307. }
  6308. }, false);
  6309. xhr.upload.addEventListener('progress', function(e) {
  6310. var curr;
  6311. if (e.lengthComputable && !chunkMerge && xhr.readyState < 2) {
  6312. loaded = e.loaded;
  6313. // to avoid strange bug in safari (not in chrome) with drag&drop.
  6314. // bug: macos finder opened in any folder,
  6315. // reset safari cache (option+command+e), reload elfinder page,
  6316. // drop file from finder
  6317. // on first attempt request starts (progress callback called ones) but never ends.
  6318. // any next drop - successfull.
  6319. if (!data.checked && loaded > 0 && !notifyto) {
  6320. notifyto = startNotify(xhr._totalSize - loaded);
  6321. }
  6322. if (!filesize) {
  6323. filesize = e.total;
  6324. if (!loaded) {
  6325. loaded = parseInt(filesize * 0.05);
  6326. }
  6327. }
  6328. curr = loaded - prev;
  6329. if (checkNotify() && (curr/e.total) >= 0.05) {
  6330. self.notify({type : 'upload', cnt : 0, progress : curr, size : 0});
  6331. prev = loaded;
  6332. progress();
  6333. }
  6334. if (!uploadedNtf && loaded >= filesize && !isChunked) {
  6335. // Use "chunkmerge" for "server-in-process" notification
  6336. uploadedNtf = true;
  6337. notifyto1 = setTimeout(function() {
  6338. self.notify({type : 'chunkmerge', cnt : cnt});
  6339. }, self.options.notifyDelay);
  6340. }
  6341. if (cancelBtn && ! data.multiupload && loaded >= filesize) {
  6342. checkNotify() && cancelToggle(false);
  6343. }
  6344. }
  6345. }, false);
  6346. var send = function(files, paths){
  6347. var size = 0,
  6348. fcnt = 1,
  6349. sfiles = [],
  6350. c = 0,
  6351. total = cnt,
  6352. maxFileSize,
  6353. totalSize = 0,
  6354. chunked = [],
  6355. chunkID = new Date().getTime().toString().substr(-9), // for take care of the 32bit backend system
  6356. BYTES_PER_CHUNK = Math.min((fm.uplMaxSize? fm.uplMaxSize : 2097152) - 8190, fm.options.uploadMaxChunkSize), // uplMaxSize margin 8kb or options.uploadMaxChunkSize
  6357. blobSlice = chunkEnable? false : '',
  6358. blobSize, blobMtime, blobName, i, start, end, chunks, blob, chunk, added, done, last, failChunk,
  6359. multi = function(files, num){
  6360. var sfiles = [], cid, sfilesLen = 0, cancelChk, hasChunk;
  6361. if (!abort) {
  6362. while(files.length && sfiles.length < num) {
  6363. sfiles.push(files.shift());
  6364. }
  6365. sfilesLen = sfiles.length;
  6366. if (sfilesLen) {
  6367. cancelChk = sfilesLen;
  6368. for (var i=0; i < sfilesLen; i++) {
  6369. if (abort) {
  6370. break;
  6371. }
  6372. cid = isDataType? (sfiles[i][0][0]._cid || null) : (sfiles[i][0]._cid || null);
  6373. hasChunk = (hasChunk || cid)? true : false;
  6374. if (!!failChunk[cid]) {
  6375. last--;
  6376. continue;
  6377. }
  6378. fm.exec('upload', {
  6379. type: data.type,
  6380. isDataType: isDataType,
  6381. files: sfiles[i],
  6382. checked: true,
  6383. target: target,
  6384. dropEvt: dropEvt,
  6385. renames: renames,
  6386. hashes: hashes,
  6387. multiupload: true,
  6388. overwrite: data.overwrite === 0? 0 : void 0,
  6389. clipdata: data.clipdata
  6390. }, void 0, target)
  6391. .fail(function(error) {
  6392. if (error && error === 'No such command') {
  6393. abort = true;
  6394. fm.error(['errUpload', 'errPerm']);
  6395. }
  6396. if (cid) {
  6397. failChunk[cid] = true;
  6398. }
  6399. })
  6400. .always(function(e) {
  6401. if (e && e.added) added = $.merge(added, e.added);
  6402. if (last <= ++done) {
  6403. fm.trigger('multiupload', {added: added});
  6404. notifyto && clearTimeout(notifyto);
  6405. if (checkNotify()) {
  6406. self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
  6407. }
  6408. }
  6409. if (files.length) {
  6410. multi(files, 1); // Next one
  6411. } else {
  6412. if (--cancelChk <= 1) {
  6413. if (cancelBtn) {
  6414. cancelToggle(false, hasChunk);
  6415. }
  6416. }
  6417. dfrd.resolve();
  6418. }
  6419. });
  6420. }
  6421. }
  6422. }
  6423. if (sfiles.length < 1 || abort) {
  6424. if (abort) {
  6425. notifyto && clearTimeout(notifyto);
  6426. if (cid) {
  6427. failChunk[cid] = true;
  6428. }
  6429. dfrd.reject();
  6430. } else {
  6431. dfrd.resolve();
  6432. self.uploads.xhrUploading = false;
  6433. }
  6434. }
  6435. },
  6436. check = function(){
  6437. if (!self.uploads.xhrUploading) {
  6438. self.uploads.xhrUploading = true;
  6439. multi(sfiles, multiMax); // Max connection: 3
  6440. } else {
  6441. setTimeout(check, 100);
  6442. }
  6443. },
  6444. reqId, err;
  6445. if (! dataChecked && (isDataType || data.type == 'files')) {
  6446. if (! (maxFileSize = fm.option('uploadMaxSize', target))) {
  6447. maxFileSize = 0;
  6448. }
  6449. for (i=0; i < files.length; i++) {
  6450. try {
  6451. blob = files[i];
  6452. blobSize = blob.size;
  6453. if (blobSlice === false) {
  6454. blobSlice = '';
  6455. if (self.api >= 2.1) {
  6456. if ('slice' in blob) {
  6457. blobSlice = 'slice';
  6458. } else if ('mozSlice' in blob) {
  6459. blobSlice = 'mozSlice';
  6460. } else if ('webkitSlice' in blob) {
  6461. blobSlice = 'webkitSlice';
  6462. }
  6463. }
  6464. }
  6465. } catch(e) {
  6466. cnt--;
  6467. total--;
  6468. continue;
  6469. }
  6470. // file size check
  6471. if ((maxFileSize && blobSize > maxFileSize) || (!blobSlice && fm.uplMaxSize && blobSize > fm.uplMaxSize)) {
  6472. triggerError(['errUploadFile', blob.name, 'errUploadFileSize'], blob, true);
  6473. cnt--;
  6474. total--;
  6475. continue;
  6476. }
  6477. // file mime check
  6478. if (blob.type && ! self.uploadMimeCheck(blob.type, target)) {
  6479. triggerError(['errUploadFile', blob.name, 'errUploadMime', '(' + blob.type + ')'], blob, true);
  6480. cnt--;
  6481. total--;
  6482. continue;
  6483. }
  6484. if (blobSlice && blobSize > BYTES_PER_CHUNK) {
  6485. start = 0;
  6486. end = BYTES_PER_CHUNK;
  6487. chunks = -1;
  6488. total = Math.floor((blobSize - 1) / BYTES_PER_CHUNK);
  6489. blobMtime = blob.lastModified? Math.round(blob.lastModified/1000) : 0;
  6490. blobName = data.clipdata? fm.date(fm.nonameDateFormat) + '.png' : blob.name;
  6491. totalSize += blobSize;
  6492. chunked[chunkID] = 0;
  6493. while(start < blobSize) {
  6494. chunk = blob[blobSlice](start, end);
  6495. chunk._chunk = blobName + '.' + (++chunks) + '_' + total + '.part';
  6496. chunk._cid = chunkID;
  6497. chunk._range = start + ',' + chunk.size + ',' + blobSize;
  6498. chunk._mtime = blobMtime;
  6499. chunked[chunkID]++;
  6500. if (size) {
  6501. c++;
  6502. }
  6503. if (typeof sfiles[c] == 'undefined') {
  6504. sfiles[c] = [];
  6505. if (isDataType) {
  6506. sfiles[c][0] = [];
  6507. sfiles[c][1] = [];
  6508. }
  6509. }
  6510. size = BYTES_PER_CHUNK;
  6511. fcnt = 1;
  6512. if (isDataType) {
  6513. sfiles[c][0].push(chunk);
  6514. sfiles[c][1].push(paths[i]);
  6515. } else {
  6516. sfiles[c].push(chunk);
  6517. }
  6518. start = end;
  6519. end = start + BYTES_PER_CHUNK;
  6520. }
  6521. if (chunk == null) {
  6522. triggerError(['errUploadFile', blob.name, 'errUploadFileSize'], blob, true);
  6523. cnt--;
  6524. total--;
  6525. } else {
  6526. total += chunks;
  6527. size = 0;
  6528. fcnt = 1;
  6529. c++;
  6530. }
  6531. continue;
  6532. }
  6533. if ((fm.uplMaxSize && size + blobSize > fm.uplMaxSize) || fcnt > fm.uplMaxFile) {
  6534. size = 0;
  6535. fcnt = 1;
  6536. c++;
  6537. }
  6538. if (typeof sfiles[c] == 'undefined') {
  6539. sfiles[c] = [];
  6540. if (isDataType) {
  6541. sfiles[c][0] = [];
  6542. sfiles[c][1] = [];
  6543. }
  6544. }
  6545. if (isDataType) {
  6546. sfiles[c][0].push(blob);
  6547. sfiles[c][1].push(paths[i]);
  6548. } else {
  6549. sfiles[c].push(blob);
  6550. }
  6551. size += blobSize;
  6552. totalSize += blobSize;
  6553. fcnt++;
  6554. }
  6555. if (errors.length) {
  6556. triggerError();
  6557. }
  6558. if (sfiles.length == 0) {
  6559. // no data
  6560. data.checked = true;
  6561. return false;
  6562. }
  6563. if (sfiles.length > 1) {
  6564. // multi upload
  6565. notifyto = startNotify(totalSize);
  6566. added = [];
  6567. done = 0;
  6568. last = sfiles.length;
  6569. failChunk = [];
  6570. check();
  6571. return true;
  6572. }
  6573. // single upload
  6574. if (isDataType) {
  6575. files = sfiles[0][0];
  6576. paths = sfiles[0][1];
  6577. } else {
  6578. files = sfiles[0];
  6579. }
  6580. }
  6581. if (!dataChecked) {
  6582. if (!fm.UA.Safari || !data.files) {
  6583. notifyto = startNotify(totalSize);
  6584. } else {
  6585. xhr._totalSize = totalSize;
  6586. }
  6587. }
  6588. dataChecked = true;
  6589. if (! files.length) {
  6590. dfrd.reject(['errUploadNoFiles']);
  6591. }
  6592. xhr.open('POST', self.uploadURL, true);
  6593. // set request headers
  6594. if (fm.customHeaders) {
  6595. $.each(fm.customHeaders, function(key) {
  6596. xhr.setRequestHeader(key, this);
  6597. });
  6598. }
  6599. // set xhrFields
  6600. if (fm.xhrFields) {
  6601. $.each(fm.xhrFields, function(key) {
  6602. if (key in xhr) {
  6603. xhr[key] = this;
  6604. }
  6605. });
  6606. }
  6607. if (self.api >= 2.1029) {
  6608. // request ID
  6609. reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16);
  6610. formData.append('reqid', reqId);
  6611. xhr._requestId = reqId;
  6612. }
  6613. formData.append('cmd', 'upload');
  6614. formData.append(self.newAPI ? 'target' : 'current', target);
  6615. if (renames && renames.length) {
  6616. $.each(renames, function(i, v) {
  6617. formData.append('renames[]', v);
  6618. });
  6619. formData.append('suffix', fm.options.backupSuffix);
  6620. }
  6621. if (hashes) {
  6622. $.each(hashes, function(i, v) {
  6623. formData.append('hashes['+ i +']', v);
  6624. });
  6625. }
  6626. $.each(self.customData, function(key, val) {
  6627. formData.append(key, val);
  6628. });
  6629. $.each(self.options.onlyMimes, function(i, mime) {
  6630. formData.append('mimes[]', mime);
  6631. });
  6632. $.each(files, function(i, file) {
  6633. var name, relpath;
  6634. if (file._chunkmerged) {
  6635. formData.append('chunk', file._chunkmerged);
  6636. formData.append('upload[]', file._name);
  6637. formData.append('mtime[]', file._mtime);
  6638. data.clipdata && formData.append('overwrite', 0);
  6639. isChunked = true;
  6640. } else {
  6641. if (file._chunkfail) {
  6642. formData.append('upload[]', 'chunkfail');
  6643. formData.append('mimes', 'chunkfail');
  6644. } else {
  6645. if (data.clipdata) {
  6646. if (!file._chunk) {
  6647. data.overwrite = 0;
  6648. name = fm.date(fm.nonameDateFormat) + '.png';
  6649. }
  6650. } else {
  6651. if (file.name) {
  6652. name = file.name;
  6653. if (fm.UA.iOS) {
  6654. if (name.match(/^image\.jpe?g$/i)) {
  6655. data.overwrite = 0;
  6656. name = fm.date(fm.nonameDateFormat) + '.jpg';
  6657. } else if (name.match(/^capturedvideo\.mov$/i)) {
  6658. data.overwrite = 0;
  6659. name = fm.date(fm.nonameDateFormat) + '.mov';
  6660. }
  6661. }
  6662. relpath = (file.webkitRelativePath || file.relativePath || file._relativePath || '').replace(/[^\/]+$/, '');
  6663. name = relpath + name;
  6664. }
  6665. }
  6666. name? formData.append('upload[]', file, name) : formData.append('upload[]', file);
  6667. }
  6668. if (file._chunk) {
  6669. formData.append('chunk', file._chunk);
  6670. formData.append('cid' , file._cid);
  6671. formData.append('range', file._range);
  6672. formData.append('mtime[]', file._mtime);
  6673. isChunked = true;
  6674. } else {
  6675. formData.append('mtime[]', file.lastModified? Math.round(file.lastModified/1000) : 0);
  6676. }
  6677. }
  6678. });
  6679. if (isDataType) {
  6680. $.each(paths, function(i, path) {
  6681. formData.append('upload_path[]', path);
  6682. });
  6683. }
  6684. if (data.overwrite === 0) {
  6685. formData.append('overwrite', 0);
  6686. }
  6687. // send int value that which meta key was pressed when dropped as `dropWith`
  6688. if (dropEvt) {
  6689. formData.append('dropWith', parseInt(
  6690. (dropEvt.altKey ? '1' : '0')+
  6691. (dropEvt.ctrlKey ? '1' : '0')+
  6692. (dropEvt.metaKey ? '1' : '0')+
  6693. (dropEvt.shiftKey? '1' : '0'), 2));
  6694. }
  6695. // set extraData on current request
  6696. if (extraData) {
  6697. $.each(extraData, function(key, val) {
  6698. formData.append(key, val);
  6699. });
  6700. }
  6701. xhr.send(formData);
  6702. return true;
  6703. };
  6704. if (! isDataType) {
  6705. if (files.length > 0) {
  6706. if (! data.clipdata && renames == null) {
  6707. var mkdirs = [],
  6708. paths = [],
  6709. excludes = fm.options.folderUploadExclude[fm.OS] || null;
  6710. $.each(files, function(i, file) {
  6711. var relPath = file.webkitRelativePath || file.relativePath || '',
  6712. idx, rootDir;
  6713. if (! relPath) {
  6714. return false;
  6715. }
  6716. if (excludes && file.name.match(excludes)) {
  6717. file._remove = true;
  6718. relPath = void(0);
  6719. } else {
  6720. // add '/' as prefix to make same to folder uploading with DnD, see #2607
  6721. relPath = '/' + relPath.replace(/\/[^\/]*$/, '').replace(/^\//, '');
  6722. if (relPath && $.inArray(relPath, mkdirs) === -1) {
  6723. mkdirs.push(relPath);
  6724. // checking the root directory to supports <input type="file" webkitdirectory> see #2378
  6725. idx = relPath.substr(1).indexOf('/');
  6726. if (idx !== -1 && (rootDir = relPath.substr(0, idx + 1)) && $.inArray(rootDir, mkdirs) === -1) {
  6727. mkdirs.unshift(rootDir);
  6728. }
  6729. }
  6730. }
  6731. paths.push(relPath);
  6732. });
  6733. renames = [];
  6734. hashes = {};
  6735. if (mkdirs.length) {
  6736. (function() {
  6737. var checkDirs = $.map(mkdirs, function(name) { return name.substr(1).indexOf('/') === -1 ? {name: name.substr(1)} : null;}),
  6738. cancelDirs = [];
  6739. fm.uploads.checkExists(checkDirs, target, fm, true).done(
  6740. function(res, res2) {
  6741. var dfds = [], dfd, bak, hash;
  6742. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  6743. cancelDirs = $.map(checkDirs, function(dir) { return dir._remove? dir.name : null ;} );
  6744. checkDirs = $.grep(checkDirs, function(dir) { return !dir._remove? true : false ;} );
  6745. }
  6746. if (cancelDirs.length) {
  6747. $.each(paths.concat(), function(i, path) {
  6748. if ($.inArray(path, cancelDirs) === 0) {
  6749. files[i]._remove = true;
  6750. paths[i] = void(0);
  6751. }
  6752. });
  6753. }
  6754. files = $.grep(files, function(file) { return file._remove? false : true; });
  6755. paths = $.grep(paths, function(path) { return path === void 0 ? false : true; });
  6756. if (checkDirs.length) {
  6757. dfd = $.Deferred();
  6758. if (res.length) {
  6759. $.each(res, function(i, existName) {
  6760. // backup
  6761. bak = fm.uniqueName(existName + fm.options.backupSuffix , null, '');
  6762. $.each(res2, function(h, name) {
  6763. if (res[0] == name) {
  6764. hash = h;
  6765. return false;
  6766. }
  6767. });
  6768. if (! hash) {
  6769. hash = fm.fileByName(res[0], target).hash;
  6770. }
  6771. fm.lockfiles({files : [hash]});
  6772. dfds.push(
  6773. fm.request({
  6774. data : {cmd : 'rename', target : hash, name : bak},
  6775. notify : {type : 'rename', cnt : 1}
  6776. })
  6777. .fail(function(error) {
  6778. dfrd.reject(error);
  6779. fm.sync();
  6780. })
  6781. .always(function() {
  6782. fm.unlockfiles({files : [hash]});
  6783. })
  6784. );
  6785. });
  6786. } else {
  6787. dfds.push(null);
  6788. }
  6789. $.when.apply($, dfds).done(function() {
  6790. // ensure directories
  6791. fm.request({
  6792. data : {cmd : 'mkdir', target : target, dirs : mkdirs},
  6793. notify : {type : 'mkdir', cnt : mkdirs.length},
  6794. preventFail: true
  6795. })
  6796. .fail(function(error) {
  6797. error = error || ['errUnknown'];
  6798. if (error[0] === 'errCmdParams') {
  6799. multiMax = 1;
  6800. } else {
  6801. multiMax = 0;
  6802. dfrd.reject(error);
  6803. }
  6804. })
  6805. .done(function(data) {
  6806. var rm = false;
  6807. if (!data.hashes) {
  6808. data.hashes = {};
  6809. }
  6810. paths = $.map(paths.concat(), function(p, i) {
  6811. if (p === '/') {
  6812. return target;
  6813. } else {
  6814. if (data.hashes[p]) {
  6815. return data.hashes[p];
  6816. } else {
  6817. rm = true;
  6818. files[i]._remove = true;
  6819. return null;
  6820. }
  6821. }
  6822. });
  6823. if (rm) {
  6824. files = $.grep(files, function(file) { return file._remove? false : true; });
  6825. }
  6826. })
  6827. .always(function(data) {
  6828. if (multiMax) {
  6829. isDataType = true;
  6830. if (! send(files, paths)) {
  6831. dfrd.reject();
  6832. }
  6833. }
  6834. });
  6835. });
  6836. } else {
  6837. dfrd.reject();
  6838. }
  6839. }
  6840. );
  6841. })();
  6842. } else {
  6843. fm.uploads.checkExists(files, target, fm).done(
  6844. function(res, res2){
  6845. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  6846. hashes = res2;
  6847. if (res === null) {
  6848. data.overwrite = 0;
  6849. } else {
  6850. renames = res;
  6851. }
  6852. files = $.grep(files, function(file){return !file._remove? true : false ;});
  6853. }
  6854. cnt = files.length;
  6855. if (cnt > 0) {
  6856. if (! send(files)) {
  6857. dfrd.reject();
  6858. }
  6859. } else {
  6860. dfrd.reject();
  6861. }
  6862. }
  6863. );
  6864. }
  6865. } else {
  6866. if (! send(files)) {
  6867. dfrd.reject();
  6868. }
  6869. }
  6870. } else {
  6871. dfrd.reject();
  6872. }
  6873. } else {
  6874. if (dataChecked) {
  6875. send(files[0], files[1]);
  6876. } else {
  6877. files.done(function(result) { // result: [files, paths, renames, hashes, mkdirs]
  6878. renames = [];
  6879. cnt = result[0].length;
  6880. if (cnt) {
  6881. if (result[4] && result[4].length) {
  6882. // ensure directories
  6883. fm.request({
  6884. data : {cmd : 'mkdir', target : target, dirs : result[4]},
  6885. notify : {type : 'mkdir', cnt : result[4].length},
  6886. preventFail: true
  6887. })
  6888. .fail(function(error) {
  6889. error = error || ['errUnknown'];
  6890. if (error[0] === 'errCmdParams') {
  6891. multiMax = 1;
  6892. } else {
  6893. multiMax = 0;
  6894. dfrd.reject(error);
  6895. }
  6896. })
  6897. .done(function(data) {
  6898. var rm = false;
  6899. if (!data.hashes) {
  6900. data.hashes = {};
  6901. }
  6902. result[1] = $.map(result[1], function(p, i) {
  6903. result[0][i]._relativePath = p.replace(/^\//, '');
  6904. p = p.replace(/\/[^\/]*$/, '');
  6905. if (p === '') {
  6906. return target;
  6907. } else {
  6908. if (data.hashes[p]) {
  6909. return data.hashes[p];
  6910. } else {
  6911. rm = true;
  6912. result[0][i]._remove = true;
  6913. return null;
  6914. }
  6915. }
  6916. });
  6917. if (rm) {
  6918. result[0] = $.grep(result[0], function(file) { return file._remove? false : true; });
  6919. }
  6920. })
  6921. .always(function(data) {
  6922. if (multiMax) {
  6923. renames = result[2];
  6924. hashes = result[3];
  6925. send(result[0], result[1]);
  6926. }
  6927. });
  6928. return;
  6929. } else {
  6930. result[1] = $.map(result[1], function() { return target; });
  6931. }
  6932. renames = result[2];
  6933. hashes = result[3];
  6934. send(result[0], result[1]);
  6935. } else {
  6936. dfrd.reject(['errUploadNoFiles']);
  6937. }
  6938. }).fail(function(){
  6939. dfrd.reject();
  6940. });
  6941. }
  6942. }
  6943. return dfrd;
  6944. },
  6945. // upload transport using iframe
  6946. iframe : function(data, fm) {
  6947. var self = fm ? fm : this,
  6948. input = data.input? data.input : false,
  6949. files = !input ? self.uploads.checkFile(data, self) : false,
  6950. dfrd = $.Deferred()
  6951. .fail(function(error) {
  6952. error && self.error(error);
  6953. }),
  6954. name = 'iframe-'+fm.namespace+(++self.iframeCnt),
  6955. form = $('<form action="'+self.uploadURL+'" method="post" enctype="multipart/form-data" encoding="multipart/form-data" target="'+name+'" style="display:none"><input type="hidden" name="cmd" value="upload" /></form>'),
  6956. msie = this.UA.IE,
  6957. // clear timeouts, close notification dialog, remove form/iframe
  6958. onload = function() {
  6959. abortto && clearTimeout(abortto);
  6960. notifyto && clearTimeout(notifyto);
  6961. notify && self.notify({type : 'upload', cnt : -cnt});
  6962. setTimeout(function() {
  6963. msie && $('<iframe src="javascript:false;"></iframe>').appendTo(form);
  6964. form.remove();
  6965. iframe.remove();
  6966. }, 100);
  6967. },
  6968. iframe = $('<iframe src="'+(msie ? 'javascript:false;' : 'about:blank')+'" name="'+name+'" style="position:absolute;left:-1000px;top:-1000px" ></iframe>')
  6969. .on('load', function() {
  6970. iframe.off('load')
  6971. .on('load', function() {
  6972. onload();
  6973. // data will be processed in callback response or window onmessage
  6974. dfrd.resolve();
  6975. });
  6976. // notify dialog
  6977. notifyto = setTimeout(function() {
  6978. notify = true;
  6979. self.notify({type : 'upload', cnt : cnt});
  6980. }, self.options.notifyDelay);
  6981. // emulate abort on timeout
  6982. if (self.options.iframeTimeout > 0) {
  6983. abortto = setTimeout(function() {
  6984. onload();
  6985. dfrd.reject(['errConnect', 'errTimeout']);
  6986. }, self.options.iframeTimeout);
  6987. }
  6988. form.submit();
  6989. }),
  6990. target = (data.target || self.cwd().hash),
  6991. names = [],
  6992. dfds = [],
  6993. renames = [],
  6994. hashes = {},
  6995. cnt, notify, notifyto, abortto;
  6996. if (files && files.length) {
  6997. $.each(files, function(i, val) {
  6998. form.append('<input type="hidden" name="upload[]" value="'+val+'"/>');
  6999. });
  7000. cnt = 1;
  7001. } else if (input && $(input).is(':file') && $(input).val()) {
  7002. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  7003. names = input.files? input.files : [{ name: $(input).val().replace(/^(?:.+[\\\/])?([^\\\/]+)$/, '$1') }];
  7004. //names = $.map(names, function(file){return file.name? { name: file.name } : null ;});
  7005. dfds.push(self.uploads.checkExists(names, target, self).done(
  7006. function(res, res2){
  7007. hashes = res2;
  7008. if (res === null) {
  7009. data.overwrite = 0;
  7010. } else{
  7011. renames = res;
  7012. cnt = $.grep(names, function(file){return !file._remove? true : false ;}).length;
  7013. if (cnt != names.length) {
  7014. cnt = 0;
  7015. }
  7016. }
  7017. }
  7018. ));
  7019. }
  7020. cnt = input.files ? input.files.length : 1;
  7021. form.append(input);
  7022. } else {
  7023. return dfrd.reject();
  7024. }
  7025. $.when.apply($, dfds).done(function() {
  7026. if (cnt < 1) {
  7027. return dfrd.reject();
  7028. }
  7029. form.append('<input type="hidden" name="'+(self.newAPI ? 'target' : 'current')+'" value="'+target+'"/>')
  7030. .append('<input type="hidden" name="html" value="1"/>')
  7031. .append('<input type="hidden" name="node" value="'+self.id+'"/>')
  7032. .append($(input).attr('name', 'upload[]'));
  7033. if (renames.length > 0) {
  7034. $.each(renames, function(i, rename) {
  7035. form.append('<input type="hidden" name="renames[]" value="'+self.escape(rename)+'"/>');
  7036. });
  7037. form.append('<input type="hidden" name="suffix" value="'+fm.options.backupSuffix+'"/>');
  7038. }
  7039. if (hashes) {
  7040. $.each(renames, function(i, v) {
  7041. form.append('<input type="hidden" name="['+i+']" value="'+self.escape(v)+'"/>');
  7042. });
  7043. }
  7044. if (data.overwrite === 0) {
  7045. form.append('<input type="hidden" name="overwrite" value="0"/>');
  7046. }
  7047. $.each(self.options.onlyMimes||[], function(i, mime) {
  7048. form.append('<input type="hidden" name="mimes[]" value="'+self.escape(mime)+'"/>');
  7049. });
  7050. $.each(self.customData, function(key, val) {
  7051. form.append('<input type="hidden" name="'+key+'" value="'+self.escape(val)+'"/>');
  7052. });
  7053. form.appendTo('body');
  7054. iframe.appendTo('body');
  7055. });
  7056. return dfrd;
  7057. }
  7058. },
  7059. /**
  7060. * Bind callback to event(s) The callback is executed at most once per event.
  7061. * To bind to multiply events at once, separate events names by space
  7062. *
  7063. * @param String event name
  7064. * @param Function callback
  7065. * @param Boolan priority first
  7066. * @return elFinder
  7067. */
  7068. one : function(ev, callback, priorityFirst) {
  7069. var self = this,
  7070. event = ev.toLowerCase(),
  7071. h = function(e, f) {
  7072. if (!self.toUnbindEvents[event]) {
  7073. self.toUnbindEvents[event] = [];
  7074. }
  7075. self.toUnbindEvents[event].push({
  7076. type: event,
  7077. callback: h
  7078. });
  7079. return (callback.done? callback.done : callback).apply(this, arguments);
  7080. };
  7081. if (callback.done) {
  7082. h = {done: h};
  7083. }
  7084. return this.bind(event, h, priorityFirst);
  7085. },
  7086. /**
  7087. * Set/get data into/from localStorage
  7088. *
  7089. * @param String key
  7090. * @param String|void value
  7091. * @return String|null
  7092. */
  7093. localStorage : function(key, val) {
  7094. var self = this,
  7095. s = window.localStorage,
  7096. oldkey = 'elfinder-'+(key || '')+this.id, // old key of elFinder < 2.1.6
  7097. prefix = window.location.pathname+'-elfinder-',
  7098. suffix = this.id,
  7099. clrs = [],
  7100. retval, oldval, t, precnt, sufcnt;
  7101. // reset this node data
  7102. if (typeof(key) === 'undefined') {
  7103. precnt = prefix.length;
  7104. sufcnt = suffix.length * -1;
  7105. $.each(s, function(key) {
  7106. if (key.substr(0, precnt) === prefix && key.substr(sufcnt) === suffix) {
  7107. clrs.push(key);
  7108. }
  7109. });
  7110. $.each(clrs, function(i, key) {
  7111. s.removeItem(key);
  7112. });
  7113. return true;
  7114. }
  7115. // new key of elFinder >= 2.1.6
  7116. key = prefix+key+suffix;
  7117. if (val === null) {
  7118. return s.removeItem(key);
  7119. }
  7120. if (val === void(0) && !(retval = s.getItem(key)) && (oldval = s.getItem(oldkey))) {
  7121. val = oldval;
  7122. s.removeItem(oldkey);
  7123. }
  7124. if (val !== void(0)) {
  7125. t = typeof val;
  7126. if (t !== 'string' && t !== 'number') {
  7127. val = JSON.stringify(val);
  7128. }
  7129. try {
  7130. s.setItem(key, val);
  7131. } catch (e) {
  7132. try {
  7133. s.clear();
  7134. s.setItem(key, val);
  7135. } catch (e) {
  7136. self.debug('error', e.toString());
  7137. }
  7138. }
  7139. retval = s.getItem(key);
  7140. }
  7141. if (retval && (retval.substr(0,1) === '{' || retval.substr(0,1) === '[')) {
  7142. try {
  7143. return JSON.parse(retval);
  7144. } catch(e) {}
  7145. }
  7146. return retval;
  7147. },
  7148. /**
  7149. * Set/get data into/from sessionStorage
  7150. *
  7151. * @param String key
  7152. * @param String|void value
  7153. * @return String|null
  7154. */
  7155. sessionStorage : function(key, val) {
  7156. var self = this,
  7157. s, retval, t;
  7158. try {
  7159. s = window.sessionStorage;
  7160. } catch(e) {}
  7161. if (!s) {
  7162. return;
  7163. }
  7164. if (val === null) {
  7165. return s.removeItem(key);
  7166. }
  7167. if (val !== void(0)) {
  7168. t = typeof val;
  7169. if (t !== 'string' && t !== 'number') {
  7170. val = JSON.stringify(val);
  7171. }
  7172. try {
  7173. s.setItem(key, val);
  7174. } catch (e) {
  7175. try {
  7176. s.clear();
  7177. s.setItem(key, val);
  7178. } catch (e) {
  7179. self.debug('error', e.toString());
  7180. }
  7181. }
  7182. }
  7183. retval = s.getItem(key);
  7184. if (retval && (retval.substr(0,1) === '{' || retval.substr(0,1) === '[')) {
  7185. try {
  7186. return JSON.parse(retval);
  7187. } catch(e) {}
  7188. }
  7189. return retval;
  7190. },
  7191. /**
  7192. * Get/set cookie
  7193. *
  7194. * @param String cookie name
  7195. * @param String|void cookie value
  7196. * @return String|null
  7197. */
  7198. cookie : function(name, value) {
  7199. var d, o, c, i, retval, t;
  7200. name = 'elfinder-'+name+this.id;
  7201. if (value === void(0)) {
  7202. if (this.cookieEnabled && document.cookie && document.cookie != '') {
  7203. c = document.cookie.split(';');
  7204. name += '=';
  7205. for (i=0; i<c.length; i++) {
  7206. c[i] = $.trim(c[i]);
  7207. if (c[i].substring(0, name.length) == name) {
  7208. retval = decodeURIComponent(c[i].substring(name.length));
  7209. if (retval.substr(0,1) === '{' || retval.substr(0,1) === '[') {
  7210. try {
  7211. return JSON.parse(retval);
  7212. } catch(e) {}
  7213. }
  7214. return retval;
  7215. }
  7216. }
  7217. }
  7218. return null;
  7219. }
  7220. if (!this.cookieEnabled) {
  7221. return '';
  7222. }
  7223. o = Object.assign({}, this.options.cookie);
  7224. if (value === null) {
  7225. value = '';
  7226. o.expires = -1;
  7227. } else {
  7228. t = typeof value;
  7229. if (t !== 'string' && t !== 'number') {
  7230. value = JSON.stringify(value);
  7231. }
  7232. }
  7233. if (typeof(o.expires) == 'number') {
  7234. d = new Date();
  7235. d.setTime(d.getTime()+(o.expires * 86400000));
  7236. o.expires = d;
  7237. }
  7238. document.cookie = name+'='+encodeURIComponent(value)+'; expires='+o.expires.toUTCString()+(o.path ? '; path='+o.path : '')+(o.domain ? '; domain='+o.domain : '')+(o.secure ? '; secure' : '');
  7239. if (value && (value.substr(0,1) === '{' || value.substr(0,1) === '[')) {
  7240. try {
  7241. return JSON.parse(value);
  7242. } catch(e) {}
  7243. }
  7244. return value;
  7245. },
  7246. /**
  7247. * Get start directory (by location.hash or last opened directory)
  7248. *
  7249. * @return String
  7250. */
  7251. startDir : function() {
  7252. var locHash = window.location.hash;
  7253. if (locHash && locHash.match(/^#elf_/)) {
  7254. return locHash.replace(/^#elf_/, '');
  7255. } else if (this.options.startPathHash) {
  7256. return this.options.startPathHash;
  7257. } else {
  7258. return this.lastDir();
  7259. }
  7260. },
  7261. /**
  7262. * Get/set last opened directory
  7263. *
  7264. * @param String|undefined dir hash
  7265. * @return String
  7266. */
  7267. lastDir : function(hash) {
  7268. return this.options.rememberLastDir ? this.storage('lastdir', hash) : '';
  7269. },
  7270. /**
  7271. * Node for escape html entities in texts
  7272. *
  7273. * @type jQuery
  7274. */
  7275. _node : $('<span></c.length;>'),
  7276. /**
  7277. * Replace not html-safe symbols to html entities
  7278. *
  7279. * @param String text to escape
  7280. * @return String
  7281. */
  7282. escape : function(name) {
  7283. return this._node.text(name).html().replace(/"/g, '&quot;').replace(/'/g, '&#039;');
  7284. },
  7285. /**
  7286. * Cleanup ajax data.
  7287. * For old api convert data into new api format
  7288. *
  7289. * @param String command name
  7290. * @param Object data from backend
  7291. * @return Object
  7292. */
  7293. normalize : function(data) {
  7294. var self = this,
  7295. fileFilter = (function() {
  7296. var func, filter;
  7297. if (filter = self.options.fileFilter) {
  7298. if (typeof filter === 'function') {
  7299. func = function(file) {
  7300. return filter.call(self, file);
  7301. };
  7302. } else if (filter instanceof RegExp) {
  7303. func = function(file) {
  7304. return filter.test(file.name);
  7305. };
  7306. }
  7307. }
  7308. return func? func : null;
  7309. })(),
  7310. chkCmdMap = function(opts) {
  7311. // Disable command to replace with other command
  7312. var disabled;
  7313. if (opts.uiCmdMap) {
  7314. if ($.isPlainObject(opts.uiCmdMap) && Object.keys(opts.uiCmdMap).length) {
  7315. if (!opts.disabledFlip) {
  7316. opts.disabledFlip = {};
  7317. }
  7318. disabled = opts.disabledFlip;
  7319. $.each(opts.uiCmdMap, function(f, t) {
  7320. if (t === 'hidden' && !disabled[f]) {
  7321. opts.disabled.push(f);
  7322. opts.disabledFlip[f] = true;
  7323. }
  7324. });
  7325. } else {
  7326. delete opts.uiCmdMap;
  7327. }
  7328. }
  7329. },
  7330. normalizeOptions = function(opts) {
  7331. var getType = function(v) {
  7332. var type = typeof v;
  7333. if (type === 'object' && Array.isArray(v)) {
  7334. type = 'array';
  7335. }
  7336. return type;
  7337. };
  7338. $.each(self.optionProperties, function(k, empty) {
  7339. if (empty !== void(0)) {
  7340. if (opts[k] && getType(opts[k]) !== getType(empty)) {
  7341. opts[k] = empty;
  7342. }
  7343. }
  7344. });
  7345. if (opts.disabled) {
  7346. opts.disabledFlip = self.arrayFlip(opts.disabled, true);
  7347. $.each(self.options.disabledCmdsRels, function(com, rels) {
  7348. var m, flg;
  7349. if (opts.disabledFlip[com]) {
  7350. flg = true;
  7351. } else if (m = com.match(/^([^&]+)&([^=]+)=(.*)$/)) {
  7352. if (opts.disabledFlip[m[1]] && opts[m[2]] == m[3]) {
  7353. flg = true;
  7354. }
  7355. }
  7356. if (flg) {
  7357. $.each(rels, function(i, rel) {
  7358. if (!opts.disabledFlip[rel]) {
  7359. opts.disabledFlip[rel] = true;
  7360. opts.disabled.push(rel);
  7361. }
  7362. });
  7363. }
  7364. });
  7365. } else {
  7366. opts.disabledFlip = {};
  7367. }
  7368. return opts;
  7369. },
  7370. filter = function(file, asMap, type) {
  7371. var res = asMap? file : true,
  7372. ign = asMap? null : false,
  7373. vid, targetOptions, isRoot, rootNames;
  7374. if (file && file.hash && file.name && file.mime) {
  7375. if (file.mime === 'application/x-empty') {
  7376. file.mime = 'text/plain';
  7377. }
  7378. isRoot = self.isRoot(file);
  7379. if (isRoot && ! file.volumeid) {
  7380. self.debug('warning', 'The volume root statuses requires `volumeid` property.');
  7381. }
  7382. if (isRoot || file.mime === 'directory') {
  7383. // Prevention of circular reference
  7384. if (file.phash) {
  7385. if (file.phash === file.hash) {
  7386. error = error.concat(['Parent folder of "$1" is itself.', file.name]);
  7387. return ign;
  7388. }
  7389. if (isRoot && file.volumeid && file.phash.indexOf(file.volumeid) === 0) {
  7390. error = error.concat(['Parent folder of "$1" is inner itself.', file.name]);
  7391. return ign;
  7392. }
  7393. }
  7394. // set options, tmbUrls for each volume
  7395. if (file.volumeid) {
  7396. vid = file.volumeid;
  7397. if (isRoot) {
  7398. // make or update of leaf roots cache
  7399. if (file.phash) {
  7400. if (! self.leafRoots[file.phash]) {
  7401. self.leafRoots[file.phash] = [ file.hash ];
  7402. } else {
  7403. if ($.inArray(file.hash, self.leafRoots[file.phash]) === -1) {
  7404. self.leafRoots[file.phash].push(file.hash);
  7405. }
  7406. }
  7407. }
  7408. self.hasVolOptions = true;
  7409. if (! self.volOptions[vid]) {
  7410. self.volOptions[vid] = {
  7411. // set dispInlineRegex
  7412. dispInlineRegex: self.options.dispInlineRegex
  7413. };
  7414. }
  7415. targetOptions = self.volOptions[vid];
  7416. if (file.options) {
  7417. // >= v.2.1.14 has file.options
  7418. Object.assign(targetOptions, file.options);
  7419. }
  7420. // for compat <= v2.1.13
  7421. if (file.disabled) {
  7422. targetOptions.disabled = file.disabled;
  7423. targetOptions.disabledFlip = self.arrayFlip(file.disabled, true);
  7424. }
  7425. if (file.tmbUrl) {
  7426. targetOptions.tmbUrl = file.tmbUrl;
  7427. }
  7428. // '/' required at the end of url
  7429. if (targetOptions.url && targetOptions.url.substr(-1) !== '/') {
  7430. targetOptions.url += '/';
  7431. }
  7432. // check uiCmdMap
  7433. chkCmdMap(targetOptions);
  7434. // check trash bin hash
  7435. if (targetOptions.trashHash) {
  7436. if (self.trashes[targetOptions.trashHash] === false) {
  7437. delete targetOptions.trashHash;
  7438. } else {
  7439. self.trashes[targetOptions.trashHash] = file.hash;
  7440. }
  7441. }
  7442. // set immediate properties
  7443. $.each(self.optionProperties, function(k) {
  7444. if (targetOptions[k]) {
  7445. file[k] = targetOptions[k];
  7446. }
  7447. });
  7448. // regist fm.roots
  7449. if (type !== 'cwd') {
  7450. self.roots[vid] = file.hash;
  7451. }
  7452. // regist fm.volumeExpires
  7453. if (file.expires) {
  7454. self.volumeExpires[vid] = file.expires;
  7455. }
  7456. }
  7457. if (prevId !== vid) {
  7458. prevId = vid;
  7459. i18nFolderName = self.option('i18nFolderName', vid);
  7460. }
  7461. }
  7462. // volume root i18n name
  7463. if (isRoot && ! file.i18) {
  7464. name = 'volume_' + file.name,
  7465. i18 = self.i18n(false, name);
  7466. if (name !== i18) {
  7467. file.i18 = i18;
  7468. }
  7469. }
  7470. // i18nFolderName
  7471. if (i18nFolderName && ! file.i18) {
  7472. name = 'folder_' + file.name,
  7473. i18 = self.i18n(false, name);
  7474. if (name !== i18) {
  7475. file.i18 = i18;
  7476. }
  7477. }
  7478. if (isRoot) {
  7479. if (rootNames = self.storage('rootNames')) {
  7480. if (rootNames[file.hash]) {
  7481. file._name = file.name;
  7482. file._i18 = file.i18;
  7483. file.name = rootNames[file.hash] = rootNames[file.hash];
  7484. delete file.i18;
  7485. }
  7486. self.storage('rootNames', rootNames);
  7487. }
  7488. }
  7489. // lock trash bins holder
  7490. if (self.trashes[file.hash]) {
  7491. file.locked = true;
  7492. }
  7493. } else {
  7494. if (fileFilter) {
  7495. try {
  7496. if (! fileFilter(file)) {
  7497. return ign;
  7498. }
  7499. } catch(e) {
  7500. self.debug(e);
  7501. }
  7502. }
  7503. if (file.size == 0) {
  7504. file.mime = self.getMimetype(file.name, file.mime);
  7505. }
  7506. }
  7507. if (file.options) {
  7508. self.optionsByHashes[file.hash] = normalizeOptions(file.options);
  7509. }
  7510. delete file.options;
  7511. return res;
  7512. }
  7513. return ign;
  7514. },
  7515. getDescendants = function(hashes) {
  7516. var res = [];
  7517. $.each(self.files(), function(h, f) {
  7518. $.each(self.parents(h), function(i, ph) {
  7519. if ($.inArray(ph, hashes) !== -1 && $.inArray(h, hashes) === -1) {
  7520. res.push(h);
  7521. return false;
  7522. }
  7523. });
  7524. });
  7525. return res;
  7526. },
  7527. applyLeafRootStats = function(dataArr, type) {
  7528. $.each(dataArr, function(i, f) {
  7529. var pfile, done;
  7530. if (self.leafRoots[f.hash]) {
  7531. self.applyLeafRootStats(f);
  7532. }
  7533. // update leaf root parent stat
  7534. if (type !== 'change' && f.phash && self.isRoot(f) && (pfile = self.file(f.phash))) {
  7535. self.applyLeafRootStats(pfile);
  7536. // add to data.changed
  7537. if (!data.changed) {
  7538. data.changed = [pfile];
  7539. } else {
  7540. $.each(data.changed, function(i, f) {
  7541. if (f.hash === pfile.hash) {
  7542. data.changed[i] = pfile;
  7543. done = true;
  7544. return false;
  7545. }
  7546. });
  7547. if (!done) {
  7548. data.changed.push(pfile);
  7549. }
  7550. }
  7551. }
  7552. });
  7553. },
  7554. error = [],
  7555. name, i18, i18nFolderName, prevId, cData;
  7556. // set cunstom data
  7557. if (data.customData && (!self.prevCustomData || (JSON.stringify(data.customData) !== JSON.stringify(self.prevCustomData)))) {
  7558. self.prevCustomData = data.customData;
  7559. try {
  7560. cData = JSON.parse(data.customData);
  7561. if ($.isPlainObject(cData)) {
  7562. self.prevCustomData = cData;
  7563. $.each(Object.keys(cData), function(i, key) {
  7564. if (cData[key] === null) {
  7565. delete cData[key];
  7566. delete self.optsCustomData[key];
  7567. }
  7568. });
  7569. self.customData = Object.assign({}, self.optsCustomData, cData);
  7570. }
  7571. } catch(e) {}
  7572. }
  7573. if (data.options) {
  7574. normalizeOptions(data.options);
  7575. }
  7576. if (data.cwd) {
  7577. if (data.cwd.volumeid && data.options && Object.keys(data.options).length && self.isRoot(data.cwd)) {
  7578. self.hasVolOptions = true;
  7579. self.volOptions[data.cwd.volumeid] = data.options;
  7580. }
  7581. data.cwd = filter(data.cwd, true, 'cwd');
  7582. }
  7583. if (data.files) {
  7584. data.files = $.grep(data.files, filter);
  7585. }
  7586. if (data.tree) {
  7587. data.tree = $.grep(data.tree, filter);
  7588. }
  7589. if (data.added) {
  7590. data.added = $.grep(data.added, filter);
  7591. }
  7592. if (data.changed) {
  7593. data.changed = $.grep(data.changed, filter);
  7594. }
  7595. if (data.removed && data.removed.length && self.searchStatus.state === 2) {
  7596. data.removed = data.removed.concat(getDescendants(data.removed));
  7597. }
  7598. if (data.api) {
  7599. data.init = true;
  7600. }
  7601. if (Object.keys(self.leafRoots).length) {
  7602. data.files && applyLeafRootStats(data.files);
  7603. data.tree && applyLeafRootStats(data.tree);
  7604. data.added && applyLeafRootStats(data.added);
  7605. data.changed && applyLeafRootStats(data.changed, 'change');
  7606. }
  7607. // merge options that apply only to cwd
  7608. if (data.cwd && data.cwd.options && data.options) {
  7609. Object.assign(data.options, normalizeOptions(data.cwd.options));
  7610. }
  7611. // '/' required at the end of url
  7612. if (data.options && data.options.url && data.options.url.substr(-1) !== '/') {
  7613. data.options.url += '/';
  7614. }
  7615. // check error
  7616. if (error.length) {
  7617. data.norError = ['errResponse'].concat(error);
  7618. }
  7619. return data;
  7620. },
  7621. /**
  7622. * Update sort options
  7623. *
  7624. * @param {String} sort type
  7625. * @param {String} sort order
  7626. * @param {Boolean} show folder first
  7627. */
  7628. setSort : function(type, order, stickFolders, alsoTreeview) {
  7629. this.storage('sortType', (this.sortType = this.sortRules[type] ? type : 'name'));
  7630. this.storage('sortOrder', (this.sortOrder = /asc|desc/.test(order) ? order : 'asc'));
  7631. this.storage('sortStickFolders', (this.sortStickFolders = !!stickFolders) ? 1 : '');
  7632. this.storage('sortAlsoTreeview', (this.sortAlsoTreeview = !!alsoTreeview) ? 1 : '');
  7633. this.trigger('sortchange');
  7634. },
  7635. _sortRules : {
  7636. name : function(file1, file2) {
  7637. return elFinder.prototype.naturalCompare(file1.i18 || file1.name, file2.i18 || file2.name);
  7638. },
  7639. size : function(file1, file2) {
  7640. var size1 = parseInt(file1.size) || 0,
  7641. size2 = parseInt(file2.size) || 0;
  7642. return size1 === size2 ? 0 : size1 > size2 ? 1 : -1;
  7643. },
  7644. kind : function(file1, file2) {
  7645. return elFinder.prototype.naturalCompare(file1.mime, file2.mime);
  7646. },
  7647. date : function(file1, file2) {
  7648. var date1 = file1.ts || file1.date || 0,
  7649. date2 = file2.ts || file2.date || 0;
  7650. return date1 === date2 ? 0 : date1 > date2 ? 1 : -1;
  7651. },
  7652. perm : function(file1, file2) {
  7653. var val = function(file) { return (file.write? 2 : 0) + (file.read? 1 : 0); },
  7654. v1 = val(file1),
  7655. v2 = val(file2);
  7656. return v1 === v2 ? 0 : v1 > v2 ? 1 : -1;
  7657. },
  7658. mode : function(file1, file2) {
  7659. var v1 = file1.mode || (file1.perm || ''),
  7660. v2 = file2.mode || (file2.perm || '');
  7661. return elFinder.prototype.naturalCompare(v1, v2);
  7662. },
  7663. owner : function(file1, file2) {
  7664. var v1 = file1.owner || '',
  7665. v2 = file2.owner || '';
  7666. return elFinder.prototype.naturalCompare(v1, v2);
  7667. },
  7668. group : function(file1, file2) {
  7669. var v1 = file1.group || '',
  7670. v2 = file2.group || '';
  7671. return elFinder.prototype.naturalCompare(v1, v2);
  7672. }
  7673. },
  7674. /**
  7675. * Valid sort rule names
  7676. *
  7677. * @type Object
  7678. */
  7679. sorters : {},
  7680. /**
  7681. * Compare strings for natural sort
  7682. *
  7683. * @param String
  7684. * @param String
  7685. * @return Number
  7686. */
  7687. naturalCompare : function(a, b) {
  7688. var self = elFinder.prototype.naturalCompare;
  7689. if (typeof self.loc == 'undefined') {
  7690. self.loc = (navigator.userLanguage || navigator.browserLanguage || navigator.language || 'en-US');
  7691. }
  7692. if (typeof self.sort == 'undefined') {
  7693. if ('11'.localeCompare('2', self.loc, {numeric: true}) > 0) {
  7694. // Native support
  7695. if (window.Intl && window.Intl.Collator) {
  7696. self.sort = new Intl.Collator(self.loc, {numeric: true}).compare;
  7697. } else {
  7698. self.sort = function(a, b) {
  7699. return a.localeCompare(b, self.loc, {numeric: true});
  7700. };
  7701. }
  7702. } else {
  7703. /*
  7704. * Edited for elFinder (emulates localeCompare() by numeric) by Naoki Sawada aka nao-pon
  7705. */
  7706. /*
  7707. * Huddle/javascript-natural-sort (https://github.com/Huddle/javascript-natural-sort)
  7708. */
  7709. /*
  7710. * Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license
  7711. * Author: Jim Palmer (based on chunking idea from Dave Koelle)
  7712. * http://opensource.org/licenses/mit-license.php
  7713. */
  7714. self.sort = function(a, b) {
  7715. var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi,
  7716. sre = /(^[ ]*|[ ]*$)/g,
  7717. dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
  7718. hre = /^0x[0-9a-f]+$/i,
  7719. ore = /^0/,
  7720. syre = /^[\x01\x21-\x2f\x3a-\x40\x5b-\x60\x7b-\x7e]/, // symbol first - (Naoki Sawada)
  7721. i = function(s) { return self.sort.insensitive && (''+s).toLowerCase() || ''+s; },
  7722. // convert all to strings strip whitespace
  7723. // first character is "_", it's smallest - (Naoki Sawada)
  7724. x = i(a).replace(sre, '').replace(/^_/, "\x01") || '',
  7725. y = i(b).replace(sre, '').replace(/^_/, "\x01") || '',
  7726. // chunk/tokenize
  7727. xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
  7728. yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
  7729. // numeric, hex or date detection
  7730. xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)),
  7731. yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null,
  7732. oFxNcL, oFyNcL,
  7733. locRes = 0;
  7734. // first try and sort Hex codes or Dates
  7735. if (yD) {
  7736. if ( xD < yD ) return -1;
  7737. else if ( xD > yD ) return 1;
  7738. }
  7739. // natural sorting through split numeric strings and default strings
  7740. for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
  7741. // find floats not starting with '0', string or 0 if not defined (Clint Priest)
  7742. oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
  7743. oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
  7744. // handle numeric vs string comparison - number < string - (Kyle Adams)
  7745. // but symbol first < number - (Naoki Sawada)
  7746. if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
  7747. if (isNaN(oFxNcL) && (typeof oFxNcL !== 'string' || ! oFxNcL.match(syre))) {
  7748. return 1;
  7749. } else if (typeof oFyNcL !== 'string' || ! oFyNcL.match(syre)) {
  7750. return -1;
  7751. }
  7752. }
  7753. // use decimal number comparison if either value is string zero
  7754. if (parseInt(oFxNcL, 10) === 0) oFxNcL = 0;
  7755. if (parseInt(oFyNcL, 10) === 0) oFyNcL = 0;
  7756. // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
  7757. if (typeof oFxNcL !== typeof oFyNcL) {
  7758. oFxNcL += '';
  7759. oFyNcL += '';
  7760. }
  7761. // use locale sensitive sort for strings when case insensitive
  7762. // note: localeCompare interleaves uppercase with lowercase (e.g. A,a,B,b)
  7763. if (self.sort.insensitive && typeof oFxNcL === 'string' && typeof oFyNcL === 'string') {
  7764. locRes = oFxNcL.localeCompare(oFyNcL, self.loc);
  7765. if (locRes !== 0) return locRes;
  7766. }
  7767. if (oFxNcL < oFyNcL) return -1;
  7768. if (oFxNcL > oFyNcL) return 1;
  7769. }
  7770. return 0;
  7771. };
  7772. self.sort.insensitive = true;
  7773. }
  7774. }
  7775. return self.sort(a, b);
  7776. },
  7777. /**
  7778. * Compare files based on elFinder.sort
  7779. *
  7780. * @param Object file
  7781. * @param Object file
  7782. * @return Number
  7783. */
  7784. compare : function(file1, file2) {
  7785. var self = this,
  7786. type = self.sortType,
  7787. asc = self.sortOrder == 'asc',
  7788. stick = self.sortStickFolders,
  7789. rules = self.sortRules,
  7790. sort = rules[type],
  7791. d1 = file1.mime == 'directory',
  7792. d2 = file2.mime == 'directory',
  7793. res;
  7794. if (stick) {
  7795. if (d1 && !d2) {
  7796. return -1;
  7797. } else if (!d1 && d2) {
  7798. return 1;
  7799. }
  7800. }
  7801. res = asc ? sort(file1, file2) : sort(file2, file1);
  7802. return type !== 'name' && res === 0
  7803. ? res = asc ? rules.name(file1, file2) : rules.name(file2, file1)
  7804. : res;
  7805. },
  7806. /**
  7807. * Sort files based on config
  7808. *
  7809. * @param Array files
  7810. * @return Array
  7811. */
  7812. sortFiles : function(files) {
  7813. return files.sort(this.compare);
  7814. },
  7815. /**
  7816. * Open notification dialog
  7817. * and append/update message for required notification type.
  7818. *
  7819. * @param Object options
  7820. * @example
  7821. * this.notify({
  7822. * type : 'copy',
  7823. * msg : 'Copy files', // not required for known types @see this.notifyType
  7824. * cnt : 3,
  7825. * hideCnt : false, // true for not show count
  7826. * progress : 10, // progress bar percents (use cnt : 0 to update progress bar)
  7827. * cancel : callback // callback function for cancel button
  7828. * })
  7829. * @return elFinder
  7830. */
  7831. notify : function(opts) {
  7832. var self = this,
  7833. type = opts.type,
  7834. id = opts.id? 'elfinder-notify-'+opts.id : '',
  7835. msg = this.i18n((typeof opts.msg !== 'undefined')? opts.msg : (this.messages['ntf'+type] ? 'ntf'+type : 'ntfsmth')),
  7836. hiddens = this.arrayFlip(this.options.notifyDialog.hiddens || []),
  7837. ndialog = this.ui.notify,
  7838. dialog = ndialog.closest('.ui-dialog'),
  7839. notify = ndialog.children('.elfinder-notify-'+type+(id? ('.'+id) : '')),
  7840. button = notify.children('div.elfinder-notify-cancel').children('button'),
  7841. ntpl = '<div class="elfinder-notify elfinder-notify-{type}'+(id? (' '+id) : '')+'"><span class="elfinder-dialog-icon elfinder-dialog-icon-{type}"></span><span class="elfinder-notify-msg">{msg}</span> <span class="elfinder-notify-cnt"></span><div class="elfinder-notify-progressbar"><div class="elfinder-notify-progress"></div></div><div class="elfinder-notify-cancel"></div></div>',
  7842. delta = opts.cnt + 0,
  7843. size = (typeof opts.size != 'undefined')? parseInt(opts.size) : null,
  7844. progress = (typeof opts.progress != 'undefined' && opts.progress >= 0) ? opts.progress : null,
  7845. fakeint = opts.fakeinterval || 200,
  7846. cancel = opts.cancel,
  7847. clhover = 'ui-state-hover',
  7848. close = function() {
  7849. var prog = notify.find('.elfinder-notify-progress'),
  7850. rm = function() {
  7851. notify.remove();
  7852. if (!ndialog.children(dialog.data('minimized')? void(0) : ':visible').length) {
  7853. if (dialog.data('minimized')) {
  7854. dialog.data('minimized').hide();
  7855. } else {
  7856. ndialog.elfinderdialog('close');
  7857. }
  7858. }
  7859. setProgressbar();
  7860. };
  7861. notify._esc && $(document).off('keydown', notify._esc);
  7862. if (notify.data('cur') < 100) {
  7863. prog.animate({
  7864. width : '100%'
  7865. }, 50, function() { requestAnimationFrame(function() { rm(); }); });
  7866. } else {
  7867. rm();
  7868. }
  7869. },
  7870. fakeUp = function(interval) {
  7871. var cur;
  7872. if (notify.length) {
  7873. cur = notify.data('cur') + 1;
  7874. if (cur <= 98) {
  7875. notify.find('.elfinder-notify-progress').width(cur + '%');
  7876. notify.data('cur', cur);
  7877. setProgressbar();
  7878. setTimeout(function() {
  7879. interval *= 1.05;
  7880. fakeUp(interval);
  7881. }, interval);
  7882. }
  7883. }
  7884. },
  7885. setProgressbar = function() {
  7886. var cnt = 0,
  7887. val = 0,
  7888. ntfs = ndialog.children('.elfinder-notify'),
  7889. w;
  7890. if (ntfs.length) {
  7891. ntfs.each(function() {
  7892. cnt++;
  7893. val += Math.min($(this).data('cur'), 100);
  7894. });
  7895. w = cnt? Math.floor(val / (cnt * 100) * 100) + '%' : 0;
  7896. self.ui.progressbar.width(w);
  7897. if (dialog.data('minimized')) {
  7898. dialog.data('minimized').title(w);
  7899. dialog.data('minimized').dialog().children('.ui-dialog-titlebar').children('.elfinder-ui-progressbar').width(w);
  7900. }
  7901. } else {
  7902. self.ui.progressbar.width(0);
  7903. dialog.data('minimized') && dialog.data('minimized').hide();
  7904. }
  7905. },
  7906. cnt, total, prc;
  7907. if (!type) {
  7908. return this;
  7909. }
  7910. if (!notify.length) {
  7911. notify = $(ntpl.replace(/\{type\}/g, type).replace(/\{msg\}/g, msg));
  7912. if (hiddens[type]) {
  7913. notify.hide();
  7914. } else {
  7915. ndialog.on('minimize', function(e) {
  7916. dialog.data('minimized') && setProgressbar();
  7917. });
  7918. }
  7919. notify.appendTo(ndialog).data('cnt', 0);
  7920. if (progress != null) {
  7921. notify.data({progress : 0, total : 0, cur : 0});
  7922. } else {
  7923. notify.data({cur : 0});
  7924. fakeUp(fakeint);
  7925. }
  7926. if (cancel) {
  7927. button = $('<span class="elfinder-notify-button ui-icon ui-icon-close" title="'+this.i18n('btnCancel')+'"></span>')
  7928. .on('mouseenter mouseleave', function(e) {
  7929. $(this).toggleClass(clhover, e.type === 'mouseenter');
  7930. });
  7931. notify.children('div.elfinder-notify-cancel').append(button);
  7932. }
  7933. ndialog.trigger('resize');
  7934. } else if (typeof opts.msg !== 'undefined') {
  7935. notify.children('span.elfinder-notify-msg').html(msg);
  7936. }
  7937. cnt = delta + parseInt(notify.data('cnt'));
  7938. if (cnt > 0) {
  7939. if (cancel && button.length) {
  7940. if ($.isFunction(cancel) || (typeof cancel === 'object' && cancel.promise)) {
  7941. notify._esc = function(e) {
  7942. if (e.type == 'keydown' && e.keyCode != $.ui.keyCode.ESCAPE) {
  7943. return;
  7944. }
  7945. e.preventDefault();
  7946. e.stopPropagation();
  7947. close();
  7948. if (cancel.promise) {
  7949. cancel.reject(0); // 0 is canceling flag
  7950. } else {
  7951. cancel(e);
  7952. }
  7953. };
  7954. button.on('click', function(e) {
  7955. notify._esc(e);
  7956. });
  7957. $(document).on('keydown.' + this.namespace, notify._esc);
  7958. }
  7959. }
  7960. !opts.hideCnt && notify.children('.elfinder-notify-cnt').text('('+cnt+')');
  7961. if (delta > 0 && ndialog.is(':hidden') && !hiddens[type]) {
  7962. if (dialog.data('minimized')) {
  7963. dialog.data('minimized').show();
  7964. } else {
  7965. ndialog.elfinderdialog('open', this).height('auto');
  7966. }
  7967. }
  7968. notify.data('cnt', cnt);
  7969. if ((progress != null)
  7970. && (total = notify.data('total')) >= 0
  7971. && (prc = notify.data('progress')) >= 0) {
  7972. total += size != null? size : delta;
  7973. prc += progress;
  7974. (size == null && delta < 0) && (prc += delta * 100);
  7975. notify.data({progress : prc, total : total});
  7976. if (size != null) {
  7977. prc *= 100;
  7978. total = Math.max(1, total);
  7979. }
  7980. progress = Math.min(parseInt(prc/total), 100);
  7981. notify.find('.elfinder-notify-progress')
  7982. .animate({
  7983. width : (progress < 100 ? progress : 100)+'%'
  7984. }, 20, function() {
  7985. notify.data('cur', progress);
  7986. setProgressbar();
  7987. });
  7988. }
  7989. } else {
  7990. close();
  7991. }
  7992. return this;
  7993. },
  7994. /**
  7995. * Open confirmation dialog
  7996. *
  7997. * @param Object options
  7998. * @example
  7999. * this.confirm({
  8000. * cssClass : 'elfinder-confirm-mydialog',
  8001. * title : 'Remove files',
  8002. * text : 'Here is question text',
  8003. * accept : { // accept callback - required
  8004. * label : 'Continue',
  8005. * callback : function(applyToAll) { fm.log('Ok') }
  8006. * },
  8007. * cancel : { // cancel callback - required
  8008. * label : 'Cancel',
  8009. * callback : function() { fm.log('Cancel')}
  8010. * },
  8011. * reject : { // reject callback - optionally
  8012. * label : 'No',
  8013. * callback : function(applyToAll) { fm.log('No')}
  8014. * },
  8015. * buttons : [ // additional buttons callback - optionally
  8016. * {
  8017. * label : 'Btn1',
  8018. * callback : function(applyToAll) { fm.log('Btn1')}
  8019. * }
  8020. * ],
  8021. * all : true // display checkbox "Apply to all"
  8022. * })
  8023. * @return elFinder
  8024. */
  8025. confirm : function(opts) {
  8026. var self = this,
  8027. complete = false,
  8028. options = {
  8029. cssClass : 'elfinder-dialog-confirm',
  8030. modal : true,
  8031. resizable : false,
  8032. title : this.i18n(opts.title || 'confirmReq'),
  8033. buttons : {},
  8034. close : function() {
  8035. !complete && opts.cancel.callback();
  8036. $(this).elfinderdialog('destroy');
  8037. }
  8038. },
  8039. apply = this.i18n('apllyAll'),
  8040. label, checkbox, btnNum;
  8041. if (opts.cssClass) {
  8042. options.cssClass += ' ' + opts.cssClass;
  8043. }
  8044. options.buttons[this.i18n(opts.accept.label)] = function() {
  8045. opts.accept.callback(!!(checkbox && checkbox.prop('checked')));
  8046. complete = true;
  8047. $(this).elfinderdialog('close');
  8048. };
  8049. options.buttons[this.i18n(opts.accept.label)]._cssClass = 'elfinder-confirm-accept';
  8050. if (opts.reject) {
  8051. options.buttons[this.i18n(opts.reject.label)] = function() {
  8052. opts.reject.callback(!!(checkbox && checkbox.prop('checked')));
  8053. complete = true;
  8054. $(this).elfinderdialog('close');
  8055. };
  8056. options.buttons[this.i18n(opts.reject.label)]._cssClass = 'elfinder-confirm-reject';
  8057. }
  8058. if (opts.buttons && opts.buttons.length > 0) {
  8059. btnNum = 1;
  8060. $.each(opts.buttons, function(i, v){
  8061. options.buttons[self.i18n(v.label)] = function() {
  8062. v.callback(!!(checkbox && checkbox.prop('checked')));
  8063. complete = true;
  8064. $(this).elfinderdialog('close');
  8065. };
  8066. options.buttons[self.i18n(v.label)]._cssClass = 'elfinder-confirm-extbtn' + (btnNum++);
  8067. if (v.cssClass) {
  8068. options.buttons[self.i18n(v.label)]._cssClass += ' ' + v.cssClass;
  8069. }
  8070. });
  8071. }
  8072. options.buttons[this.i18n(opts.cancel.label)] = function() {
  8073. $(this).elfinderdialog('close');
  8074. };
  8075. options.buttons[this.i18n(opts.cancel.label)]._cssClass = 'elfinder-confirm-cancel';
  8076. if (opts.all) {
  8077. options.create = function() {
  8078. var base = $('<div class="elfinder-dialog-confirm-applyall"></div>');
  8079. checkbox = $('<input type="checkbox" />');
  8080. $(this).next().find('.ui-dialog-buttonset')
  8081. .prepend(base.append($('<label>'+apply+'</label>').prepend(checkbox)));
  8082. };
  8083. }
  8084. if (opts.optionsCallback && $.isFunction(opts.optionsCallback)) {
  8085. opts.optionsCallback(options);
  8086. }
  8087. return this.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-confirm"></span>' + this.i18n(opts.text), options);
  8088. },
  8089. /**
  8090. * Create unique file name in required dir
  8091. *
  8092. * @param String file name
  8093. * @param String parent dir hash
  8094. * @param String glue
  8095. * @return String
  8096. */
  8097. uniqueName : function(prefix, phash, glue) {
  8098. var i = 0, ext = '', p, name;
  8099. prefix = this.i18n(false, prefix);
  8100. phash = phash || this.cwd().hash;
  8101. glue = (typeof glue === 'undefined')? ' ' : glue;
  8102. if (p = prefix.match(/^(.+)(\.[^.]+)$/)) {
  8103. ext = p[2];
  8104. prefix = p[1];
  8105. }
  8106. name = prefix+ext;
  8107. if (!this.fileByName(name, phash)) {
  8108. return name;
  8109. }
  8110. while (i < 10000) {
  8111. name = prefix + glue + (++i) + ext;
  8112. if (!this.fileByName(name, phash)) {
  8113. return name;
  8114. }
  8115. }
  8116. return prefix + Math.random() + ext;
  8117. },
  8118. /**
  8119. * Return message translated onto current language
  8120. * Allowed accept HTML element that was wrapped in jQuery object
  8121. * To be careful to XSS vulnerability of HTML element Ex. You should use `fm.escape(file.name)`
  8122. *
  8123. * @param String|Array message[s]|Object jQuery
  8124. * @return String
  8125. **/
  8126. i18n : function() {
  8127. var self = this,
  8128. messages = this.messages,
  8129. input = [],
  8130. ignore = [],
  8131. message = function(m) {
  8132. var file;
  8133. if (m.indexOf('#') === 0) {
  8134. if ((file = self.file(m.substr(1)))) {
  8135. return file.name;
  8136. }
  8137. }
  8138. return m;
  8139. },
  8140. i, j, m, escFunc, start = 0, isErr;
  8141. if (arguments.length && arguments[0] === false) {
  8142. escFunc = function(m){ return m; };
  8143. start = 1;
  8144. }
  8145. for (i = start; i< arguments.length; i++) {
  8146. m = arguments[i];
  8147. if (Array.isArray(m)) {
  8148. for (j = 0; j < m.length; j++) {
  8149. if (m[j] instanceof jQuery) {
  8150. // jQuery object is HTML element
  8151. input.push(m[j]);
  8152. } else if (typeof m[j] !== 'undefined'){
  8153. input.push(message('' + m[j]));
  8154. }
  8155. }
  8156. } else if (m instanceof jQuery) {
  8157. // jQuery object is HTML element
  8158. input.push(m[j]);
  8159. } else if (typeof m !== 'undefined'){
  8160. input.push(message('' + m));
  8161. }
  8162. }
  8163. for (i = 0; i < input.length; i++) {
  8164. // dont translate placeholders
  8165. if ($.inArray(i, ignore) !== -1) {
  8166. continue;
  8167. }
  8168. m = input[i];
  8169. if (typeof m == 'string') {
  8170. isErr = !!(messages[m] && m.match(/^err/));
  8171. // translate message
  8172. m = messages[m] || (escFunc? escFunc(m) : self.escape(m));
  8173. // replace placeholders in message
  8174. m = m.replace(/\$(\d+)/g, function(match, placeholder) {
  8175. var res;
  8176. placeholder = i + parseInt(placeholder);
  8177. if (placeholder > 0 && input[placeholder]) {
  8178. ignore.push(placeholder);
  8179. }
  8180. res = escFunc? escFunc(input[placeholder]) : self.escape(input[placeholder]);
  8181. if (isErr) {
  8182. res = '<span class="elfinder-err-var elfinder-err-var' + placeholder + '">' + res + '</span>';
  8183. }
  8184. return res;
  8185. });
  8186. } else {
  8187. // get HTML from jQuery object
  8188. m = m.get(0).outerHTML;
  8189. }
  8190. input[i] = m;
  8191. }
  8192. return $.grep(input, function(m, i) { return $.inArray(i, ignore) === -1 ? true : false; }).join('<br>');
  8193. },
  8194. /**
  8195. * Get icon style from file.icon
  8196. *
  8197. * @param Object elFinder file object
  8198. * @return String|Object
  8199. */
  8200. getIconStyle : function(file, asObject) {
  8201. var self = this,
  8202. template = {
  8203. 'background' : 'url(\'{url}\') 0 0 no-repeat',
  8204. 'background-size' : 'contain'
  8205. },
  8206. style = '',
  8207. cssObj = {},
  8208. i = 0;
  8209. if (file.icon) {
  8210. style = 'style="';
  8211. $.each(template, function(k, v) {
  8212. if (i++ === 0) {
  8213. v = v.replace('{url}', self.escape(file.icon));
  8214. }
  8215. if (asObject) {
  8216. cssObj[k] = v;
  8217. } else {
  8218. style += k+':'+v+';';
  8219. }
  8220. });
  8221. style += '"';
  8222. }
  8223. return asObject? cssObj : style;
  8224. },
  8225. /**
  8226. * Convert mimetype into css classes
  8227. *
  8228. * @param String file mimetype
  8229. * @return String
  8230. */
  8231. mime2class : function(mimeType) {
  8232. var prefix = 'elfinder-cwd-icon-',
  8233. mime = mimeType.toLowerCase(),
  8234. isText = this.textMimes[mime];
  8235. mime = mime.split('/');
  8236. if (isText) {
  8237. mime[0] += ' ' + prefix + 'text';
  8238. } else if (mime[1] && mime[1].match(/\+xml$/)) {
  8239. mime[0] += ' ' + prefix + 'xml';
  8240. }
  8241. return prefix + mime[0] + (mime[1] ? ' ' + prefix + mime[1].replace(/(\.|\+)/g, '-') : '');
  8242. },
  8243. /**
  8244. * Return localized kind of file
  8245. *
  8246. * @param Object|String file or file mimetype
  8247. * @return String
  8248. */
  8249. mime2kind : function(f) {
  8250. var isObj = typeof(f) == 'object' ? true : false,
  8251. mime = isObj ? f.mime : f,
  8252. kind;
  8253. if (isObj && f.alias && mime != 'symlink-broken') {
  8254. kind = 'Alias';
  8255. } else if (this.kinds[mime]) {
  8256. if (isObj && mime === 'directory' && (! f.phash || f.isroot)) {
  8257. kind = 'Root';
  8258. } else {
  8259. kind = this.kinds[mime];
  8260. }
  8261. }
  8262. if (! kind) {
  8263. if (mime.indexOf('text') === 0) {
  8264. kind = 'Text';
  8265. } else if (mime.indexOf('image') === 0) {
  8266. kind = 'Image';
  8267. } else if (mime.indexOf('audio') === 0) {
  8268. kind = 'Audio';
  8269. } else if (mime.indexOf('video') === 0) {
  8270. kind = 'Video';
  8271. } else if (mime.indexOf('application') === 0) {
  8272. kind = 'App';
  8273. } else {
  8274. kind = mime;
  8275. }
  8276. }
  8277. return this.messages['kind'+kind] ? this.i18n('kind'+kind) : mime;
  8278. },
  8279. /**
  8280. * Return boolean Is mime-type text file
  8281. *
  8282. * @param String mime-type
  8283. * @return Boolean
  8284. */
  8285. mimeIsText : function(mime) {
  8286. return (this.textMimes[mime.toLowerCase()] || (mime.indexOf('text/') === 0 && mime.substr(5, 3) !== 'rtf') || mime.match(/^application\/.+\+xml$/))? true : false;
  8287. },
  8288. /**
  8289. * Returns a date string formatted according to the given format string
  8290. *
  8291. * @param String format string
  8292. * @param Object Date object
  8293. * @return String
  8294. */
  8295. date : function(format, date) {
  8296. var self = this,
  8297. output, d, dw, m, y, h, g, i, s;
  8298. if (! date) {
  8299. date = new Date();
  8300. }
  8301. h = date[self.getHours]();
  8302. g = h > 12 ? h - 12 : h;
  8303. i = date[self.getMinutes]();
  8304. s = date[self.getSeconds]();
  8305. d = date[self.getDate]();
  8306. dw = date[self.getDay]();
  8307. m = date[self.getMonth]() + 1;
  8308. y = date[self.getFullYear]();
  8309. output = format.replace(/[a-z]/gi, function(val) {
  8310. switch (val) {
  8311. case 'd': return d > 9 ? d : '0'+d;
  8312. case 'j': return d;
  8313. case 'D': return self.i18n(self.i18.daysShort[dw]);
  8314. case 'l': return self.i18n(self.i18.days[dw]);
  8315. case 'm': return m > 9 ? m : '0'+m;
  8316. case 'n': return m;
  8317. case 'M': return self.i18n(self.i18.monthsShort[m-1]);
  8318. case 'F': return self.i18n(self.i18.months[m-1]);
  8319. case 'Y': return y;
  8320. case 'y': return (''+y).substr(2);
  8321. case 'H': return h > 9 ? h : '0'+h;
  8322. case 'G': return h;
  8323. case 'g': return g;
  8324. case 'h': return g > 9 ? g : '0'+g;
  8325. case 'a': return h >= 12 ? 'pm' : 'am';
  8326. case 'A': return h >= 12 ? 'PM' : 'AM';
  8327. case 'i': return i > 9 ? i : '0'+i;
  8328. case 's': return s > 9 ? s : '0'+s;
  8329. }
  8330. return val;
  8331. });
  8332. return output;
  8333. },
  8334. /**
  8335. * Return localized date
  8336. *
  8337. * @param Object file object
  8338. * @return String
  8339. */
  8340. formatDate : function(file, t) {
  8341. var self = this,
  8342. ts = t || file.ts,
  8343. i18 = self.i18,
  8344. date, format, output, d, dw, m, y, h, g, i, s;
  8345. if (self.options.clientFormatDate && ts > 0) {
  8346. date = new Date(ts*1000);
  8347. format = ts >= this.yesterday
  8348. ? this.fancyFormat
  8349. : this.dateFormat;
  8350. output = self.date(format, date);
  8351. return ts >= this.yesterday
  8352. ? output.replace('$1', this.i18n(ts >= this.today ? 'Today' : 'Yesterday'))
  8353. : output;
  8354. } else if (file.date) {
  8355. return file.date.replace(/([a-z]+)\s/i, function(a1, a2) { return self.i18n(a2)+' '; });
  8356. }
  8357. return self.i18n('dateUnknown');
  8358. },
  8359. /**
  8360. * Return localized number string
  8361. *
  8362. * @param Number
  8363. * @return String
  8364. */
  8365. toLocaleString : function(num) {
  8366. var v = new Number(num);
  8367. if (v) {
  8368. if (v.toLocaleString) {
  8369. return v.toLocaleString();
  8370. } else {
  8371. return String(num).replace( /(\d)(?=(\d\d\d)+(?!\d))/g, '$1,');
  8372. }
  8373. }
  8374. return num;
  8375. },
  8376. /**
  8377. * Return css class marks file permissions
  8378. *
  8379. * @param Object file
  8380. * @return String
  8381. */
  8382. perms2class : function(o) {
  8383. var c = '';
  8384. if (!o.read && !o.write) {
  8385. c = 'elfinder-na';
  8386. } else if (!o.read) {
  8387. c = 'elfinder-wo';
  8388. } else if (!o.write) {
  8389. c = 'elfinder-ro';
  8390. }
  8391. if (o.type) {
  8392. c += ' elfinder-' + this.escape(o.type);
  8393. }
  8394. return c;
  8395. },
  8396. /**
  8397. * Return localized string with file permissions
  8398. *
  8399. * @param Object file
  8400. * @return String
  8401. */
  8402. formatPermissions : function(f) {
  8403. var p = [];
  8404. f.read && p.push(this.i18n('read'));
  8405. f.write && p.push(this.i18n('write'));
  8406. return p.length ? p.join(' '+this.i18n('and')+' ') : this.i18n('noaccess');
  8407. },
  8408. /**
  8409. * Return formated file size
  8410. *
  8411. * @param Number file size
  8412. * @return String
  8413. */
  8414. formatSize : function(s) {
  8415. var n = 1, u = 'b';
  8416. if (s == 'unknown') {
  8417. return this.i18n('unknown');
  8418. }
  8419. if (s > 1073741824) {
  8420. n = 1073741824;
  8421. u = 'GB';
  8422. } else if (s > 1048576) {
  8423. n = 1048576;
  8424. u = 'MB';
  8425. } else if (s > 1024) {
  8426. n = 1024;
  8427. u = 'KB';
  8428. }
  8429. s = s/n;
  8430. return (s > 0 ? n >= 1048576 ? s.toFixed(2) : Math.round(s) : 0) +' '+u;
  8431. },
  8432. /**
  8433. * Return formated file mode by options.fileModeStyle
  8434. *
  8435. * @param String file mode
  8436. * @param String format style
  8437. * @return String
  8438. */
  8439. formatFileMode : function(p, style) {
  8440. var i, o, s, b, sticy, suid, sgid, str, oct;
  8441. if (!style) {
  8442. style = this.options.fileModeStyle.toLowerCase();
  8443. }
  8444. p = $.trim(p);
  8445. if (p.match(/[rwxs-]{9}$/i)) {
  8446. str = p = p.substr(-9);
  8447. if (style == 'string') {
  8448. return str;
  8449. }
  8450. oct = '';
  8451. s = 0;
  8452. for (i=0; i<7; i=i+3) {
  8453. o = p.substr(i, 3);
  8454. b = 0;
  8455. if (o.match(/[r]/i)) {
  8456. b += 4;
  8457. }
  8458. if (o.match(/[w]/i)) {
  8459. b += 2;
  8460. }
  8461. if (o.match(/[xs]/i)) {
  8462. if (o.match(/[xs]/)) {
  8463. b += 1;
  8464. }
  8465. if (o.match(/[s]/i)) {
  8466. if (i == 0) {
  8467. s += 4;
  8468. } else if (i == 3) {
  8469. s += 2;
  8470. }
  8471. }
  8472. }
  8473. oct += b.toString(8);
  8474. }
  8475. if (s) {
  8476. oct = s.toString(8) + oct;
  8477. }
  8478. } else {
  8479. p = parseInt(p, 8);
  8480. oct = p? p.toString(8) : '';
  8481. if (!p || style == 'octal') {
  8482. return oct;
  8483. }
  8484. o = p.toString(8);
  8485. s = 0;
  8486. if (o.length > 3) {
  8487. o = o.substr(-4);
  8488. s = parseInt(o.substr(0, 1), 8);
  8489. o = o.substr(1);
  8490. }
  8491. sticy = ((s & 1) == 1); // not support
  8492. sgid = ((s & 2) == 2);
  8493. suid = ((s & 4) == 4);
  8494. str = '';
  8495. for(i=0; i<3; i++) {
  8496. if ((parseInt(o.substr(i, 1), 8) & 4) == 4) {
  8497. str += 'r';
  8498. } else {
  8499. str += '-';
  8500. }
  8501. if ((parseInt(o.substr(i, 1), 8) & 2) == 2) {
  8502. str += 'w';
  8503. } else {
  8504. str += '-';
  8505. }
  8506. if ((parseInt(o.substr(i, 1), 8) & 1) == 1) {
  8507. str += ((i==0 && suid)||(i==1 && sgid))? 's' : 'x';
  8508. } else {
  8509. str += '-';
  8510. }
  8511. }
  8512. }
  8513. if (style == 'both') {
  8514. return str + ' (' + oct + ')';
  8515. } else if (style == 'string') {
  8516. return str;
  8517. } else {
  8518. return oct;
  8519. }
  8520. },
  8521. /**
  8522. * Regist this.decodeRawString function
  8523. *
  8524. * @return void
  8525. */
  8526. registRawStringDecoder : function(rawStringDecoder) {
  8527. if ($.isFunction(rawStringDecoder)) {
  8528. this.decodeRawString = this.options.rawStringDecoder = rawStringDecoder;
  8529. }
  8530. },
  8531. /**
  8532. * Return boolean that uploadable MIME type into target folder
  8533. *
  8534. * @param String mime MIME type
  8535. * @param String target target folder hash
  8536. * @return Bool
  8537. */
  8538. uploadMimeCheck : function(mime, target) {
  8539. target = target || this.cwd().hash;
  8540. var res = true, // default is allow
  8541. mimeChecker = this.option('uploadMime', target),
  8542. allow,
  8543. deny,
  8544. check = function(checker) {
  8545. var ret = false;
  8546. if (typeof checker === 'string' && checker.toLowerCase() === 'all') {
  8547. ret = true;
  8548. } else if (Array.isArray(checker) && checker.length) {
  8549. $.each(checker, function(i, v) {
  8550. v = v.toLowerCase();
  8551. if (v === 'all' || mime.indexOf(v) === 0) {
  8552. ret = true;
  8553. return false;
  8554. }
  8555. });
  8556. }
  8557. return ret;
  8558. };
  8559. if (mime && $.isPlainObject(mimeChecker)) {
  8560. mime = mime.toLowerCase();
  8561. allow = check(mimeChecker.allow);
  8562. deny = check(mimeChecker.deny);
  8563. if (mimeChecker.firstOrder === 'allow') {
  8564. res = false; // default is deny
  8565. if (! deny && allow === true) { // match only allow
  8566. res = true;
  8567. }
  8568. } else {
  8569. res = true; // default is allow
  8570. if (deny === true && ! allow) { // match only deny
  8571. res = false;
  8572. }
  8573. }
  8574. }
  8575. return res;
  8576. },
  8577. /**
  8578. * call chained sequence of async deferred functions
  8579. *
  8580. * @param Array tasks async functions
  8581. * @return Object jQuery.Deferred
  8582. */
  8583. sequence : function(tasks) {
  8584. var l = tasks.length,
  8585. chain = function(task, idx) {
  8586. ++idx;
  8587. if (tasks[idx]) {
  8588. return chain(task.then(tasks[idx]), idx);
  8589. } else {
  8590. return task;
  8591. }
  8592. };
  8593. if (l > 1) {
  8594. return chain(tasks[0](), 0);
  8595. } else {
  8596. return tasks[0]();
  8597. }
  8598. },
  8599. /**
  8600. * Reload contents of target URL for clear browser cache
  8601. *
  8602. * @param String url target URL
  8603. * @return Object jQuery.Deferred
  8604. */
  8605. reloadContents : function(url) {
  8606. var dfd = $.Deferred(),
  8607. ifm;
  8608. try {
  8609. ifm = $('<iframe width="1" height="1" scrolling="no" frameborder="no" style="position:absolute; top:-1px; left:-1px" crossorigin="use-credentials">')
  8610. .attr('src', url)
  8611. .one('load', function() {
  8612. var ifm = $(this);
  8613. try {
  8614. this.contentDocument.location.reload(true);
  8615. ifm.one('load', function() {
  8616. ifm.remove();
  8617. dfd.resolve();
  8618. });
  8619. } catch(e) {
  8620. ifm.attr('src', '').attr('src', url).one('load', function() {
  8621. ifm.remove();
  8622. dfd.resolve();
  8623. });
  8624. }
  8625. })
  8626. .appendTo('body');
  8627. } catch(e) {
  8628. ifm && ifm.remove();
  8629. dfd.reject();
  8630. }
  8631. return dfd;
  8632. },
  8633. /**
  8634. * Make netmount option for OAuth2
  8635. *
  8636. * @param String protocol
  8637. * @param String name
  8638. * @param String host
  8639. * @param Object opts Default {noOffline: false, root: 'root', pathI18n: 'folderId', folders: true}
  8640. }
  8641. *
  8642. * @return Object
  8643. */
  8644. makeNetmountOptionOauth : function(protocol, name, host, opt) {
  8645. var noOffline = typeof opt === 'boolean'? opt : null, // for backward compat
  8646. opts = Object.assign({
  8647. noOffline : false,
  8648. root : 'root',
  8649. pathI18n : 'folderId',
  8650. folders : true
  8651. }, (noOffline === null? (opt || {}) : {noOffline : noOffline})),
  8652. addFolders = function(fm, bro, folders) {
  8653. var self = this,
  8654. cnt = Object.keys($.isPlainObject(folders)? folders : {}).length,
  8655. select;
  8656. bro.next().remove();
  8657. if (cnt) {
  8658. select = $('<select class="ui-corner-all elfinder-tabstop" style="max-width:200px;">').append(
  8659. $($.map(folders, function(n,i){return '<option value="'+fm.escape((i+'').trim())+'">'+fm.escape(n)+'</option>';}).join(''))
  8660. ).on('change click', function(e){
  8661. var node = $(this),
  8662. path = node.val(),
  8663. spn;
  8664. self.inputs.path.val(path);
  8665. if (opts.folders && (e.type === 'change' || node.data('current') !== path)) {
  8666. node.next().remove();
  8667. node.data('current', path);
  8668. if (path != opts.root) {
  8669. spn = spinner();
  8670. if (xhr && xhr.state() === 'pending') {
  8671. fm.abortXHR(xhr, { quiet: true , abort: true });
  8672. }
  8673. node.after(spn);
  8674. xhr = fm.request({
  8675. data : {cmd : 'netmount', protocol: protocol, host: host, user: 'init', path: path, pass: 'folders'},
  8676. preventDefault : true
  8677. }).done(function(data){
  8678. addFolders.call(self, fm, node, data.folders);
  8679. }).always(function() {
  8680. fm.abortXHR(xhr, { quiet: true });
  8681. spn.remove();
  8682. }).xhr;
  8683. }
  8684. }
  8685. });
  8686. bro.after($('<div></div>').append(select))
  8687. .closest('.ui-dialog').trigger('tabstopsInit');
  8688. select.trigger('focus');
  8689. }
  8690. },
  8691. spinner = function() {
  8692. return $('<div class="elfinder-netmount-spinner"></div>').append('<span class="elfinder-spinner"></span>');
  8693. },
  8694. xhr;
  8695. return {
  8696. vars : {},
  8697. name : name,
  8698. inputs: {
  8699. offline : $('<input type="checkbox"/>').on('change', function() {
  8700. $(this).parents('table.elfinder-netmount-tb').find('select:first').trigger('change', 'reset');
  8701. }),
  8702. host : $('<span><span class="elfinder-spinner"></span></span><input type="hidden"/>'),
  8703. path : $('<input type="text" value="'+opts.root+'"/>'),
  8704. user : $('<input type="hidden"/>'),
  8705. pass : $('<input type="hidden"/>'),
  8706. mnt2res : $('<input type="hidden"/>')
  8707. },
  8708. select: function(fm, ev, d){
  8709. var f = this.inputs,
  8710. oline = f.offline,
  8711. f0 = $(f.host[0]),
  8712. data = d || null;
  8713. this.vars.mbtn = f.host.closest('.ui-dialog').children('.ui-dialog-buttonpane:first').find('button.elfinder-btncnt-0');
  8714. if (! f0.data('inrequest')
  8715. && (f0.find('span.elfinder-spinner').length
  8716. || data === 'reset'
  8717. || (data === 'winfocus' && ! f0.siblings('span.elfinder-button-icon-reload').length))
  8718. )
  8719. {
  8720. if (oline.parent().children().length === 1) {
  8721. f.path.parent().prev().html(fm.i18n(opts.pathI18n));
  8722. oline.attr('title', fm.i18n('offlineAccess'));
  8723. oline.uniqueId().after($('<label></label>').attr('for', oline.attr('id')).html(' '+fm.i18n('offlineAccess')));
  8724. }
  8725. f0.data('inrequest', true).empty().addClass('elfinder-spinner')
  8726. .parent().find('span.elfinder-button-icon').remove();
  8727. fm.request({
  8728. data : {cmd : 'netmount', protocol: protocol, host: host, user: 'init', options: {id: fm.id, offline: oline.prop('checked')? 1:0, pass: f.host[1].value}},
  8729. preventDefault : true
  8730. }).done(function(data){
  8731. f0.removeClass("elfinder-spinner").html(data.body.replace(/\{msg:([^}]+)\}/g, function(whole,s1){return fm.i18n(s1, host);}));
  8732. });
  8733. opts.noOffline && oline.closest('tr').hide();
  8734. } else {
  8735. oline.closest('tr')[(opts.noOffline || f.user.val())? 'hide':'show']();
  8736. f0.data('funcexpup') && f0.data('funcexpup')();
  8737. }
  8738. this.vars.mbtn[$(f.host[1]).val()? 'show':'hide']();
  8739. },
  8740. done: function(fm, data){
  8741. var f = this.inputs,
  8742. p = this.protocol,
  8743. f0 = $(f.host[0]),
  8744. f1 = $(f.host[1]),
  8745. expires = '&nbsp;',
  8746. vars = this.vars,
  8747. chk = function() {
  8748. if (vars.oauthW && !document.hasFocus() && --vars.chkCnt) {
  8749. p.trigger('change', 'winfocus');
  8750. vars.tm = setTimeout(chk, 3000);
  8751. }
  8752. },
  8753. btn;
  8754. opts.noOffline && f.offline.closest('tr').hide();
  8755. if (data.mode == 'makebtn') {
  8756. f0.removeClass('elfinder-spinner').removeData('expires').removeData('funcexpup');
  8757. btn = f.host.find('input').on('mouseenter mouseleave', function(){$(this).toggleClass('ui-state-hover');});
  8758. if (data.url) {
  8759. btn.on('click', function() {
  8760. vars.tm && clearTimeout(vars.tm);
  8761. vars.oauthW = window.open(data.url);
  8762. // To correspond to safari, authentication tab sometimes not closing in CORS environment.
  8763. // This may be a safari bug and may improve in the future.
  8764. if ((fm.UA.iOS || fm.UA.Mac) && fm.isCORS && !vars.chkdone) {
  8765. vars.chkCnt = 60;
  8766. vars.tm = setTimeout(chk, 5000);
  8767. }
  8768. });
  8769. }
  8770. f1.val('');
  8771. f.path.val(opts.root).next().remove();
  8772. f.user.val('');
  8773. f.pass.val('');
  8774. ! opts.noOffline && f.offline.closest('tr').show();
  8775. vars.mbtn.hide();
  8776. } else if (data.mode == 'folders') {
  8777. if (data.folders) {
  8778. addFolders.call(this, fm, f.path.nextAll(':last'), data.folders);
  8779. }
  8780. } else {
  8781. if (vars.oauthW) {
  8782. vars.tm && clearTimeout(vars.tm);
  8783. vars.oauthW.close();
  8784. delete vars.oauthW;
  8785. // The problem that Safari's authentication tab doesn't close only affects the first time.
  8786. vars.chkdone = true;
  8787. }
  8788. if (data.expires) {
  8789. expires = '()';
  8790. f0.data('expires', data.expires);
  8791. }
  8792. f0.html(host + expires).removeClass('elfinder-spinner');
  8793. if (data.expires) {
  8794. f0.data('funcexpup', function() {
  8795. var rem = Math.floor((f0.data('expires') - (+new Date()) / 1000) / 60);
  8796. if (rem < 3) {
  8797. f0.parent().children('.elfinder-button-icon-reload').click();
  8798. } else {
  8799. f0.text(f0.text().replace(/\(.*\)/, '('+fm.i18n(['minsLeft', rem])+')'));
  8800. setTimeout(function() {
  8801. if (f0.is(':visible')) {
  8802. f0.data('funcexpup')();
  8803. }
  8804. }, 60000);
  8805. }
  8806. });
  8807. f0.data('funcexpup')();
  8808. }
  8809. if (data.reset) {
  8810. p.trigger('change', 'reset');
  8811. return;
  8812. }
  8813. f0.parent().append($('<span class="elfinder-button-icon elfinder-button-icon-reload" title="'+fm.i18n('reAuth')+'">')
  8814. .on('click', function() {
  8815. f1.val('reauth');
  8816. p.trigger('change', 'reset');
  8817. }));
  8818. f1.val(protocol);
  8819. vars.mbtn.show();
  8820. if (data.folders) {
  8821. addFolders.call(this, fm, f.path, data.folders);
  8822. }
  8823. if (data.mnt2res) {
  8824. f.mnt2res.val('1');
  8825. }
  8826. f.user.val('done');
  8827. f.pass.val('done');
  8828. f.offline.closest('tr').hide();
  8829. }
  8830. f0.removeData('inrequest');
  8831. },
  8832. fail: function(fm, err){
  8833. $(this.inputs.host[0]).removeData('inrequest');
  8834. this.protocol.trigger('change', 'reset');
  8835. },
  8836. integrateInfo: opts.integrate
  8837. };
  8838. },
  8839. /**
  8840. * Find cwd's nodes from files
  8841. *
  8842. * @param Array files
  8843. * @param Object opts {firstOnly: true|false}
  8844. */
  8845. findCwdNodes : function(files, opts) {
  8846. var self = this,
  8847. cwd = this.getUI('cwd'),
  8848. cwdHash = this.cwd().hash,
  8849. newItem = $();
  8850. opts = opts || {};
  8851. $.each(files, function(i, f) {
  8852. if (f.phash === cwdHash || self.searchStatus.state > 1) {
  8853. newItem = newItem.add(self.cwdHash2Elm(f.hash));
  8854. if (opts.firstOnly) {
  8855. return false;
  8856. }
  8857. }
  8858. });
  8859. return newItem;
  8860. },
  8861. /**
  8862. * Convert from relative URL to abstract URL based on current URL
  8863. *
  8864. * @param String URL
  8865. * @return String
  8866. */
  8867. convAbsUrl : function(url) {
  8868. if (url.match(/^http/i)) {
  8869. return url;
  8870. }
  8871. if (url.substr(0,2) === '//') {
  8872. return window.location.protocol + url;
  8873. }
  8874. var root = window.location.protocol + '//' + window.location.host,
  8875. reg = /[^\/]+\/\.\.\//,
  8876. ret;
  8877. if (url.substr(0, 1) === '/') {
  8878. ret = root + url;
  8879. } else {
  8880. ret = root + window.location.pathname.replace(/\/[^\/]+$/, '/') + url;
  8881. }
  8882. ret = ret.replace('/./', '/');
  8883. while(reg.test(ret)) {
  8884. ret = ret.replace(reg, '');
  8885. }
  8886. return ret;
  8887. },
  8888. /**
  8889. * Is same origin to current site
  8890. *
  8891. * @param String check url
  8892. * @return Boolean
  8893. */
  8894. isSameOrigin : function (checkUrl) {
  8895. var url;
  8896. checkUrl = this.convAbsUrl(checkUrl);
  8897. if (location.origin && window.URL) {
  8898. try {
  8899. url = new URL(checkUrl);
  8900. return location.origin === url.origin;
  8901. } catch(e) {}
  8902. }
  8903. url = document.createElement('a');
  8904. url.href = checkUrl;
  8905. return location.protocol === url.protocol && location.host === url.host && location.port && url.port;
  8906. },
  8907. navHash2Id : function(hash) {
  8908. return this.navPrefix + hash;
  8909. },
  8910. navId2Hash : function(id) {
  8911. return typeof(id) == 'string' ? id.substr(this.navPrefix.length) : false;
  8912. },
  8913. cwdHash2Id : function(hash) {
  8914. return this.cwdPrefix + hash;
  8915. },
  8916. cwdId2Hash : function(id) {
  8917. return typeof(id) == 'string' ? id.substr(this.cwdPrefix.length) : false;
  8918. },
  8919. /**
  8920. * navHash to jQuery element object
  8921. *
  8922. * @param String hash nav hash
  8923. * @return Object jQuery element object
  8924. */
  8925. navHash2Elm : function(hash) {
  8926. return $(document.getElementById(this.navHash2Id(hash)));
  8927. },
  8928. /**
  8929. * cwdHash to jQuery element object
  8930. *
  8931. * @param String hash cwd hash
  8932. * @return Object jQuery element object
  8933. */
  8934. cwdHash2Elm : function(hash) {
  8935. return $(document.getElementById(this.cwdHash2Id(hash)));
  8936. },
  8937. isInWindow : function(elem, nochkHide) {
  8938. var elm, rect;
  8939. if (! (elm = elem.get(0))) {
  8940. return false;
  8941. }
  8942. if (! nochkHide && elm.offsetParent === null) {
  8943. return false;
  8944. }
  8945. rect = elm.getBoundingClientRect();
  8946. return document.elementFromPoint(rect.left, rect.top)? true : false;
  8947. },
  8948. /**
  8949. * calculate elFinder node z-index
  8950. *
  8951. * @return void
  8952. */
  8953. zIndexCalc : function() {
  8954. var self = this,
  8955. node = this.getUI(),
  8956. ni = node.css('z-index');
  8957. if (ni && ni !== 'auto' && ni !== 'inherit') {
  8958. self.zIndex = ni;
  8959. } else {
  8960. node.parents().each(function(i, n) {
  8961. var z = $(n).css('z-index');
  8962. if (z !== 'auto' && z !== 'inherit' && (z = parseInt(z))) {
  8963. self.zIndex = z;
  8964. return false;
  8965. }
  8966. });
  8967. }
  8968. },
  8969. /**
  8970. * Load JavaScript files
  8971. *
  8972. * @param Array urls to load JavaScript file URLs
  8973. * @param Function callback call back function on script loaded
  8974. * @param Object opts Additional options to $.ajax OR {loadType: 'tag'} to load by script tag
  8975. * @param Object check { obj: (Object)ParentObject, name: (String)"Attribute name", timeout: (Integer)milliseconds }
  8976. * @return elFinder
  8977. */
  8978. loadScript : function(urls, callback, opts, check) {
  8979. var defOpts = {
  8980. dataType : 'script',
  8981. cache : true
  8982. },
  8983. success, cnt, scripts = {}, results = {};
  8984. opts = opts || {};
  8985. if (opts.tryRequire && this.hasRequire) {
  8986. require(urls, callback, opts.error);
  8987. } else {
  8988. success = function() {
  8989. var cnt, fi, hasError;
  8990. $.each(results, function(i, status) {
  8991. if (status !== 'success' && status !== 'notmodified') {
  8992. hasError = true;
  8993. return false;
  8994. }
  8995. });
  8996. if (!hasError) {
  8997. if ($.isFunction(callback)) {
  8998. if (check) {
  8999. if (typeof check.obj[check.name] === 'undefined') {
  9000. cnt = check.timeout? (check.timeout / 10) : 1;
  9001. fi = setInterval(function() {
  9002. if (--cnt < 0 || typeof check.obj[check.name] !== 'undefined') {
  9003. clearInterval(fi);
  9004. callback();
  9005. }
  9006. }, 10);
  9007. } else {
  9008. callback();
  9009. }
  9010. } else {
  9011. callback();
  9012. }
  9013. }
  9014. } else {
  9015. if (opts.error && $.isFunction(opts.error)) {
  9016. opts.error({ loadResults: results });
  9017. }
  9018. }
  9019. };
  9020. if (opts.loadType === 'tag') {
  9021. $('head > script').each(function() {
  9022. scripts[this.src] = this;
  9023. });
  9024. cnt = urls.length;
  9025. $.each(urls, function(i, url) {
  9026. var done = false,
  9027. script;
  9028. if (scripts[url]) {
  9029. results[i] = scripts[url]._error || 'success';
  9030. (--cnt < 1) && success();
  9031. } else {
  9032. script = document.createElement('script');
  9033. script.charset = opts.charset || 'UTF-8';
  9034. $('head').append(script);
  9035. script.onload = script.onreadystatechange = function() {
  9036. if ( !done && (!this.readyState ||
  9037. this.readyState === 'loaded' || this.readyState === 'complete') ) {
  9038. done = true;
  9039. results[i] = 'success';
  9040. (--cnt < 1) && success();
  9041. }
  9042. };
  9043. script.onerror = function(err) {
  9044. results[i] = script._error = (err && err.type)? err.type : 'error';
  9045. (--cnt < 1) && success();
  9046. };
  9047. script.src = url;
  9048. }
  9049. });
  9050. } else {
  9051. opts = $.isPlainObject(opts)? Object.assign(defOpts, opts) : defOpts;
  9052. cnt = 0;
  9053. (function appendScript(d, status) {
  9054. if (d !== void(0)) {
  9055. results[cnt++] = status;
  9056. }
  9057. if (urls.length) {
  9058. $.ajax(Object.assign({}, opts, {
  9059. url: urls.shift(),
  9060. success: appendScript,
  9061. error: appendScript
  9062. }));
  9063. } else {
  9064. success();
  9065. }
  9066. })();
  9067. }
  9068. }
  9069. return this;
  9070. },
  9071. /**
  9072. * Load CSS files
  9073. *
  9074. * @param Array to load CSS file URLs
  9075. * @param Object options
  9076. * @return elFinder
  9077. */
  9078. loadCss : function(urls, opts) {
  9079. var self = this,
  9080. clName, dfds;
  9081. if (typeof urls === 'string') {
  9082. urls = [ urls ];
  9083. }
  9084. if (opts) {
  9085. if (opts.className) {
  9086. clName = opts.className;
  9087. }
  9088. if (opts.dfd && opts.dfd.promise) {
  9089. dfds = [];
  9090. }
  9091. }
  9092. $.each(urls, function(i, url) {
  9093. var link, df;
  9094. url = self.convAbsUrl(url).replace(/^https?:/i, '');
  9095. if (dfds) {
  9096. dfds[i] = $.Deferred();
  9097. }
  9098. if (! $('head > link[href="' + self.escape(url) + '"]').length) {
  9099. link = document.createElement('link');
  9100. link.type = 'text/css';
  9101. link.rel = 'stylesheet';
  9102. link.href = url;
  9103. if (clName) {
  9104. link.className = clName;
  9105. }
  9106. if (dfds) {
  9107. link.onload = function() {
  9108. dfds[i].resolve();
  9109. };
  9110. link.onerror = function() {
  9111. dfds[i].reject();
  9112. };
  9113. }
  9114. $('head').append(link);
  9115. } else {
  9116. dfds && dfds[i].resolve();
  9117. }
  9118. });
  9119. if (dfds) {
  9120. $.when.apply(null, dfds).done(function() {
  9121. opts.dfd.resolve();
  9122. }).fail(function() {
  9123. opts.dfd.reject();
  9124. });
  9125. }
  9126. return this;
  9127. },
  9128. /**
  9129. * Abortable async job performer
  9130. *
  9131. * @param func Function
  9132. * @param arr Array
  9133. * @param opts Object
  9134. *
  9135. * @return Object $.Deferred that has an extended method _abort()
  9136. */
  9137. asyncJob : function(func, arr, opts) {
  9138. var dfrd = $.Deferred(),
  9139. abortFlg = false,
  9140. parms = Object.assign({
  9141. interval : 0,
  9142. numPerOnce : 1
  9143. }, opts || {}),
  9144. resArr = [],
  9145. vars =[],
  9146. curVars = [],
  9147. exec,
  9148. tm;
  9149. dfrd._abort = function(resolve) {
  9150. tm && clearTimeout(tm);
  9151. vars = [];
  9152. abortFlg = true;
  9153. if (dfrd.state() === 'pending') {
  9154. dfrd[resolve? 'resolve' : 'reject'](resArr);
  9155. }
  9156. };
  9157. dfrd.fail(function() {
  9158. dfrd._abort();
  9159. }).always(function() {
  9160. dfrd._abort = function() {};
  9161. });
  9162. if (typeof func === 'function' && Array.isArray(arr)) {
  9163. vars = arr.concat();
  9164. exec = function() {
  9165. var i, len, res;
  9166. if (abortFlg) {
  9167. return;
  9168. }
  9169. curVars = vars.splice(0, parms.numPerOnce);
  9170. len = curVars.length;
  9171. for (i = 0; i < len; i++) {
  9172. if (abortFlg) {
  9173. break;
  9174. }
  9175. res = func(curVars[i]);
  9176. (res !== null) && resArr.push(res);
  9177. }
  9178. if (abortFlg) {
  9179. return;
  9180. }
  9181. if (vars.length) {
  9182. tm = setTimeout(exec, parms.interval);
  9183. } else {
  9184. dfrd.resolve(resArr);
  9185. }
  9186. };
  9187. if (vars.length) {
  9188. tm = setTimeout(exec, 0);
  9189. } else {
  9190. dfrd.resolve(resArr);
  9191. }
  9192. } else {
  9193. dfrd.reject();
  9194. }
  9195. return dfrd;
  9196. },
  9197. getSize : function(targets) {
  9198. var self = this,
  9199. reqs = [],
  9200. tgtlen = targets.length,
  9201. dfrd = $.Deferred().fail(function() {
  9202. $.each(reqs, function(i, req) {
  9203. if (req) {
  9204. req.syncOnFail && req.syncOnFail(false);
  9205. req.reject();
  9206. }
  9207. });
  9208. }),
  9209. getLeafRoots = function(file) {
  9210. var targets = [];
  9211. if (file.mime === 'directory') {
  9212. $.each(self.leafRoots, function(hash, roots) {
  9213. var phash;
  9214. if (hash === file.hash) {
  9215. targets.push.apply(targets, roots);
  9216. } else {
  9217. phash = (self.file(hash) || {}).phash;
  9218. while(phash) {
  9219. if (phash === file.hash) {
  9220. targets.push.apply(targets, roots);
  9221. }
  9222. phash = (self.file(phash) || {}).phash;
  9223. }
  9224. }
  9225. });
  9226. }
  9227. return targets;
  9228. },
  9229. checkPhash = function(hash) {
  9230. var dfd = $.Deferred(),
  9231. dir = self.file(hash),
  9232. target = dir? dir.phash : hash;
  9233. if (target && ! self.file(target)) {
  9234. self.request({
  9235. data : {
  9236. cmd : 'parents',
  9237. target : target
  9238. },
  9239. preventFail : true
  9240. }).done(function() {
  9241. self.one('parentsdone', function() {
  9242. dfd.resolve();
  9243. });
  9244. }).fail(function() {
  9245. dfd.resolve();
  9246. });
  9247. } else {
  9248. dfd.resolve();
  9249. }
  9250. return dfd;
  9251. },
  9252. cache = function() {
  9253. var dfd = $.Deferred(),
  9254. cnt = Object.keys(self.leafRoots).length;
  9255. if (cnt > 0) {
  9256. $.each(self.leafRoots, function(hash) {
  9257. checkPhash(hash).done(function() {
  9258. --cnt;
  9259. if (cnt < 1) {
  9260. dfd.resolve();
  9261. }
  9262. });
  9263. });
  9264. } else {
  9265. dfd.resolve();
  9266. }
  9267. return dfd;
  9268. };
  9269. self.autoSync('stop');
  9270. cache().done(function() {
  9271. var files = [], grps = {}, dfds = [], cache = [], singles = {};
  9272. $.each(targets, function() {
  9273. files.push.apply(files, getLeafRoots(self.file(this)));
  9274. });
  9275. targets.push.apply(targets, files);
  9276. $.each(targets, function() {
  9277. var root = self.root(this),
  9278. file = self.file(this);
  9279. if (file && (file.sizeInfo || file.mime !== 'directory')) {
  9280. cache.push($.Deferred().resolve(file.sizeInfo? file.sizeInfo : {size: file.size, dirCnt: 0, fileCnt : 1}));
  9281. } else {
  9282. if (! grps[root]) {
  9283. grps[root] = [ this.toString() ];
  9284. } else {
  9285. grps[root].push(this.toString());
  9286. }
  9287. }
  9288. });
  9289. $.each(grps, function() {
  9290. var idx = dfds.length;
  9291. if (this.length === 1) {
  9292. singles[idx] = this[0];
  9293. }
  9294. dfds.push(self.request({
  9295. data : {cmd : 'size', targets : this},
  9296. preventDefault : true
  9297. }));
  9298. });
  9299. reqs.push.apply(reqs, dfds);
  9300. dfds.push.apply(dfds, cache);
  9301. $.when.apply($, dfds).fail(function() {
  9302. dfrd.reject();
  9303. }).done(function() {
  9304. var cache = function(h, data) {
  9305. var file;
  9306. if (file = self.file(h)) {
  9307. file.sizeInfo = { isCache: true };
  9308. $.each(['size', 'dirCnt', 'fileCnt'], function() {
  9309. file.sizeInfo[this] = data[this] || 0;
  9310. });
  9311. file.size = parseInt(file.sizeInfo.size);
  9312. changed.push(file);
  9313. }
  9314. },
  9315. size = 0,
  9316. fileCnt = 0,
  9317. dirCnt = 0,
  9318. argLen = arguments.length,
  9319. cnts = [],
  9320. cntsTxt = '',
  9321. changed = [],
  9322. i, file, data;
  9323. for (i = 0; i < argLen; i++) {
  9324. data = arguments[i];
  9325. file = null;
  9326. if (!data.isCache) {
  9327. if (singles[i] && (file = self.file(singles[i]))) {
  9328. cache(singles[i], data);
  9329. } else if (data.sizes && $.isPlainObject(data.sizes)) {
  9330. $.each(data.sizes, function(h, sizeInfo) {
  9331. cache(h, sizeInfo);
  9332. });
  9333. }
  9334. }
  9335. size += parseInt(data.size);
  9336. if (fileCnt !== false) {
  9337. if (typeof data.fileCnt === 'undefined') {
  9338. fileCnt = false;
  9339. } else {
  9340. fileCnt += parseInt(data.fileCnt || 0);
  9341. }
  9342. }
  9343. if (dirCnt !== false) {
  9344. if (typeof data.dirCnt === 'undefined') {
  9345. dirCnt = false;
  9346. } else {
  9347. dirCnt += parseInt(data.dirCnt || 0);
  9348. }
  9349. }
  9350. }
  9351. changed.length && self.change({changed: changed});
  9352. if (dirCnt !== false){
  9353. cnts.push(self.i18n('folders') + ': ' + (dirCnt - (tgtlen > 1? 0 : 1)));
  9354. }
  9355. if (fileCnt !== false){
  9356. cnts.push(self.i18n('files') + ': ' + fileCnt);
  9357. }
  9358. if (cnts.length) {
  9359. cntsTxt = '<br>' + cnts.join(', ');
  9360. }
  9361. dfrd.resolve({
  9362. size: size,
  9363. fileCnt: fileCnt,
  9364. dirCnt: dirCnt,
  9365. formated: (size >= 0 ? self.formatSize(size) : self.i18n('unknown')) + cntsTxt
  9366. });
  9367. });
  9368. self.autoSync();
  9369. });
  9370. return dfrd;
  9371. },
  9372. /**
  9373. * Worker Object URL for Blob URL of getWorker()
  9374. */
  9375. wkObjUrl : null,
  9376. /**
  9377. * Gets the web worker.
  9378. *
  9379. * @param {Object} options The options
  9380. * @return {Worker} The worker.
  9381. */
  9382. getWorker : function(options){
  9383. // for to make blob URL
  9384. function woker() {
  9385. self.onmessage = function(e) {
  9386. var d = e.data;
  9387. try {
  9388. self.data = d.data;
  9389. if (d.scripts) {
  9390. for(var i = 0; i < d.scripts.length; i++) {
  9391. importScripts(d.scripts[i]);
  9392. }
  9393. }
  9394. self.postMessage(self.res);
  9395. } catch (e) {
  9396. self.postMessage({error: e.toString()});
  9397. }
  9398. };
  9399. }
  9400. // get woker
  9401. var wk;
  9402. try {
  9403. if (!this.wkObjUrl) {
  9404. this.wkObjUrl = (window.URL || window.webkitURL).createObjectURL(new Blob(
  9405. [woker.toString().replace(/\s+/g, ' ').replace(/ *([^\w]) */g, '$1').replace(/^function\b.+?\{|\}$/g, '')],
  9406. { type:'text/javascript' }
  9407. ));
  9408. }
  9409. wk = new Worker(this.wkObjUrl, options);
  9410. } catch(e) {
  9411. this.debug('error', e.toString());
  9412. }
  9413. return wk;
  9414. },
  9415. /**
  9416. * Get worker absolute URL by filename
  9417. *
  9418. * @param {string} filename The filename
  9419. * @return {<type>} The worker url.
  9420. */
  9421. getWorkerUrl : function(filename) {
  9422. return this.convAbsUrl(this.baseUrl + 'js/worker/' + filename);
  9423. },
  9424. /**
  9425. * Gets the theme object by settings of options.themes
  9426. *
  9427. * @param String themeid The themeid
  9428. * @return Object jQuery.Deferred
  9429. */
  9430. getTheme : function(themeid) {
  9431. var self = this,
  9432. dfd = $.Deferred(),
  9433. absUrl = function(url, base) {
  9434. if (!base) {
  9435. base = self.convAbsUrl(self.baseUrl);
  9436. }
  9437. if (Array.isArray(url)) {
  9438. return $.map(url, function(v) {
  9439. return absUrl(v, base);
  9440. });
  9441. } else {
  9442. return url.match(/^(?:http|\/\/)/i)? url : base + url.replace(/^(?:\.\/|\/)/, '');
  9443. }
  9444. },
  9445. themeObj, m;
  9446. if (themeid && (themeObj = self.options.themes[themeid])) {
  9447. if (typeof themeObj === 'string') {
  9448. url = absUrl(themeObj);
  9449. if (m = url.match(/^(.+\/)[^/]+\.json$/i)) {
  9450. $.getJSON(url).done(function(data) {
  9451. themeObj = data;
  9452. themeObj.id = themeid;
  9453. if (themeObj.cssurls) {
  9454. themeObj.cssurls = absUrl(themeObj.cssurls, m[1]);
  9455. }
  9456. dfd.resolve(themeObj);
  9457. }).fail(function() {
  9458. dfd.reject();
  9459. });
  9460. } else {
  9461. dfd.resolve({
  9462. id: themeid,
  9463. name: themeid,
  9464. cssurls: [url]
  9465. });
  9466. }
  9467. } else if ($.isPlainObject(themeObj) && themeObj.cssurls) {
  9468. themeObj.id = themeid;
  9469. themeObj.cssurls = absUrl(themeObj.cssurls);
  9470. if (!Array.isArray(themeObj.cssurls)) {
  9471. themeObj.cssurls = [themeObj.cssurls];
  9472. }
  9473. if (!themeObj.name) {
  9474. themeObj.name = themeid;
  9475. }
  9476. dfd.resolve(themeObj);
  9477. } else {
  9478. dfd.reject();
  9479. }
  9480. } else {
  9481. dfd.reject();
  9482. }
  9483. return dfd;
  9484. },
  9485. /**
  9486. * Change current theme
  9487. *
  9488. * @param String themeid The themeid
  9489. * @return Object this elFinder instance
  9490. */
  9491. changeTheme : function(themeid) {
  9492. var self = this;
  9493. if (themeid) {
  9494. if (self.options.themes[themeid] && (!self.theme || self.theme.id !== themeid)) {
  9495. self.getTheme(themeid).done(function(themeObj) {
  9496. if (themeObj.cssurls) {
  9497. $('head>link.elfinder-theme-ext').remove();
  9498. self.loadCss(themeObj.cssurls, {
  9499. className: 'elfinder-theme-ext',
  9500. dfd: $.Deferred().done(function() {
  9501. self.theme = themeObj;
  9502. self.trigger && self.trigger('themechange');
  9503. })
  9504. });
  9505. }
  9506. });
  9507. } else if (themeid === 'default' && self.theme && self.theme.id !== 'default') {
  9508. $('head>link.elfinder-theme-ext').remove();
  9509. self.theme = null;
  9510. self.trigger && self.trigger('themechange');
  9511. }
  9512. }
  9513. return this;
  9514. },
  9515. /**
  9516. * Apply leaf root stats to target directory
  9517. *
  9518. * @param object dir object of target directory
  9519. * @param boolean update is force update
  9520. *
  9521. * @return boolean dir object was chenged
  9522. */
  9523. applyLeafRootStats : function(dir, update) {
  9524. var self = this,
  9525. prev = update? dir : (self.file(dir.hash) || dir),
  9526. prevTs = prev.ts,
  9527. change = false;
  9528. // backup original stats
  9529. if (update || !dir._realStats) {
  9530. dir._realStats = {
  9531. locked: dir.locked || 0,
  9532. dirs: dir.dirs || 0,
  9533. ts: dir.ts
  9534. };
  9535. }
  9536. // set lock
  9537. dir.locked = 1;
  9538. if (!prev.locked) {
  9539. change = true;
  9540. }
  9541. // has leaf root to `dirs: 1`
  9542. dir.dirs = 1;
  9543. if (!prev.dirs) {
  9544. change = true;
  9545. }
  9546. // set ts
  9547. $.each(self.leafRoots[dir.hash], function() {
  9548. var f = self.file(this);
  9549. if (f && f.ts && (dir.ts || 0) < f.ts) {
  9550. dir.ts = f.ts;
  9551. }
  9552. });
  9553. if (prevTs !== dir.ts) {
  9554. change = true;
  9555. }
  9556. return change;
  9557. },
  9558. /**
  9559. * To aborted XHR object
  9560. *
  9561. * @param Object xhr
  9562. * @param Object opts
  9563. *
  9564. * @return void
  9565. */
  9566. abortXHR : function(xhr, o) {
  9567. var opts = o || {};
  9568. if (xhr) {
  9569. opts.quiet && (xhr.quiet = true);
  9570. if (opts.abort && xhr._requestId) {
  9571. this.request({
  9572. data: {
  9573. cmd: 'abort',
  9574. id: xhr._requestId
  9575. },
  9576. preventDefault: true
  9577. });
  9578. }
  9579. xhr.abort();
  9580. xhr = void 0;
  9581. }
  9582. },
  9583. /**
  9584. * Sets the custom header by xhr response header with options.parrotHeaders
  9585. *
  9586. * @param Object xhr
  9587. *
  9588. * @return void
  9589. */
  9590. setCustomHeaderByXhr : function(xhr) {
  9591. var self = this;
  9592. if (xhr.getResponseHeader && self.parrotHeaders && self.parrotHeaders.length) {
  9593. $.each(self.parrotHeaders, function(i, h) {
  9594. var val = xhr.getResponseHeader(h);
  9595. if (val) {
  9596. self.customHeaders[h] = val;
  9597. self.sessionStorage('core-ph:'+h, val);
  9598. } else if (typeof val === 'string') {
  9599. delete self.customHeaders[h];
  9600. self.sessionStorage('core-ph:'+h, null);
  9601. }
  9602. });
  9603. }
  9604. },
  9605. /**
  9606. * Determines if parrot headers.
  9607. *
  9608. * @return {boolean} True if parrot headers, False otherwise.
  9609. */
  9610. hasParrotHeaders : function() {
  9611. var res = false,
  9612. phs = this.parrotHeaders;
  9613. if (Object.keys(this.customHeaders).length) {
  9614. for (var i = 0; i < phs.length; i++) {
  9615. if (this.customHeaders[phs[i]]) {
  9616. res = true;
  9617. break;
  9618. }
  9619. }
  9620. }
  9621. return res;
  9622. },
  9623. /**
  9624. * Gets the request identifier
  9625. *
  9626. * @return String The request identifier.
  9627. */
  9628. getRequestId : function() {
  9629. return (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16);
  9630. },
  9631. /**
  9632. * Flip key and value of array or object
  9633. *
  9634. * @param Array | Object { a: 1, b: 1, c: 2 }
  9635. * @param Mixed Static value
  9636. * @return Object { 1: "b", 2: "c" }
  9637. */
  9638. arrayFlip : function (trans, val) {
  9639. var key,
  9640. tmpArr = {},
  9641. isArr = $.isArray(trans);
  9642. for (key in trans) {
  9643. if (isArr || trans.hasOwnProperty(key)) {
  9644. tmpArr[trans[key]] = val || key;
  9645. }
  9646. }
  9647. return tmpArr;
  9648. },
  9649. /**
  9650. * Return array ["name without extention", "extention"]
  9651. *
  9652. * @param String name
  9653. *
  9654. * @return Array
  9655. *
  9656. */
  9657. splitFileExtention : function(name) {
  9658. var m;
  9659. if (m = name.match(/^(.+?)?\.((?:tar\.(?:gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(?:gz|bz2)|[a-z0-9]{1,10})$/i)) {
  9660. if (typeof m[1] === 'undefined') {
  9661. m[1] = '';
  9662. }
  9663. return [m[1], m[2]];
  9664. } else {
  9665. return [name, ''];
  9666. }
  9667. },
  9668. /**
  9669. * Slice the ArrayBuffer by sliceSize
  9670. *
  9671. * @param arraybuffer arrayBuffer The array buffer
  9672. * @param Number sliceSize The slice size
  9673. * @return Array Array of sleced arraybuffer
  9674. */
  9675. sliceArrayBuffer : function(arrayBuffer, sliceSize) {
  9676. var segments= [],
  9677. fi = 0;
  9678. while(fi * sliceSize < arrayBuffer.byteLength){
  9679. segments.push(arrayBuffer.slice(fi * sliceSize, (fi + 1) * sliceSize));
  9680. fi++;
  9681. }
  9682. return segments;
  9683. },
  9684. arrayBufferToBase64 : function(ab) {
  9685. if (!window.btoa) {
  9686. return '';
  9687. }
  9688. var dView = new Uint8Array(ab), // Get a byte view
  9689. arr = Array.prototype.slice.call(dView), // Create a normal array
  9690. arr1 = arr.map(function(item) {
  9691. return String.fromCharCode(item); // Convert
  9692. });
  9693. return window.btoa(arr1.join('')); // Form a string
  9694. },
  9695. log : function(m) { window.console && window.console.log && window.console.log(m); return this; },
  9696. debug : function(type, m) {
  9697. var self = this,
  9698. d = this.options.debug,
  9699. tb = this.options.toastBackendWarn,
  9700. tbOpts, showlog;
  9701. if (type === 'backend-error') {
  9702. if (! this.cwd().hash || (d && (d === 'all' || d['backend-error']))) {
  9703. m = Array.isArray(m)? m : [ m ];
  9704. this.error(m);
  9705. }
  9706. } else if (type === 'backend-warning') {
  9707. showlog = true;
  9708. if (tb) {
  9709. tbOpts = $.isPlainObject(tb)? tb : {};
  9710. $.each(Array.isArray(m)? m : [ m ], function(i, m) {
  9711. self.toast(Object.assign({
  9712. mode : 'warning',
  9713. msg: m
  9714. }, tbOpts));
  9715. });
  9716. }
  9717. } else if (type === 'backend-debug') {
  9718. this.trigger('backenddebug', m);
  9719. }
  9720. if (showlog || (d && (d === 'all' || d[type]))) {
  9721. window.console && window.console.log && window.console.log('elfinder debug: ['+type+'] ['+this.id+']', m);
  9722. }
  9723. return this;
  9724. },
  9725. /**
  9726. * Parse response.debug and trigger debug
  9727. *
  9728. * @param Object response The response
  9729. */
  9730. responseDebug : function(response) {
  9731. var rd = response.debug,
  9732. d;
  9733. if (rd) {
  9734. // set options.debug
  9735. d = this.options.debug;
  9736. if (!d || d !== 'all') {
  9737. if (!d) {
  9738. d = this.options.debug = {};
  9739. }
  9740. d['backend-error'] = true;
  9741. d['warning'] = true;
  9742. }
  9743. if (rd.mountErrors && (typeof rd.mountErrors === 'string' || (Array.isArray(rd.mountErrors) && rd.mountErrors.length))) {
  9744. this.debug('backend-error', rd.mountErrors);
  9745. }
  9746. if (rd.backendErrors && (typeof rd.backendErrors === 'string' || (Array.isArray(rd.backendErrors) && rd.backendErrors.length))) {
  9747. this.debug('backend-warning', rd.backendErrors);
  9748. }
  9749. }
  9750. },
  9751. time : function(l) { window.console && window.console.time && window.console.time(l); },
  9752. timeEnd : function(l) { window.console && window.console.timeEnd && window.console.timeEnd(l); }
  9753. };
  9754. /**
  9755. * for conpat ex. ie8...
  9756. *
  9757. * Object.keys() - JavaScript | MDN
  9758. * https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
  9759. */
  9760. if (!Object.keys) {
  9761. Object.keys = (function () {
  9762. var hasOwnProperty = Object.prototype.hasOwnProperty,
  9763. hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
  9764. dontEnums = [
  9765. 'toString',
  9766. 'toLocaleString',
  9767. 'valueOf',
  9768. 'hasOwnProperty',
  9769. 'isPrototypeOf',
  9770. 'propertyIsEnumerable',
  9771. 'constructor'
  9772. ],
  9773. dontEnumsLength = dontEnums.length;
  9774. return function (obj) {
  9775. if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object');
  9776. var result = [];
  9777. for (var prop in obj) {
  9778. if (hasOwnProperty.call(obj, prop)) result.push(prop);
  9779. }
  9780. if (hasDontEnumBug) {
  9781. for (var i=0; i < dontEnumsLength; i++) {
  9782. if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
  9783. }
  9784. }
  9785. return result;
  9786. };
  9787. })();
  9788. }
  9789. // Array.isArray
  9790. if (!Array.isArray) {
  9791. Array.isArray = function(arr) {
  9792. return jQuery.isArray(arr);
  9793. };
  9794. }
  9795. // Object.assign
  9796. if (!Object.assign) {
  9797. Object.assign = function() {
  9798. return jQuery.extend.apply(null, arguments);
  9799. };
  9800. }
  9801. // String.repeat
  9802. if (!String.prototype.repeat) {
  9803. String.prototype.repeat = function(count) {
  9804. 'use strict';
  9805. if (this == null) {
  9806. throw new TypeError('can\'t convert ' + this + ' to object');
  9807. }
  9808. var str = '' + this;
  9809. count = +count;
  9810. if (count != count) {
  9811. count = 0;
  9812. }
  9813. if (count < 0) {
  9814. throw new RangeError('repeat count must be non-negative');
  9815. }
  9816. if (count == Infinity) {
  9817. throw new RangeError('repeat count must be less than infinity');
  9818. }
  9819. count = Math.floor(count);
  9820. if (str.length == 0 || count == 0) {
  9821. return '';
  9822. }
  9823. // Ensuring count is a 31-bit integer allows us to heavily optimize the
  9824. // main part. But anyway, most current (August 2014) browsers can't handle
  9825. // strings 1 << 28 chars or longer, so:
  9826. if (str.length * count >= 1 << 28) {
  9827. throw new RangeError('repeat count must not overflow maximum string size');
  9828. }
  9829. var rpt = '';
  9830. for (var i = 0; i < count; i++) {
  9831. rpt += str;
  9832. }
  9833. return rpt;
  9834. };
  9835. }
  9836. // String.trim
  9837. if (!String.prototype.trim) {
  9838. String.prototype.trim = function() {
  9839. return this.replace(/^\s+|\s+$/g, '');
  9840. };
  9841. }
  9842. // Array.apply
  9843. (function () {
  9844. try {
  9845. Array.apply(null, {});
  9846. return;
  9847. } catch (e) { }
  9848. var toString = Object.prototype.toString,
  9849. arrayType = '[object Array]',
  9850. _apply = Function.prototype.apply,
  9851. slice = /*@cc_on @if (@_jscript_version <= 5.8)
  9852. function () {
  9853. var a = [], i = this.length;
  9854. while (i-- > 0) a[i] = this[i];
  9855. return a;
  9856. }@else@*/Array.prototype.slice/*@end@*/;
  9857. Function.prototype.apply = function apply(thisArg, argArray) {
  9858. return _apply.call(this, thisArg,
  9859. toString.call(argArray) === arrayType ? argArray : slice.call(argArray));
  9860. };
  9861. })();
  9862. // Array.from
  9863. if (!Array.from) {
  9864. Array.from = function(obj) {
  9865. return obj.length === 1 ? [obj[0]] : Array.apply(null, obj);
  9866. };
  9867. }
  9868. // window.requestAnimationFrame and window.cancelAnimationFrame
  9869. if (!window.cancelAnimationFrame) {
  9870. // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
  9871. // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
  9872. // requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
  9873. // MIT license
  9874. (function() {
  9875. var lastTime = 0;
  9876. var vendors = ['ms', 'moz', 'webkit', 'o'];
  9877. for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
  9878. window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
  9879. window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
  9880. || window[vendors[x]+'CancelRequestAnimationFrame'];
  9881. }
  9882. if (!window.requestAnimationFrame)
  9883. window.requestAnimationFrame = function(callback, element) {
  9884. var currTime = new Date().getTime();
  9885. var timeToCall = Math.max(0, 16 - (currTime - lastTime));
  9886. var id = window.setTimeout(function() { callback(currTime + timeToCall); },
  9887. timeToCall);
  9888. lastTime = currTime + timeToCall;
  9889. return id;
  9890. };
  9891. if (!window.cancelAnimationFrame)
  9892. window.cancelAnimationFrame = function(id) {
  9893. clearTimeout(id);
  9894. };
  9895. }());
  9896. }
  9897. /*
  9898. * File: /js/elFinder.version.js
  9899. */
  9900. /**
  9901. * Application version
  9902. *
  9903. * @type String
  9904. **/
  9905. elFinder.prototype.version = '2.1.57';
  9906. /*
  9907. * File: /js/jquery.elfinder.js
  9908. */
  9909. /*** jQuery UI droppable performance tune for elFinder ***/
  9910. (function(){
  9911. if ($.ui) {
  9912. if ($.ui.ddmanager) {
  9913. var origin = $.ui.ddmanager.prepareOffsets;
  9914. $.ui.ddmanager.prepareOffsets = function( t, event ) {
  9915. var isOutView = function(elem) {
  9916. if (elem.is(':hidden')) {
  9917. return true;
  9918. }
  9919. var rect = elem[0].getBoundingClientRect();
  9920. return document.elementFromPoint(rect.left, rect.top) || document.elementFromPoint(rect.left + rect.width, rect.top + rect.height)? false : true;
  9921. };
  9922. if (event.type === 'mousedown' || t.options.elfRefresh) {
  9923. var i, d,
  9924. m = $.ui.ddmanager.droppables[ t.options.scope ] || [],
  9925. l = m.length;
  9926. for ( i = 0; i < l; i++ ) {
  9927. d = m[ i ];
  9928. if (d.options.autoDisable && (!d.options.disabled || d.options.autoDisable > 1)) {
  9929. d.options.disabled = isOutView(d.element);
  9930. d.options.autoDisable = d.options.disabled? 2 : 1;
  9931. }
  9932. }
  9933. }
  9934. // call origin function
  9935. return origin( t, event );
  9936. };
  9937. }
  9938. }
  9939. })();
  9940. /**
  9941. *
  9942. * jquery.binarytransport
  9943. *
  9944. * @description. jQuery ajax transport for making binary data type requests.
  9945. *
  9946. */
  9947. (function($, undefined) {
  9948. // use this transport for "binary" data type
  9949. $.ajaxTransport("+binary", function(options, originalOptions, jqXHR) {
  9950. // check for conditions and support for blob / arraybuffer response type
  9951. if (window.FormData && ((options.dataType && (options.dataType == 'binary')) || (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || (window.Blob && options.data instanceof Blob))))) {
  9952. var callback;
  9953. // Cross domain only allowed if supported through XMLHttpRequest
  9954. return {
  9955. send: function( headers, complete ) {
  9956. var i,
  9957. dataType = options.responseType || "blob",
  9958. xhr = options.xhr();
  9959. xhr.open(
  9960. options.type,
  9961. options.url,
  9962. options.async,
  9963. options.username,
  9964. options.password
  9965. );
  9966. // Apply custom fields if provided
  9967. if ( options.xhrFields ) {
  9968. for ( i in options.xhrFields ) {
  9969. xhr[ i ] = options.xhrFields[ i ];
  9970. }
  9971. }
  9972. // Override mime type if needed
  9973. if ( options.mimeType && xhr.overrideMimeType ) {
  9974. xhr.overrideMimeType( options.mimeType );
  9975. }
  9976. // X-Requested-With header
  9977. // For cross-domain requests, seeing as conditions for a preflight are
  9978. // akin to a jigsaw puzzle, we simply never set it to be sure.
  9979. // (it can always be set on a per-request basis or even using ajaxSetup)
  9980. // For same-domain requests, won't change header if already provided.
  9981. if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) {
  9982. headers[ "X-Requested-With" ] = "XMLHttpRequest";
  9983. }
  9984. // Set headers
  9985. for ( i in headers ) {
  9986. xhr.setRequestHeader( i, headers[ i ] );
  9987. }
  9988. // Callback
  9989. callback = function( type ) {
  9990. return function() {
  9991. if ( callback ) {
  9992. callback = xhr.onload = xhr.onerror = xhr.onabort = xhr.ontimeout = null;
  9993. if ( type === "abort" ) {
  9994. xhr.abort();
  9995. } else if ( type === "error" ) {
  9996. complete(
  9997. xhr.status,
  9998. xhr.statusText
  9999. );
  10000. } else {
  10001. var data = {};
  10002. data[options.dataType] = xhr.response;
  10003. complete(
  10004. xhr.status,
  10005. xhr.statusText,
  10006. data,
  10007. xhr.getAllResponseHeaders()
  10008. );
  10009. }
  10010. }
  10011. };
  10012. };
  10013. // Listen to events
  10014. xhr.onload = callback();
  10015. xhr.onabort = xhr.onerror = xhr.ontimeout = callback( "error" );
  10016. // Create the abort callback
  10017. callback = callback( "abort" );
  10018. try {
  10019. xhr.responseType = dataType;
  10020. // Do send the request (this may raise an exception)
  10021. xhr.send( options.data || null );
  10022. } catch ( e ) {
  10023. if ( callback ) {
  10024. throw e;
  10025. }
  10026. }
  10027. },
  10028. abort: function() {
  10029. if ( callback ) {
  10030. callback();
  10031. }
  10032. }
  10033. };
  10034. }
  10035. });
  10036. })(window.jQuery);
  10037. /*!
  10038. * jQuery UI Touch Punch 0.2.3
  10039. *
  10040. * Copyright 2011–2014, Dave Furfero
  10041. * Dual licensed under the MIT or GPL Version 2 licenses.
  10042. *
  10043. * Depends:
  10044. * jquery.ui.widget.js
  10045. * jquery.ui.mouse.js
  10046. */
  10047. (function ($) {
  10048. // Detect touch support
  10049. $.support.touch = 'ontouchend' in document;
  10050. // Ignore browsers without touch support
  10051. if (!$.support.touch) {
  10052. return;
  10053. }
  10054. var mouseProto = $.ui.mouse.prototype,
  10055. _mouseInit = mouseProto._mouseInit,
  10056. _mouseDestroy = mouseProto._mouseDestroy,
  10057. touchHandled,
  10058. posX, posY;
  10059. /**
  10060. * Simulate a mouse event based on a corresponding touch event
  10061. * @param {Object} event A touch event
  10062. * @param {String} simulatedType The corresponding mouse event
  10063. */
  10064. function simulateMouseEvent (event, simulatedType) {
  10065. // Ignore multi-touch events
  10066. if (event.originalEvent.touches.length > 1) {
  10067. return;
  10068. }
  10069. if (! $(event.currentTarget).hasClass('touch-punch-keep-default')) {
  10070. event.preventDefault();
  10071. }
  10072. var touch = event.originalEvent.changedTouches[0],
  10073. simulatedEvent = document.createEvent('MouseEvents');
  10074. // Initialize the simulated mouse event using the touch event's coordinates
  10075. simulatedEvent.initMouseEvent(
  10076. simulatedType, // type
  10077. true, // bubbles
  10078. true, // cancelable
  10079. window, // view
  10080. 1, // detail
  10081. touch.screenX, // screenX
  10082. touch.screenY, // screenY
  10083. touch.clientX, // clientX
  10084. touch.clientY, // clientY
  10085. false, // ctrlKey
  10086. false, // altKey
  10087. false, // shiftKey
  10088. false, // metaKey
  10089. 0, // button
  10090. null // relatedTarget
  10091. );
  10092. // Dispatch the simulated event to the target element
  10093. event.target.dispatchEvent(simulatedEvent);
  10094. }
  10095. /**
  10096. * Handle the jQuery UI widget's touchstart events
  10097. * @param {Object} event The widget element's touchstart event
  10098. */
  10099. mouseProto._touchStart = function (event) {
  10100. var self = this;
  10101. // Ignore the event if another widget is already being handled
  10102. if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
  10103. return;
  10104. }
  10105. // Track element position to avoid "false" move
  10106. posX = event.originalEvent.changedTouches[0].screenX.toFixed(0);
  10107. posY = event.originalEvent.changedTouches[0].screenY.toFixed(0);
  10108. // Set the flag to prevent other widgets from inheriting the touch event
  10109. touchHandled = true;
  10110. // Track movement to determine if interaction was a click
  10111. self._touchMoved = false;
  10112. // Simulate the mouseover event
  10113. simulateMouseEvent(event, 'mouseover');
  10114. // Simulate the mousemove event
  10115. simulateMouseEvent(event, 'mousemove');
  10116. // Simulate the mousedown event
  10117. simulateMouseEvent(event, 'mousedown');
  10118. };
  10119. /**
  10120. * Handle the jQuery UI widget's touchmove events
  10121. * @param {Object} event The document's touchmove event
  10122. */
  10123. mouseProto._touchMove = function (event) {
  10124. // Ignore event if not handled
  10125. if (!touchHandled) {
  10126. return;
  10127. }
  10128. // Ignore if it's a "false" move (position not changed)
  10129. var x = event.originalEvent.changedTouches[0].screenX.toFixed(0);
  10130. var y = event.originalEvent.changedTouches[0].screenY.toFixed(0);
  10131. // Ignore if it's a "false" move (position not changed)
  10132. if (Math.abs(posX - x) <= 4 && Math.abs(posY - y) <= 4) {
  10133. return;
  10134. }
  10135. // Interaction was not a click
  10136. this._touchMoved = true;
  10137. // Simulate the mousemove event
  10138. simulateMouseEvent(event, 'mousemove');
  10139. };
  10140. /**
  10141. * Handle the jQuery UI widget's touchend events
  10142. * @param {Object} event The document's touchend event
  10143. */
  10144. mouseProto._touchEnd = function (event) {
  10145. // Ignore event if not handled
  10146. if (!touchHandled) {
  10147. return;
  10148. }
  10149. // Simulate the mouseup event
  10150. simulateMouseEvent(event, 'mouseup');
  10151. // Simulate the mouseout event
  10152. simulateMouseEvent(event, 'mouseout');
  10153. // If the touch interaction did not move, it should trigger a click
  10154. if (!this._touchMoved) {
  10155. // Simulate the click event
  10156. simulateMouseEvent(event, 'click');
  10157. }
  10158. // Unset the flag to allow other widgets to inherit the touch event
  10159. touchHandled = false;
  10160. this._touchMoved = false;
  10161. };
  10162. /**
  10163. * A duck punch of the $.ui.mouse _mouseInit method to support touch events.
  10164. * This method extends the widget with bound touch event handlers that
  10165. * translate touch events to mouse events and pass them to the widget's
  10166. * original mouse event handling methods.
  10167. */
  10168. mouseProto._mouseInit = function () {
  10169. var self = this;
  10170. if (self.element.hasClass('touch-punch')) {
  10171. // Delegate the touch handlers to the widget's element
  10172. self.element.on({
  10173. touchstart: $.proxy(self, '_touchStart'),
  10174. touchmove: $.proxy(self, '_touchMove'),
  10175. touchend: $.proxy(self, '_touchEnd')
  10176. });
  10177. }
  10178. // Call the original $.ui.mouse init method
  10179. _mouseInit.call(self);
  10180. };
  10181. /**
  10182. * Remove the touch event handlers
  10183. */
  10184. mouseProto._mouseDestroy = function () {
  10185. var self = this;
  10186. if (self.element.hasClass('touch-punch')) {
  10187. // Delegate the touch handlers to the widget's element
  10188. self.element.off({
  10189. touchstart: $.proxy(self, '_touchStart'),
  10190. touchmove: $.proxy(self, '_touchMove'),
  10191. touchend: $.proxy(self, '_touchEnd')
  10192. });
  10193. }
  10194. // Call the original $.ui.mouse destroy method
  10195. _mouseDestroy.call(self);
  10196. };
  10197. })(jQuery);
  10198. $.fn.elfinder = function(o, o2) {
  10199. if (o === 'instance') {
  10200. return this.getElFinder();
  10201. } else if (o === 'ondemand') {
  10202. }
  10203. return this.each(function() {
  10204. var cmd = typeof o === 'string' ? o : '',
  10205. bootCallback = typeof o2 === 'function'? o2 : void(0),
  10206. elfinder = this.elfinder,
  10207. opts, reloadCallback;
  10208. if (!elfinder) {
  10209. if ($.isPlainObject(o)) {
  10210. new elFinder(this, o, bootCallback);
  10211. }
  10212. } else {
  10213. switch(cmd) {
  10214. case 'close':
  10215. case 'hide':
  10216. elfinder.hide();
  10217. break;
  10218. case 'open':
  10219. case 'show':
  10220. elfinder.show();
  10221. break;
  10222. case 'destroy':
  10223. elfinder.destroy();
  10224. break;
  10225. case 'reload':
  10226. case 'restart':
  10227. if (elfinder) {
  10228. opts = $.extend(true, elfinder.options, $.isPlainObject(o2)? o2 : {});
  10229. bootCallback = elfinder.bootCallback;
  10230. if (elfinder.reloadCallback && $.isFunction(elfinder.reloadCallback)) {
  10231. elfinder.reloadCallback(opts, bootCallback);
  10232. } else {
  10233. elfinder.destroy();
  10234. new elFinder(this, opts, bootCallback);
  10235. }
  10236. }
  10237. break;
  10238. }
  10239. }
  10240. });
  10241. };
  10242. $.fn.getElFinder = function() {
  10243. var instance;
  10244. this.each(function() {
  10245. if (this.elfinder) {
  10246. instance = this.elfinder;
  10247. return false;
  10248. }
  10249. });
  10250. return instance;
  10251. };
  10252. $.fn.elfUiWidgetInstance = function(name) {
  10253. try {
  10254. return this[name]('instance');
  10255. } catch(e) {
  10256. // fallback for jQuery UI < 1.11
  10257. var data = this.data('ui-' + name);
  10258. if (data && typeof data === 'object' && data.widgetFullName === 'ui-' + name) {
  10259. return data;
  10260. }
  10261. return null;
  10262. }
  10263. };
  10264. // function scrollRight
  10265. if (! $.fn.scrollRight) {
  10266. $.fn.extend({
  10267. scrollRight: function (val) {
  10268. var node = this.get(0);
  10269. if (val === undefined) {
  10270. return Math.max(0, node.scrollWidth - (node.scrollLeft + node.clientWidth));
  10271. }
  10272. return this.scrollLeft(node.scrollWidth - node.clientWidth - val);
  10273. }
  10274. });
  10275. }
  10276. // function scrollBottom
  10277. if (! $.fn.scrollBottom) {
  10278. $.fn.extend({
  10279. scrollBottom: function(val) {
  10280. var node = this.get(0);
  10281. if (val === undefined) {
  10282. return Math.max(0, node.scrollHeight - (node.scrollTop + node.clientHeight));
  10283. }
  10284. return this.scrollTop(node.scrollHeight - node.clientHeight - val);
  10285. }
  10286. });
  10287. }
  10288. /*
  10289. * File: /js/elFinder.mimetypes.js
  10290. */
  10291. elFinder.prototype.mimeTypes = {"application\/x-executable":"exe","application\/x-jar":"jar","application\/x-gzip":"gz","application\/x-bzip2":"tbz","application\/x-rar":"rar","text\/x-php":"php","text\/javascript":"js","application\/rtfd":"rtfd","text\/x-python":"py","text\/x-ruby":"rb","text\/x-shellscript":"sh","text\/x-perl":"pl","text\/xml":"xml","text\/x-csrc":"c","text\/x-chdr":"h","text\/x-c++src":"cpp","text\/x-c++hdr":"hh","text\/x-markdown":"md","text\/x-yaml":"yml","image\/x-ms-bmp":"bmp","image\/x-targa":"tga","image\/xbm":"xbm","image\/pxm":"pxm","audio\/wav":"wav","video\/x-dv":"dv","video\/x-ms-wmv":"wm","video\/ogg":"ogm","video\/MP2T":"m2ts","application\/x-mpegURL":"m3u8","application\/dash+xml":"mpd","application\/andrew-inset":"ez","application\/applixware":"aw","application\/atom+xml":"atom","application\/atomcat+xml":"atomcat","application\/atomsvc+xml":"atomsvc","application\/ccxml+xml":"ccxml","application\/cdmi-capability":"cdmia","application\/cdmi-container":"cdmic","application\/cdmi-domain":"cdmid","application\/cdmi-object":"cdmio","application\/cdmi-queue":"cdmiq","application\/cu-seeme":"cu","application\/davmount+xml":"davmount","application\/docbook+xml":"dbk","application\/dssc+der":"dssc","application\/dssc+xml":"xdssc","application\/ecmascript":"ecma","application\/emma+xml":"emma","application\/epub+zip":"epub","application\/exi":"exi","application\/font-tdpfr":"pfr","application\/gml+xml":"gml","application\/gpx+xml":"gpx","application\/gxf":"gxf","application\/hyperstudio":"stk","application\/inkml+xml":"ink","application\/ipfix":"ipfix","application\/java-serialized-object":"ser","application\/java-vm":"class","application\/json":"json","application\/jsonml+json":"jsonml","application\/lost+xml":"lostxml","application\/mac-binhex40":"hqx","application\/mac-compactpro":"cpt","application\/mads+xml":"mads","application\/marc":"mrc","application\/marcxml+xml":"mrcx","application\/mathematica":"ma","application\/mathml+xml":"mathml","application\/mbox":"mbox","application\/mediaservercontrol+xml":"mscml","application\/metalink+xml":"metalink","application\/metalink4+xml":"meta4","application\/mets+xml":"mets","application\/mods+xml":"mods","application\/mp21":"m21","application\/mp4":"mp4s","application\/msword":"doc","application\/mxf":"mxf","application\/octet-stream":"bin","application\/oda":"oda","application\/oebps-package+xml":"opf","application\/ogg":"ogx","application\/omdoc+xml":"omdoc","application\/onenote":"onetoc","application\/oxps":"oxps","application\/patch-ops-error+xml":"xer","application\/pdf":"pdf","application\/pgp-encrypted":"pgp","application\/pgp-signature":"asc","application\/pics-rules":"prf","application\/pkcs10":"p10","application\/pkcs7-mime":"p7m","application\/pkcs7-signature":"p7s","application\/pkcs8":"p8","application\/pkix-attr-cert":"ac","application\/pkix-cert":"cer","application\/pkix-crl":"crl","application\/pkix-pkipath":"pkipath","application\/pkixcmp":"pki","application\/pls+xml":"pls","application\/postscript":"ai","application\/prs.cww":"cww","application\/pskc+xml":"pskcxml","application\/rdf+xml":"rdf","application\/reginfo+xml":"rif","application\/relax-ng-compact-syntax":"rnc","application\/resource-lists+xml":"rl","application\/resource-lists-diff+xml":"rld","application\/rls-services+xml":"rs","application\/rpki-ghostbusters":"gbr","application\/rpki-manifest":"mft","application\/rpki-roa":"roa","application\/rsd+xml":"rsd","application\/rss+xml":"rss","application\/rtf":"rtf","application\/sbml+xml":"sbml","application\/scvp-cv-request":"scq","application\/scvp-cv-response":"scs","application\/scvp-vp-request":"spq","application\/scvp-vp-response":"spp","application\/sdp":"sdp","application\/set-payment-initiation":"setpay","application\/set-registration-initiation":"setreg","application\/shf+xml":"shf","application\/smil+xml":"smi","application\/sparql-query":"rq","application\/sparql-results+xml":"srx","application\/srgs":"gram","application\/srgs+xml":"grxml","application\/sru+xml":"sru","application\/ssdl+xml":"ssdl","application\/ssml+xml":"ssml","application\/tei+xml":"tei","application\/thraud+xml":"tfi","application\/timestamped-data":"tsd","application\/vnd.3gpp.pic-bw-large":"plb","application\/vnd.3gpp.pic-bw-small":"psb","application\/vnd.3gpp.pic-bw-var":"pvb","application\/vnd.3gpp2.tcap":"tcap","application\/vnd.3m.post-it-notes":"pwn","application\/vnd.accpac.simply.aso":"aso","application\/vnd.accpac.simply.imp":"imp","application\/vnd.acucobol":"acu","application\/vnd.acucorp":"atc","application\/vnd.adobe.air-application-installer-package+zip":"air","application\/vnd.adobe.formscentral.fcdt":"fcdt","application\/vnd.adobe.fxp":"fxp","application\/vnd.adobe.xdp+xml":"xdp","application\/vnd.adobe.xfdf":"xfdf","application\/vnd.ahead.space":"ahead","application\/vnd.airzip.filesecure.azf":"azf","application\/vnd.airzip.filesecure.azs":"azs","application\/vnd.amazon.ebook":"azw","application\/vnd.americandynamics.acc":"acc","application\/vnd.amiga.ami":"ami","application\/vnd.android.package-archive":"apk","application\/vnd.anser-web-certificate-issue-initiation":"cii","application\/vnd.anser-web-funds-transfer-initiation":"fti","application\/vnd.antix.game-component":"atx","application\/vnd.apple.installer+xml":"mpkg","application\/vnd.aristanetworks.swi":"swi","application\/vnd.astraea-software.iota":"iota","application\/vnd.audiograph":"aep","application\/vnd.blueice.multipass":"mpm","application\/vnd.bmi":"bmi","application\/vnd.businessobjects":"rep","application\/vnd.chemdraw+xml":"cdxml","application\/vnd.chipnuts.karaoke-mmd":"mmd","application\/vnd.cinderella":"cdy","application\/vnd.claymore":"cla","application\/vnd.cloanto.rp9":"rp9","application\/vnd.clonk.c4group":"c4g","application\/vnd.cluetrust.cartomobile-config":"c11amc","application\/vnd.cluetrust.cartomobile-config-pkg":"c11amz","application\/vnd.commonspace":"csp","application\/vnd.contact.cmsg":"cdbcmsg","application\/vnd.cosmocaller":"cmc","application\/vnd.crick.clicker":"clkx","application\/vnd.crick.clicker.keyboard":"clkk","application\/vnd.crick.clicker.palette":"clkp","application\/vnd.crick.clicker.template":"clkt","application\/vnd.crick.clicker.wordbank":"clkw","application\/vnd.criticaltools.wbs+xml":"wbs","application\/vnd.ctc-posml":"pml","application\/vnd.cups-ppd":"ppd","application\/vnd.curl.car":"car","application\/vnd.curl.pcurl":"pcurl","application\/vnd.dart":"dart","application\/vnd.data-vision.rdz":"rdz","application\/vnd.dece.data":"uvf","application\/vnd.dece.ttml+xml":"uvt","application\/vnd.dece.unspecified":"uvx","application\/vnd.dece.zip":"uvz","application\/vnd.denovo.fcselayout-link":"fe_launch","application\/vnd.dna":"dna","application\/vnd.dolby.mlp":"mlp","application\/vnd.dpgraph":"dpg","application\/vnd.dreamfactory":"dfac","application\/vnd.ds-keypoint":"kpxx","application\/vnd.dvb.ait":"ait","application\/vnd.dvb.service":"svc","application\/vnd.dynageo":"geo","application\/vnd.ecowin.chart":"mag","application\/vnd.enliven":"nml","application\/vnd.epson.esf":"esf","application\/vnd.epson.msf":"msf","application\/vnd.epson.quickanime":"qam","application\/vnd.epson.salt":"slt","application\/vnd.epson.ssf":"ssf","application\/vnd.eszigno3+xml":"es3","application\/vnd.ezpix-album":"ez2","application\/vnd.ezpix-package":"ez3","application\/vnd.fdf":"fdf","application\/vnd.fdsn.mseed":"mseed","application\/vnd.fdsn.seed":"seed","application\/vnd.flographit":"gph","application\/vnd.fluxtime.clip":"ftc","application\/vnd.framemaker":"fm","application\/vnd.frogans.fnc":"fnc","application\/vnd.frogans.ltf":"ltf","application\/vnd.fsc.weblaunch":"fsc","application\/vnd.fujitsu.oasys":"oas","application\/vnd.fujitsu.oasys2":"oa2","application\/vnd.fujitsu.oasys3":"oa3","application\/vnd.fujitsu.oasysgp":"fg5","application\/vnd.fujitsu.oasysprs":"bh2","application\/vnd.fujixerox.ddd":"ddd","application\/vnd.fujixerox.docuworks":"xdw","application\/vnd.fujixerox.docuworks.binder":"xbd","application\/vnd.fuzzysheet":"fzs","application\/vnd.genomatix.tuxedo":"txd","application\/vnd.geogebra.file":"ggb","application\/vnd.geogebra.tool":"ggt","application\/vnd.geometry-explorer":"gex","application\/vnd.geonext":"gxt","application\/vnd.geoplan":"g2w","application\/vnd.geospace":"g3w","application\/vnd.gmx":"gmx","application\/vnd.google-earth.kml+xml":"kml","application\/vnd.google-earth.kmz":"kmz","application\/vnd.grafeq":"gqf","application\/vnd.groove-account":"gac","application\/vnd.groove-help":"ghf","application\/vnd.groove-identity-message":"gim","application\/vnd.groove-injector":"grv","application\/vnd.groove-tool-message":"gtm","application\/vnd.groove-tool-template":"tpl","application\/vnd.groove-vcard":"vcg","application\/vnd.hal+xml":"hal","application\/vnd.handheld-entertainment+xml":"zmm","application\/vnd.hbci":"hbci","application\/vnd.hhe.lesson-player":"les","application\/vnd.hp-hpgl":"hpgl","application\/vnd.hp-hpid":"hpid","application\/vnd.hp-hps":"hps","application\/vnd.hp-jlyt":"jlt","application\/vnd.hp-pcl":"pcl","application\/vnd.hp-pclxl":"pclxl","application\/vnd.hydrostatix.sof-data":"sfd-hdstx","application\/vnd.ibm.minipay":"mpy","application\/vnd.ibm.modcap":"afp","application\/vnd.ibm.rights-management":"irm","application\/vnd.ibm.secure-container":"sc","application\/vnd.iccprofile":"icc","application\/vnd.igloader":"igl","application\/vnd.immervision-ivp":"ivp","application\/vnd.immervision-ivu":"ivu","application\/vnd.insors.igm":"igm","application\/vnd.intercon.formnet":"xpw","application\/vnd.intergeo":"i2g","application\/vnd.intu.qbo":"qbo","application\/vnd.intu.qfx":"qfx","application\/vnd.ipunplugged.rcprofile":"rcprofile","application\/vnd.irepository.package+xml":"irp","application\/vnd.is-xpr":"xpr","application\/vnd.isac.fcs":"fcs","application\/vnd.jam":"jam","application\/vnd.jcp.javame.midlet-rms":"rms","application\/vnd.jisp":"jisp","application\/vnd.joost.joda-archive":"joda","application\/vnd.kahootz":"ktz","application\/vnd.kde.karbon":"karbon","application\/vnd.kde.kchart":"chrt","application\/vnd.kde.kformula":"kfo","application\/vnd.kde.kivio":"flw","application\/vnd.kde.kontour":"kon","application\/vnd.kde.kpresenter":"kpr","application\/vnd.kde.kspread":"ksp","application\/vnd.kde.kword":"kwd","application\/vnd.kenameaapp":"htke","application\/vnd.kidspiration":"kia","application\/vnd.kinar":"kne","application\/vnd.koan":"skp","application\/vnd.kodak-descriptor":"sse","application\/vnd.las.las+xml":"lasxml","application\/vnd.llamagraphics.life-balance.desktop":"lbd","application\/vnd.llamagraphics.life-balance.exchange+xml":"lbe","application\/vnd.lotus-1-2-3":123,"application\/vnd.lotus-approach":"apr","application\/vnd.lotus-freelance":"pre","application\/vnd.lotus-notes":"nsf","application\/vnd.lotus-organizer":"org","application\/vnd.lotus-screencam":"scm","application\/vnd.lotus-wordpro":"lwp","application\/vnd.macports.portpkg":"portpkg","application\/vnd.mcd":"mcd","application\/vnd.medcalcdata":"mc1","application\/vnd.mediastation.cdkey":"cdkey","application\/vnd.mfer":"mwf","application\/vnd.mfmp":"mfm","application\/vnd.micrografx.flo":"flo","application\/vnd.micrografx.igx":"igx","application\/vnd.mif":"mif","application\/vnd.mobius.daf":"daf","application\/vnd.mobius.dis":"dis","application\/vnd.mobius.mbk":"mbk","application\/vnd.mobius.mqy":"mqy","application\/vnd.mobius.msl":"msl","application\/vnd.mobius.plc":"plc","application\/vnd.mobius.txf":"txf","application\/vnd.mophun.application":"mpn","application\/vnd.mophun.certificate":"mpc","application\/vnd.mozilla.xul+xml":"xul","application\/vnd.ms-artgalry":"cil","application\/vnd.ms-cab-compressed":"cab","application\/vnd.ms-excel":"xls","application\/vnd.ms-excel.addin.macroenabled.12":"xlam","application\/vnd.ms-excel.sheet.binary.macroenabled.12":"xlsb","application\/vnd.ms-excel.sheet.macroenabled.12":"xlsm","application\/vnd.ms-excel.template.macroenabled.12":"xltm","application\/vnd.ms-fontobject":"eot","application\/vnd.ms-htmlhelp":"chm","application\/vnd.ms-ims":"ims","application\/vnd.ms-lrm":"lrm","application\/vnd.ms-officetheme":"thmx","application\/vnd.ms-pki.seccat":"cat","application\/vnd.ms-pki.stl":"stl","application\/vnd.ms-powerpoint":"ppt","application\/vnd.ms-powerpoint.addin.macroenabled.12":"ppam","application\/vnd.ms-powerpoint.presentation.macroenabled.12":"pptm","application\/vnd.ms-powerpoint.slide.macroenabled.12":"sldm","application\/vnd.ms-powerpoint.slideshow.macroenabled.12":"ppsm","application\/vnd.ms-powerpoint.template.macroenabled.12":"potm","application\/vnd.ms-project":"mpp","application\/vnd.ms-word.document.macroenabled.12":"docm","application\/vnd.ms-word.template.macroenabled.12":"dotm","application\/vnd.ms-works":"wps","application\/vnd.ms-wpl":"wpl","application\/vnd.ms-xpsdocument":"xps","application\/vnd.mseq":"mseq","application\/vnd.musician":"mus","application\/vnd.muvee.style":"msty","application\/vnd.mynfc":"taglet","application\/vnd.neurolanguage.nlu":"nlu","application\/vnd.nitf":"ntf","application\/vnd.noblenet-directory":"nnd","application\/vnd.noblenet-sealer":"nns","application\/vnd.noblenet-web":"nnw","application\/vnd.nokia.n-gage.data":"ngdat","application\/vnd.nokia.n-gage.symbian.install":"n-gage","application\/vnd.nokia.radio-preset":"rpst","application\/vnd.nokia.radio-presets":"rpss","application\/vnd.novadigm.edm":"edm","application\/vnd.novadigm.edx":"edx","application\/vnd.novadigm.ext":"ext","application\/vnd.oasis.opendocument.chart":"odc","application\/vnd.oasis.opendocument.chart-template":"otc","application\/vnd.oasis.opendocument.database":"odb","application\/vnd.oasis.opendocument.formula":"odf","application\/vnd.oasis.opendocument.formula-template":"odft","application\/vnd.oasis.opendocument.graphics":"odg","application\/vnd.oasis.opendocument.graphics-template":"otg","application\/vnd.oasis.opendocument.image":"odi","application\/vnd.oasis.opendocument.image-template":"oti","application\/vnd.oasis.opendocument.presentation":"odp","application\/vnd.oasis.opendocument.presentation-template":"otp","application\/vnd.oasis.opendocument.spreadsheet":"ods","application\/vnd.oasis.opendocument.spreadsheet-template":"ots","application\/vnd.oasis.opendocument.text":"odt","application\/vnd.oasis.opendocument.text-master":"odm","application\/vnd.oasis.opendocument.text-template":"ott","application\/vnd.oasis.opendocument.text-web":"oth","application\/vnd.olpc-sugar":"xo","application\/vnd.oma.dd2+xml":"dd2","application\/vnd.openofficeorg.extension":"oxt","application\/vnd.openxmlformats-officedocument.presentationml.presentation":"pptx","application\/vnd.openxmlformats-officedocument.presentationml.slide":"sldx","application\/vnd.openxmlformats-officedocument.presentationml.slideshow":"ppsx","application\/vnd.openxmlformats-officedocument.presentationml.template":"potx","application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet":"xlsx","application\/vnd.openxmlformats-officedocument.spreadsheetml.template":"xltx","application\/vnd.openxmlformats-officedocument.wordprocessingml.document":"docx","application\/vnd.openxmlformats-officedocument.wordprocessingml.template":"dotx","application\/vnd.osgeo.mapguide.package":"mgp","application\/vnd.osgi.dp":"dp","application\/vnd.osgi.subsystem":"esa","application\/vnd.palm":"pdb","application\/vnd.pawaafile":"paw","application\/vnd.pg.format":"str","application\/vnd.pg.osasli":"ei6","application\/vnd.picsel":"efif","application\/vnd.pmi.widget":"wg","application\/vnd.pocketlearn":"plf","application\/vnd.powerbuilder6":"pbd","application\/vnd.previewsystems.box":"box","application\/vnd.proteus.magazine":"mgz","application\/vnd.publishare-delta-tree":"qps","application\/vnd.pvi.ptid1":"ptid","application\/vnd.quark.quarkxpress":"qxd","application\/vnd.realvnc.bed":"bed","application\/vnd.recordare.musicxml":"mxl","application\/vnd.recordare.musicxml+xml":"musicxml","application\/vnd.rig.cryptonote":"cryptonote","application\/vnd.rim.cod":"cod","application\/vnd.rn-realmedia":"rm","application\/vnd.rn-realmedia-vbr":"rmvb","application\/vnd.route66.link66+xml":"link66","application\/vnd.sailingtracker.track":"st","application\/vnd.seemail":"see","application\/vnd.sema":"sema","application\/vnd.semd":"semd","application\/vnd.semf":"semf","application\/vnd.shana.informed.formdata":"ifm","application\/vnd.shana.informed.formtemplate":"itp","application\/vnd.shana.informed.interchange":"iif","application\/vnd.shana.informed.package":"ipk","application\/vnd.simtech-mindmapper":"twd","application\/vnd.smaf":"mmf","application\/vnd.smart.teacher":"teacher","application\/vnd.solent.sdkm+xml":"sdkm","application\/vnd.spotfire.dxp":"dxp","application\/vnd.spotfire.sfs":"sfs","application\/vnd.stardivision.calc":"sdc","application\/vnd.stardivision.draw":"sda","application\/vnd.stardivision.impress":"sdd","application\/vnd.stardivision.math":"smf","application\/vnd.stardivision.writer":"sdw","application\/vnd.stardivision.writer-global":"sgl","application\/vnd.stepmania.package":"smzip","application\/vnd.stepmania.stepchart":"sm","application\/vnd.sun.xml.calc":"sxc","application\/vnd.sun.xml.calc.template":"stc","application\/vnd.sun.xml.draw":"sxd","application\/vnd.sun.xml.draw.template":"std","application\/vnd.sun.xml.impress":"sxi","application\/vnd.sun.xml.impress.template":"sti","application\/vnd.sun.xml.math":"sxm","application\/vnd.sun.xml.writer":"sxw","application\/vnd.sun.xml.writer.global":"sxg","application\/vnd.sun.xml.writer.template":"stw","application\/vnd.sus-calendar":"sus","application\/vnd.svd":"svd","application\/vnd.symbian.install":"sis","application\/vnd.syncml+xml":"xsm","application\/vnd.syncml.dm+wbxml":"bdm","application\/vnd.syncml.dm+xml":"xdm","application\/vnd.tao.intent-module-archive":"tao","application\/vnd.tcpdump.pcap":"pcap","application\/vnd.tmobile-livetv":"tmo","application\/vnd.trid.tpt":"tpt","application\/vnd.triscape.mxs":"mxs","application\/vnd.trueapp":"tra","application\/vnd.ufdl":"ufd","application\/vnd.uiq.theme":"utz","application\/vnd.umajin":"umj","application\/vnd.unity":"unityweb","application\/vnd.uoml+xml":"uoml","application\/vnd.vcx":"vcx","application\/vnd.visio":"vsd","application\/vnd.visionary":"vis","application\/vnd.vsf":"vsf","application\/vnd.wap.wbxml":"wbxml","application\/vnd.wap.wmlc":"wmlc","application\/vnd.wap.wmlscriptc":"wmlsc","application\/vnd.webturbo":"wtb","application\/vnd.wolfram.player":"nbp","application\/vnd.wordperfect":"wpd","application\/vnd.wqd":"wqd","application\/vnd.wt.stf":"stf","application\/vnd.xara":"xar","application\/vnd.xfdl":"xfdl","application\/vnd.yamaha.hv-dic":"hvd","application\/vnd.yamaha.hv-script":"hvs","application\/vnd.yamaha.hv-voice":"hvp","application\/vnd.yamaha.openscoreformat":"osf","application\/vnd.yamaha.openscoreformat.osfpvg+xml":"osfpvg","application\/vnd.yamaha.smaf-audio":"saf","application\/vnd.yamaha.smaf-phrase":"spf","application\/vnd.yellowriver-custom-menu":"cmp","application\/vnd.zul":"zir","application\/vnd.zzazz.deck+xml":"zaz","application\/voicexml+xml":"vxml","application\/widget":"wgt","application\/winhlp":"hlp","application\/wsdl+xml":"wsdl","application\/wspolicy+xml":"wspolicy","application\/x-7z-compressed":"7z","application\/x-abiword":"abw","application\/x-ace-compressed":"ace","application\/x-apple-diskimage":"dmg","application\/x-authorware-bin":"aab","application\/x-authorware-map":"aam","application\/x-authorware-seg":"aas","application\/x-bcpio":"bcpio","application\/x-bittorrent":"torrent","application\/x-blorb":"blb","application\/x-bzip":"bz","application\/x-cbr":"cbr","application\/x-cdlink":"vcd","application\/x-cfs-compressed":"cfs","application\/x-chat":"chat","application\/x-chess-pgn":"pgn","application\/x-conference":"nsc","application\/x-cpio":"cpio","application\/x-csh":"csh","application\/x-debian-package":"deb","application\/x-dgc-compressed":"dgc","application\/x-director":"dir","application\/x-doom":"wad","application\/x-dtbncx+xml":"ncx","application\/x-dtbook+xml":"dtb","application\/x-dtbresource+xml":"res","application\/x-dvi":"dvi","application\/x-envoy":"evy","application\/x-eva":"eva","application\/x-font-bdf":"bdf","application\/x-font-ghostscript":"gsf","application\/x-font-linux-psf":"psf","application\/x-font-pcf":"pcf","application\/x-font-snf":"snf","application\/x-font-type1":"pfa","application\/x-freearc":"arc","application\/x-futuresplash":"spl","application\/x-gca-compressed":"gca","application\/x-glulx":"ulx","application\/x-gnumeric":"gnumeric","application\/x-gramps-xml":"gramps","application\/x-gtar":"gtar","application\/x-hdf":"hdf","application\/x-install-instructions":"install","application\/x-iso9660-image":"iso","application\/x-java-jnlp-file":"jnlp","application\/x-latex":"latex","application\/x-lzh-compressed":"lzh","application\/x-mie":"mie","application\/x-mobipocket-ebook":"prc","application\/x-ms-application":"application","application\/x-ms-shortcut":"lnk","application\/x-ms-wmd":"wmd","application\/x-ms-wmz":"wmz","application\/x-ms-xbap":"xbap","application\/x-msaccess":"mdb","application\/x-msbinder":"obd","application\/x-mscardfile":"crd","application\/x-msclip":"clp","application\/x-msdownload":"dll","application\/x-msmediaview":"mvb","application\/x-msmetafile":"wmf","application\/x-msmoney":"mny","application\/x-mspublisher":"pub","application\/x-msschedule":"scd","application\/x-msterminal":"trm","application\/x-mswrite":"wri","application\/x-netcdf":"nc","application\/x-nzb":"nzb","application\/x-pkcs12":"p12","application\/x-pkcs7-certificates":"p7b","application\/x-pkcs7-certreqresp":"p7r","application\/x-research-info-systems":"ris","application\/x-shar":"shar","application\/x-shockwave-flash":"swf","application\/x-silverlight-app":"xap","application\/x-sql":"sql","application\/x-stuffit":"sit","application\/x-stuffitx":"sitx","application\/x-subrip":"srt","application\/x-sv4cpio":"sv4cpio","application\/x-sv4crc":"sv4crc","application\/x-t3vm-image":"t3","application\/x-tads":"gam","application\/x-tar":"tar","application\/x-tcl":"tcl","application\/x-tex":"tex","application\/x-tex-tfm":"tfm","application\/x-texinfo":"texinfo","application\/x-tgif":"obj","application\/x-ustar":"ustar","application\/x-wais-source":"src","application\/x-x509-ca-cert":"der","application\/x-xfig":"fig","application\/x-xliff+xml":"xlf","application\/x-xpinstall":"xpi","application\/x-xz":"xz","application\/x-zmachine":"z1","application\/xaml+xml":"xaml","application\/xcap-diff+xml":"xdf","application\/xenc+xml":"xenc","application\/xhtml+xml":"xhtml","application\/xml":"xsl","application\/xml-dtd":"dtd","application\/xop+xml":"xop","application\/xproc+xml":"xpl","application\/xslt+xml":"xslt","application\/xspf+xml":"xspf","application\/xv+xml":"mxml","application\/yang":"yang","application\/yin+xml":"yin","application\/zip":"zip","audio\/adpcm":"adp","audio\/basic":"au","audio\/midi":"mid","audio\/mp4":"m4a","audio\/mpeg":"mpga","audio\/ogg":"oga","audio\/s3m":"s3m","audio\/silk":"sil","audio\/vnd.dece.audio":"uva","audio\/vnd.digital-winds":"eol","audio\/vnd.dra":"dra","audio\/vnd.dts":"dts","audio\/vnd.dts.hd":"dtshd","audio\/vnd.lucent.voice":"lvp","audio\/vnd.ms-playready.media.pya":"pya","audio\/vnd.nuera.ecelp4800":"ecelp4800","audio\/vnd.nuera.ecelp7470":"ecelp7470","audio\/vnd.nuera.ecelp9600":"ecelp9600","audio\/vnd.rip":"rip","audio\/webm":"weba","audio\/x-aac":"aac","audio\/x-aiff":"aif","audio\/x-caf":"caf","audio\/x-flac":"flac","audio\/x-matroska":"mka","audio\/x-mpegurl":"m3u","audio\/x-ms-wax":"wax","audio\/x-ms-wma":"wma","audio\/x-pn-realaudio":"ram","audio\/x-pn-realaudio-plugin":"rmp","audio\/xm":"xm","chemical\/x-cdx":"cdx","chemical\/x-cif":"cif","chemical\/x-cmdf":"cmdf","chemical\/x-cml":"cml","chemical\/x-csml":"csml","chemical\/x-xyz":"xyz","font\/collection":"ttc","font\/otf":"otf","font\/ttf":"ttf","font\/woff":"woff","font\/woff2":"woff2","image\/cgm":"cgm","image\/g3fax":"g3","image\/gif":"gif","image\/ief":"ief","image\/jpeg":"jpeg","image\/ktx":"ktx","image\/png":"png","image\/prs.btif":"btif","image\/sgi":"sgi","image\/svg+xml":"svg","image\/tiff":"tiff","image\/vnd.adobe.photoshop":"psd","image\/vnd.dece.graphic":"uvi","image\/vnd.djvu":"djvu","image\/vnd.dvb.subtitle":"sub","image\/vnd.dwg":"dwg","image\/vnd.dxf":"dxf","image\/vnd.fastbidsheet":"fbs","image\/vnd.fpx":"fpx","image\/vnd.fst":"fst","image\/vnd.fujixerox.edmics-mmr":"mmr","image\/vnd.fujixerox.edmics-rlc":"rlc","image\/vnd.ms-modi":"mdi","image\/vnd.ms-photo":"wdp","image\/vnd.net-fpx":"npx","image\/vnd.wap.wbmp":"wbmp","image\/vnd.xiff":"xif","image\/webp":"webp","image\/x-3ds":"3ds","image\/x-cmu-raster":"ras","image\/x-cmx":"cmx","image\/x-freehand":"fh","image\/x-icon":"ico","image\/x-mrsid-image":"sid","image\/x-pcx":"pcx","image\/x-pict":"pic","image\/x-portable-anymap":"pnm","image\/x-portable-bitmap":"pbm","image\/x-portable-graymap":"pgm","image\/x-portable-pixmap":"ppm","image\/x-rgb":"rgb","image\/x-xpixmap":"xpm","image\/x-xwindowdump":"xwd","message\/rfc822":"eml","model\/iges":"igs","model\/mesh":"msh","model\/vnd.collada+xml":"dae","model\/vnd.dwf":"dwf","model\/vnd.gdl":"gdl","model\/vnd.gtw":"gtw","model\/vnd.vtu":"vtu","model\/vrml":"wrl","model\/x3d+binary":"x3db","model\/x3d+vrml":"x3dv","model\/x3d+xml":"x3d","text\/cache-manifest":"appcache","text\/calendar":"ics","text\/css":"css","text\/csv":"csv","text\/html":"html","text\/n3":"n3","text\/plain":"txt","text\/prs.lines.tag":"dsc","text\/richtext":"rtx","text\/sgml":"sgml","text\/tab-separated-values":"tsv","text\/troff":"t","text\/turtle":"ttl","text\/uri-list":"uri","text\/vcard":"vcard","text\/vnd.curl":"curl","text\/vnd.curl.dcurl":"dcurl","text\/vnd.curl.mcurl":"mcurl","text\/vnd.curl.scurl":"scurl","text\/vnd.fly":"fly","text\/vnd.fmi.flexstor":"flx","text\/vnd.graphviz":"gv","text\/vnd.in3d.3dml":"3dml","text\/vnd.in3d.spot":"spot","text\/vnd.sun.j2me.app-descriptor":"jad","text\/vnd.wap.wml":"wml","text\/vnd.wap.wmlscript":"wmls","text\/x-asm":"s","text\/x-c":"cc","text\/x-fortran":"f","text\/x-java-source":"java","text\/x-nfo":"nfo","text\/x-opml":"opml","text\/x-pascal":"p","text\/x-setext":"etx","text\/x-sfv":"sfv","text\/x-uuencode":"uu","text\/x-vcalendar":"vcs","text\/x-vcard":"vcf","video\/3gpp":"3gp","video\/3gpp2":"3g2","video\/h261":"h261","video\/h263":"h263","video\/h264":"h264","video\/jpeg":"jpgv","video\/jpm":"jpm","video\/mj2":"mj2","video\/mp4":"mp4","video\/mpeg":"mpeg","video\/quicktime":"qt","video\/vnd.dece.hd":"uvh","video\/vnd.dece.mobile":"uvm","video\/vnd.dece.pd":"uvp","video\/vnd.dece.sd":"uvs","video\/vnd.dece.video":"uvv","video\/vnd.dvb.file":"dvb","video\/vnd.fvt":"fvt","video\/vnd.mpegurl":"mxu","video\/vnd.ms-playready.media.pyv":"pyv","video\/vnd.uvvu.mp4":"uvu","video\/vnd.vivo":"viv","video\/webm":"webm","video\/x-f4v":"f4v","video\/x-fli":"fli","video\/x-flv":"flv","video\/x-m4v":"m4v","video\/x-matroska":"mkv","video\/x-mng":"mng","video\/x-ms-asf":"asf","video\/x-ms-vob":"vob","video\/x-ms-wmx":"wmx","video\/x-ms-wvx":"wvx","video\/x-msvideo":"avi","video\/x-sgi-movie":"movie","video\/x-smv":"smv","x-conference\/x-cooltalk":"ice","text\/x-sql":"sql","image\/x-pixlr-data":"pxd","image\/x-adobe-dng":"dng","image\/x-sketch":"sketch","image\/x-xcf":"xcf","audio\/amr":"amr","image\/vnd-ms.dds":"dds","application\/plt":"plt","application\/sat":"sat","application\/step":"step","text\/x-httpd-cgi":"cgi","text\/x-asap":"asp","text\/x-jsp":"jsp"};
  10292. /*
  10293. * File: /js/elFinder.options.js
  10294. */
  10295. /**
  10296. * Default elFinder config
  10297. *
  10298. * @type Object
  10299. * @autor Dmitry (dio) Levashov
  10300. */
  10301. elFinder.prototype._options = {
  10302. /**
  10303. * URLs of 3rd party libraries CDN
  10304. *
  10305. * @type Object
  10306. */
  10307. cdns : {
  10308. // for editor etc.
  10309. ace : 'https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.8',
  10310. codemirror : 'https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2',
  10311. ckeditor : 'https://cdnjs.cloudflare.com/ajax/libs/ckeditor/4.12.1',
  10312. ckeditor5 : 'https://cdn.ckeditor.com/ckeditor5/17.0.0',
  10313. tinymce : 'https://cdnjs.cloudflare.com/ajax/libs/tinymce/5.2.0',
  10314. simplemde : 'https://cdnjs.cloudflare.com/ajax/libs/simplemde/1.11.2',
  10315. fabric : 'https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2',
  10316. fabric16 : 'https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.7',
  10317. tui : 'https://uicdn.toast.com',
  10318. // for quicklook etc.
  10319. hls : 'https://cdnjs.cloudflare.com/ajax/libs/hls.js/0.13.2/hls.min.js',
  10320. dash : 'https://cdnjs.cloudflare.com/ajax/libs/dashjs/3.0.3/dash.all.min.js',
  10321. flv : 'https://cdnjs.cloudflare.com/ajax/libs/flv.js/1.5.0/flv.min.js',
  10322. videojs : 'https://cdnjs.cloudflare.com/ajax/libs/video.js/7.7.5',
  10323. prettify : 'https://cdn.jsdelivr.net/gh/google/code-prettify@f1c3473acd1e8ea8c8c1a60c56e89f5cdd06f915/loader/run_prettify.js',
  10324. psd : 'https://cdnjs.cloudflare.com/ajax/libs/psd.js/3.2.0/psd.min.js',
  10325. rar : 'https://cdn.jsdelivr.net/gh/nao-pon/rar.js@6cef13ec66dd67992fc7f3ea22f132d770ebaf8b/rar.min.js',
  10326. zlibUnzip : 'https://cdn.jsdelivr.net/gh/imaya/zlib.js@0.3.1/bin/unzip.min.js', // need check unzipFiles() in quicklook.plugins.js when update
  10327. zlibGunzip : 'https://cdn.jsdelivr.net/gh/imaya/zlib.js@0.3.1/bin/gunzip.min.js',
  10328. bzip2 : 'https://cdn.jsdelivr.net/gh/nao-pon/bzip2.js@0.8.0/bzip2.js',
  10329. marked : 'https://cdnjs.cloudflare.com/ajax/libs/marked/0.7.0/marked.min.js',
  10330. sparkmd5 : 'https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.0/spark-md5.min.js',
  10331. jssha : 'https://cdnjs.cloudflare.com/ajax/libs/jsSHA/2.3.1/sha.js',
  10332. amr : 'https://cdn.jsdelivr.net/gh/yxl/opencore-amr-js@dcf3d2b5f384a1d9ded2a54e4c137a81747b222b/js/amrnb.js',
  10333. tiff : 'https://cdn.jsdelivr.net/gh/seikichi/tiff.js@545ede3ee46b5a5bc5f06d65954e775aa2a64017/tiff.min.js'
  10334. },
  10335. /**
  10336. * Connector url. Required!
  10337. *
  10338. * @type String
  10339. */
  10340. url : '',
  10341. /**
  10342. * Ajax request type.
  10343. *
  10344. * @type String
  10345. * @default "get"
  10346. */
  10347. requestType : 'get',
  10348. /**
  10349. * Use CORS to connector url
  10350. *
  10351. * @type Boolean|null true|false|null(Auto detect)
  10352. */
  10353. cors : null,
  10354. /**
  10355. * Array of header names to return parrot out in HTTP headers received from the server
  10356. *
  10357. * @type Array
  10358. */
  10359. parrotHeaders : [],
  10360. /**
  10361. * Maximum number of concurrent connections on request
  10362. *
  10363. * @type Number
  10364. * @default 3
  10365. */
  10366. requestMaxConn : 3,
  10367. /**
  10368. * Transport to send request to backend.
  10369. * Required for future extensions using websockets/webdav etc.
  10370. * Must be an object with "send" method.
  10371. * transport.send must return $.Deferred() object
  10372. *
  10373. * @type Object
  10374. * @default null
  10375. * @example
  10376. * transport : {
  10377. * init : function(elfinderInstance) { },
  10378. * send : function(options) {
  10379. * var dfrd = $.Deferred();
  10380. * // connect to backend ...
  10381. * return dfrd;
  10382. * },
  10383. * upload : function(data) {
  10384. * var dfrd = $.Deferred();
  10385. * // upload ...
  10386. * return dfrd;
  10387. * }
  10388. *
  10389. * }
  10390. **/
  10391. transport : {},
  10392. /**
  10393. * URL to upload file to.
  10394. * If not set - connector URL will be used
  10395. *
  10396. * @type String
  10397. * @default ''
  10398. */
  10399. urlUpload : '',
  10400. /**
  10401. * Allow to drag and drop to upload files
  10402. *
  10403. * @type Boolean|String
  10404. * @default 'auto'
  10405. */
  10406. dragUploadAllow : 'auto',
  10407. /**
  10408. * Confirmation dialog displayed at the time of overwriting upload
  10409. *
  10410. * @type Boolean
  10411. * @default true
  10412. */
  10413. overwriteUploadConfirm : true,
  10414. /**
  10415. * Max size of chunked data of file upload
  10416. *
  10417. * @type Number
  10418. * @default 10485760(10MB)
  10419. */
  10420. uploadMaxChunkSize : 10485760,
  10421. /**
  10422. * Regular expression of file name to exclude when uploading folder
  10423. *
  10424. * @type Object
  10425. * @default { win: /^(?:desktop\.ini|thumbs\.db)$/i, mac: /^\.ds_store$/i }
  10426. */
  10427. folderUploadExclude : {
  10428. win: /^(?:desktop\.ini|thumbs\.db)$/i,
  10429. mac: /^\.ds_store$/i
  10430. },
  10431. /**
  10432. * Timeout for upload using iframe
  10433. *
  10434. * @type Number
  10435. * @default 0 - no timeout
  10436. */
  10437. iframeTimeout : 0,
  10438. /**
  10439. * Data to append to all requests and to upload files
  10440. *
  10441. * @type Object
  10442. * @default {}
  10443. */
  10444. customData : {},
  10445. /**
  10446. * Event listeners to bind on elFinder init
  10447. *
  10448. * @type Object
  10449. * @default {}
  10450. */
  10451. handlers : {},
  10452. /**
  10453. * Any custom headers to send across every ajax request
  10454. *
  10455. * @type Object
  10456. * @default {}
  10457. */
  10458. customHeaders : {},
  10459. /**
  10460. * Any custom xhrFields to send across every ajax request
  10461. *
  10462. * @type Object
  10463. * @default {}
  10464. */
  10465. xhrFields : {},
  10466. /**
  10467. * Interface language
  10468. *
  10469. * @type String
  10470. * @default "en"
  10471. */
  10472. lang : 'en',
  10473. /**
  10474. * Base URL of elfFinder library starting from Manager HTML
  10475. * Auto detect when empty value
  10476. *
  10477. * @type String
  10478. * @default ""
  10479. */
  10480. baseUrl : '',
  10481. /**
  10482. * Base URL of i18n js files
  10483. * baseUrl + "js/i18n/" when empty value
  10484. *
  10485. * @type String
  10486. * @default ""
  10487. */
  10488. i18nBaseUrl : '',
  10489. /**
  10490. * Auto load required CSS
  10491. * `false` to disable this function or
  10492. * CSS URL Array to load additional CSS files
  10493. *
  10494. * @type Boolean|Array
  10495. * @default true
  10496. */
  10497. cssAutoLoad : true,
  10498. /**
  10499. * Theme to load
  10500. * {"themeid" : "Theme CSS URL"} or
  10501. * {"themeid" : "Theme manifesto.json URL"} or
  10502. * Theme manifesto.json Object
  10503. * {
  10504. * "themeid" : {
  10505. * "name":"Theme Name",
  10506. * "cssurls":"Theme CSS URL",
  10507. * "author":"Author Name",
  10508. * "email":"Author Email",
  10509. * "license":"License",
  10510. * "link":"Web Site URL",
  10511. * "image":"Screen Shot URL",
  10512. * "description":"Description"
  10513. * }
  10514. * }
  10515. *
  10516. * @type Object
  10517. */
  10518. themes : {},
  10519. /**
  10520. * Theme id to initial theme
  10521. *
  10522. * @type String|Null
  10523. */
  10524. theme : null,
  10525. /**
  10526. * Maximum value of error dialog open at the same time
  10527. *
  10528. * @type Number
  10529. */
  10530. maxErrorDialogs : 5,
  10531. /**
  10532. * Additional css class for filemanager node.
  10533. *
  10534. * @type String
  10535. */
  10536. cssClass : '',
  10537. /**
  10538. * Active commands list. '*' means all of the commands that have been load.
  10539. * If some required commands will be missed here, elFinder will add its
  10540. *
  10541. * @type Array
  10542. */
  10543. commands : ['*'],
  10544. // Available commands list
  10545. //commands : [
  10546. // 'archive', 'back', 'chmod', 'colwidth', 'copy', 'cut', 'download', 'duplicate', 'edit', 'extract',
  10547. // 'forward', 'fullscreen', 'getfile', 'help', 'home', 'info', 'mkdir', 'mkfile', 'netmount', 'netunmount',
  10548. // 'open', 'opendir', 'paste', 'places', 'quicklook', 'reload', 'rename', 'resize', 'restore', 'rm',
  10549. // 'search', 'sort', 'up', 'upload', 'view', 'zipdl'
  10550. //],
  10551. /**
  10552. * Commands options.
  10553. *
  10554. * @type Object
  10555. **/
  10556. commandsOptions : {
  10557. // // configure shortcuts of any command
  10558. // // add `shortcuts` property into each command
  10559. // any_command_name : {
  10560. // shortcuts : [] // for disable this command's shortcuts
  10561. // },
  10562. // any_command_name : {
  10563. // shortcuts : function(fm, shortcuts) {
  10564. // // for add `CTRL + E` for this command action
  10565. // shortcuts[0]['pattern'] += ' ctrl+e';
  10566. // return shortcuts;
  10567. // }
  10568. // },
  10569. // any_command_name : {
  10570. // shortcuts : function(fm, shortcuts) {
  10571. // // for full customize of this command's shortcuts
  10572. // return [ { pattern: 'ctrl+e ctrl+down numpad_enter' + (fm.OS != 'mac' && ' enter') } ];
  10573. // }
  10574. // },
  10575. // "getfile" command options.
  10576. getfile : {
  10577. onlyURL : false,
  10578. // allow to return multiple files info
  10579. multiple : false,
  10580. // allow to return filers info
  10581. folders : false,
  10582. // action after callback (""/"close"/"destroy")
  10583. oncomplete : '',
  10584. // action when callback is fail (""/"close"/"destroy")
  10585. onerror : '',
  10586. // get path before callback call
  10587. getPath : true,
  10588. // get image sizes before callback call
  10589. getImgSize : false
  10590. },
  10591. open : {
  10592. // HTTP method that request to the connector when item URL is not valid URL.
  10593. // If you set to "get" will be displayed request parameter in the browser's location field
  10594. // so if you want to conceal its parameters should be given "post".
  10595. // Nevertheless, please specify "get" if you want to enable the partial request by HTTP Range header.
  10596. method : 'post',
  10597. // Where to open into : 'window'(default), 'tab' or 'tabs'
  10598. // 'tabs' opens in each tabs
  10599. into : 'window',
  10600. // Default command list of action when select file
  10601. // String value that is 'Command Name' or 'Command Name1/CommandName2...'
  10602. selectAction : 'open'
  10603. },
  10604. opennew : {
  10605. // URL of to open elFinder manager
  10606. // Default '' : Origin URL
  10607. url : '',
  10608. // Use search query of origin URL
  10609. useOriginQuery : true
  10610. },
  10611. // "upload" command options.
  10612. upload : {
  10613. // Open elFinder upload dialog: 'button' OR Open system OS upload dialog: 'uploadbutton'
  10614. ui : 'button'
  10615. },
  10616. // "download" command options.
  10617. download : {
  10618. // max request to download files when zipdl disabled
  10619. maxRequests : 10,
  10620. // minimum count of files to use zipdl
  10621. minFilesZipdl : 2
  10622. },
  10623. // "quicklook" command options.
  10624. quicklook : {
  10625. autoplay : true,
  10626. width : 450,
  10627. height : 300,
  10628. // ControlsList of HTML5 audio/video preview
  10629. // see https://googlechrome.github.io/samples/media/controlslist.html
  10630. mediaControlsList : '', // e.g. 'nodownload nofullscreen noremoteplayback'
  10631. // Show toolbar of PDF preview (with <embed> tag)
  10632. pdfToolbar : true,
  10633. // Maximum lines to preview at initial
  10634. textInitialLines : 100,
  10635. // Maximum lines to preview by prettify
  10636. prettifyMaxLines : 300,
  10637. // quicklook window must be contained in elFinder node on window open (true|false)
  10638. contain : false,
  10639. // preview window into NavDock (0 : undocked | 1 : docked(show) | 2 : docked(hide))
  10640. docked : 0,
  10641. // Docked preview height ('auto' or Number of pixel) 'auto' is setted to the Navbar width
  10642. dockHeight : 'auto',
  10643. // media auto play when docked
  10644. dockAutoplay : false,
  10645. // Google Maps API key (Require Maps JavaScript API)
  10646. googleMapsApiKey : '',
  10647. // Google Maps API Options
  10648. googleMapsOpts : {
  10649. maps : {},
  10650. kml : {
  10651. suppressInfoWindows : false,
  10652. preserveViewport : false
  10653. }
  10654. },
  10655. // ViewerJS (https://viewerjs.org/) Options
  10656. // To enable this you need to place ViewerJS on the same server as elFinder and specify that URL in `url`.
  10657. viewerjs : {
  10658. url: '', // Example '/ViewerJS/index.html'
  10659. mimes: ['application/pdf', 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.oasis.opendocument.presentation'],
  10660. pdfNative: true // Use Native PDF Viewer first
  10661. },
  10662. // MIME types to CAD-Files and 3D-Models online viewer on sharecad.org
  10663. // Example ['image/vnd.dwg', 'image/vnd.dxf', 'model/vnd.dwf', 'application/vnd.hp-hpgl', 'application/plt', 'application/step', 'model/iges', 'application/vnd.ms-pki.stl', 'application/sat', 'image/cgm', 'application/x-msmetafile']
  10664. sharecadMimes : [],
  10665. // MIME types to use Google Docs online viewer
  10666. // Example ['application/pdf', 'image/tiff', 'application/vnd.ms-office', 'application/msword', 'application/vnd.ms-word', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/postscript', 'application/rtf']
  10667. googleDocsMimes : [],
  10668. // MIME types to use Microsoft Office Online viewer
  10669. // Example ['application/msword', 'application/vnd.ms-word', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.oasis.opendocument.presentation']
  10670. // These MIME types override "googleDocsMimes"
  10671. officeOnlineMimes : [],
  10672. // File size threshold when using the dim command for obtain the image size necessary to image preview
  10673. getDimThreshold : '200K',
  10674. // Max filesize to show filenames of the zip/tar/gzip/bzip file
  10675. unzipMaxSize : '50M',
  10676. // MIME-Type regular expression that does not check empty files
  10677. mimeRegexNotEmptyCheck : /^application\/vnd\.google-apps\./
  10678. },
  10679. // "edit" command options.
  10680. edit : {
  10681. // dialog width, integer(px) or integer+'%' (example: 650, '80%' ...)
  10682. dialogWidth : void(0),
  10683. // dialog height, integer(px) or integer+'%' (example: 650, '80%' ...)
  10684. dialogHeight : void(0),
  10685. // list of allowed mimetypes to edit of text files
  10686. // if empty - any text files can be edited
  10687. mimes : [],
  10688. // MIME-types to unselected as default of "File types to enable with "New file"" in preferences
  10689. mkfileHideMimes : [],
  10690. // MIME-types of text file to make empty file
  10691. makeTextMimes : ['text/plain', 'text/css', 'text/html'],
  10692. // Use the editor stored in the browser
  10693. // This value allowd overwrite with user preferences
  10694. useStoredEditor : false,
  10695. // Open the maximized editor window
  10696. // This value allowd overwrite with user preferences
  10697. editorMaximized : false,
  10698. // edit files in wysisyg's
  10699. editors : [
  10700. // {
  10701. // /**
  10702. // * editor info
  10703. // * @type Object
  10704. // */
  10705. // info : { name: 'Editor Name' },
  10706. // /**
  10707. // * files mimetypes allowed to edit in current wysisyg
  10708. // * @type Array
  10709. // */
  10710. // mimes : ['text/html'],
  10711. // /**
  10712. // * HTML element for editing area (optional for text editor)
  10713. // * @type String
  10714. // */
  10715. // html : '<textarea></textarea>',
  10716. // /**
  10717. // * Initialize editing area node (optional for text editor)
  10718. // *
  10719. // * @param String dialog DOM id
  10720. // * @param Object target file object
  10721. // * @param String target file content (text or Data URI Scheme(binary file))
  10722. // * @param Object elFinder instance
  10723. // * @type Function
  10724. // */
  10725. // init : function(id, file, content, fm) {
  10726. // $(this).attr('id', id + '-text').val(content);
  10727. // },
  10728. // /**
  10729. // * Get edited contents (optional for text editor)
  10730. // * @type Function
  10731. // */
  10732. // getContent : function() {
  10733. // return $(this).val();
  10734. // },
  10735. // /**
  10736. // * Called when "edit" dialog loaded.
  10737. // * Place to init wysisyg.
  10738. // * Can return wysisyg instance
  10739. // *
  10740. // * @param DOMElement textarea node
  10741. // * @return Object editor instance|jQuery.Deferred(return instance on resolve())
  10742. // */
  10743. // load : function(textarea) { },
  10744. // /**
  10745. // * Called before "edit" dialog closed.
  10746. // * Place to destroy wysisyg instance.
  10747. // *
  10748. // * @param DOMElement textarea node
  10749. // * @param Object wysisyg instance (if was returned by "load" callback)
  10750. // * @return void
  10751. // */
  10752. // close : function(textarea, instance) { },
  10753. // /**
  10754. // * Called before file content send to backend.
  10755. // * Place to update textarea content if needed.
  10756. // *
  10757. // * @param DOMElement textarea node
  10758. // * @param Object wysisyg instance (if was returned by "load" callback)
  10759. // * @return void
  10760. // */
  10761. // save : function(textarea, instance) {},
  10762. // /**
  10763. // * Called after load() or save().
  10764. // * Set focus to wysisyg editor.
  10765. // *
  10766. // * @param DOMElement textarea node
  10767. // * @param Object wysisyg instance (if was returned by "load" callback)
  10768. // * @return void
  10769. // */
  10770. // focus : function(textarea, instance) {}
  10771. // /**
  10772. // * Called after dialog resized..
  10773. // *
  10774. // * @param DOMElement textarea node
  10775. // * @param Object wysisyg instance (if was returned by "load" callback)
  10776. // * @param Object resize event object
  10777. // * @param Object data object
  10778. // * @return void
  10779. // */
  10780. // resize : function(textarea, instance, event, data) {}
  10781. //
  10782. // }
  10783. ],
  10784. // Character encodings of select box
  10785. encodings : ['Big5', 'Big5-HKSCS', 'Cp437', 'Cp737', 'Cp775', 'Cp850', 'Cp852', 'Cp855', 'Cp857', 'Cp858',
  10786. 'Cp862', 'Cp866', 'Cp874', 'EUC-CN', 'EUC-JP', 'EUC-KR', 'GB18030', 'ISO-2022-CN', 'ISO-2022-JP', 'ISO-2022-KR',
  10787. 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4', 'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7',
  10788. 'ISO-8859-8', 'ISO-8859-9', 'ISO-8859-13', 'ISO-8859-15', 'KOI8-R', 'KOI8-U', 'Shift-JIS',
  10789. 'Windows-1250', 'Windows-1251', 'Windows-1252', 'Windows-1253', 'Windows-1254', 'Windows-1257'],
  10790. // options for extra editors
  10791. extraOptions : {
  10792. // upload command options
  10793. uploadOpts : {},
  10794. // TUI Image Editor's options
  10795. tuiImgEditOpts : {
  10796. // Path prefix of icon-a.svg, icon-b.svg, icon-c.svg and icon-d.svg in the Theme.
  10797. // `iconsPath` MUST follow the same origin policy.
  10798. iconsPath : void(0), // default is "./img/tui-"
  10799. // Theme object
  10800. theme : {}
  10801. },
  10802. // Pixo image editor constructor options - https://pixoeditor.com/
  10803. // Require 'apikey' to enable it
  10804. pixo: {
  10805. apikey: ''
  10806. },
  10807. // Browsing manager URL for CKEditor, TinyMCE
  10808. // Uses self location with the empty value or not defined.
  10809. //managerUrl : 'elfinder.html'
  10810. managerUrl : null,
  10811. // CKEditor editor options
  10812. ckeditor: {},
  10813. // CKEditor 5 editor options
  10814. ckeditor5: {
  10815. // builds mode - 'classic', 'inline', 'balloon', 'balloon-block' or 'decoupled-document'
  10816. mode: 'decoupled-document'
  10817. },
  10818. // TinyMCE editor options
  10819. tinymce : {},
  10820. // Setting for Online-Convert.com
  10821. onlineConvert : {
  10822. maxSize : 100, // (MB) Max 100MB on free account
  10823. showLink : true // It must be enabled with free account
  10824. }
  10825. }
  10826. },
  10827. fullscreen : {
  10828. // fullscreen mode 'screen'(When the browser supports it) or 'window'
  10829. mode: 'screen' // 'screen' or 'window'
  10830. },
  10831. search : {
  10832. // Incremental search from the current view
  10833. incsearch : {
  10834. enable : true, // is enable true or false
  10835. minlen : 1, // minimum number of characters
  10836. wait : 500 // wait milliseconds
  10837. },
  10838. // Additional search types
  10839. searchTypes : {
  10840. // "SearchMime" is implemented in default
  10841. SearchMime : { // The key is search type that send to the connector
  10842. name : 'btnMime', // Button text to be processed in i18n()
  10843. title : 'searchMime',// Button title to be processed in i18n()
  10844. incsearch : 'mime' // Incremental search target filed name of the file object
  10845. // Or Callable function
  10846. /* incsearch function example
  10847. function(queryObject, cwdHashes, elFinderInstance) {
  10848. var q = queryObject.val;
  10849. var regex = queryObject.regex;
  10850. var matchedHashes = $.grep(cwdHashes, function(hash) {
  10851. var file = elFinderInstance.file(hash);
  10852. return (file && file.mime && file.mime.match(regex))? true : false;
  10853. });
  10854. return matchedHashes;
  10855. }
  10856. */
  10857. }
  10858. }
  10859. },
  10860. // "info" command options.
  10861. info : {
  10862. // If the URL of the Directory is null,
  10863. // it is assumed that the link destination is a URL to open the folder in elFinder
  10864. nullUrlDirLinkSelf : true,
  10865. // Information items to be hidden by default
  10866. // These name are 'size', 'aliasfor', 'path', 'link', 'dim', 'modify', 'perms', 'locked', 'owner', 'group', 'perm' and your custom info items label
  10867. hideItems : [],
  10868. // Maximum file size (byte) to get file contents hash (md5, sha256 ...)
  10869. showHashMaxsize : 104857600, // 100 MB
  10870. // Array of hash algorisms to show on info dialog
  10871. // These name are 'md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'shake128' and 'shake256'
  10872. showHashAlgorisms : ['md5', 'sha256'],
  10873. // Options for fm.getContentsHashes()
  10874. showHashOpts : {
  10875. shake128len : 256,
  10876. shake256len : 512
  10877. },
  10878. custom : {
  10879. // /**
  10880. // * Example of custom info `desc`
  10881. // */
  10882. // desc : {
  10883. // /**
  10884. // * Lable (require)
  10885. // * It is filtered by the `fm.i18n()`
  10886. // *
  10887. // * @type String
  10888. // */
  10889. // label : 'Description',
  10890. //
  10891. // /**
  10892. // * Template (require)
  10893. // * `{id}` is replaced in dialog.id
  10894. // *
  10895. // * @type String
  10896. // */
  10897. // tpl : '<div class="elfinder-info-desc"><span class="elfinder-spinner"></span></div>',
  10898. //
  10899. // /**
  10900. // * Restricts to mimetypes (optional)
  10901. // * Exact match or category match
  10902. // *
  10903. // * @type Array
  10904. // */
  10905. // mimes : ['text', 'image/jpeg', 'directory'],
  10906. //
  10907. // /**
  10908. // * Restricts to file.hash (optional)
  10909. // *
  10910. // * @ type Regex
  10911. // */
  10912. // hashRegex : /^l\d+_/,
  10913. //
  10914. // /**
  10915. // * Request that asks for the description and sets the field (optional)
  10916. // *
  10917. // * @type Function
  10918. // */
  10919. // action : function(file, fm, dialog) {
  10920. // fm.request({
  10921. // data : { cmd : 'desc', target: file.hash },
  10922. // preventDefault: true,
  10923. // })
  10924. // .fail(function() {
  10925. // dialog.find('div.elfinder-info-desc').html(fm.i18n('unknown'));
  10926. // })
  10927. // .done(function(data) {
  10928. // dialog.find('div.elfinder-info-desc').html(data.desc);
  10929. // });
  10930. // }
  10931. // }
  10932. }
  10933. },
  10934. mkdir: {
  10935. // Enable automatic switching function ["New Folder" / "Into New Folder"] of toolbar buttton
  10936. intoNewFolderToolbtn: false
  10937. },
  10938. resize: {
  10939. // defalt status of snap to 8px grid of the jpeg image ("enable" or "disable")
  10940. grid8px : 'disable',
  10941. // Preset size array [width, height]
  10942. presetSize : [[320, 240], [400, 400], [640, 480], [800,600]],
  10943. // File size (bytes) threshold when using the `dim` command for obtain the image size necessary to start editing
  10944. getDimThreshold : 204800,
  10945. // File size (bytes) to request to get substitute image (400px) with the `dim` command
  10946. dimSubImgSize : 307200
  10947. },
  10948. rm: {
  10949. // If trash is valid, items moves immediately to the trash holder without confirm.
  10950. quickTrash : true,
  10951. // Maximum wait seconds when checking the number of items to into the trash
  10952. infoCheckWait : 10,
  10953. // Maximum number of items that can be placed into the Trash at one time
  10954. toTrashMaxItems : 1000
  10955. },
  10956. paste : {
  10957. moveConfirm : false // Display confirmation dialog when moving items
  10958. },
  10959. help : {
  10960. // Tabs to show
  10961. view : ['about', 'shortcuts', 'help', 'integrations', 'debug'],
  10962. // HTML source URL of the heip tab
  10963. helpSource : ''
  10964. },
  10965. preference : {
  10966. // dialog width
  10967. width: 600,
  10968. // dialog height
  10969. height: 400,
  10970. // tabs setting see preference.js : build()
  10971. categories: null,
  10972. // preference setting see preference.js : build()
  10973. prefs: null,
  10974. // language setting see preference.js : build()
  10975. langs: null,
  10976. // Command list of action when select file
  10977. // Array value are 'Command Name' or 'Command Name1/CommandName2...'
  10978. selectActions : ['open', 'edit/download', 'resize/edit/download', 'download', 'quicklook']
  10979. }
  10980. },
  10981. /**
  10982. * Disabled commands relationship
  10983. *
  10984. * @type Object
  10985. */
  10986. disabledCmdsRels : {
  10987. 'get' : ['edit'],
  10988. 'rm' : ['cut', 'empty'],
  10989. 'file&url=' : ['download', 'zipdl'] // file command and volume options url is empty
  10990. },
  10991. /**
  10992. * Callback for prepare boot up
  10993. *
  10994. * - The this object in the function is an elFinder node
  10995. * - The first parameter is elFinder Instance
  10996. * - The second parameter is an object of other parameters
  10997. * For now it can use `dfrdsBeforeBootup` Array
  10998. *
  10999. * @type Function
  11000. * @default null
  11001. * @return void
  11002. */
  11003. bootCallback : null,
  11004. /**
  11005. * Callback for "getfile" commands.
  11006. * Required to use elFinder with WYSIWYG editors etc..
  11007. *
  11008. * @type Function
  11009. * @default null (command not active)
  11010. */
  11011. getFileCallback : null,
  11012. /**
  11013. * Default directory view. icons/list
  11014. *
  11015. * @type String
  11016. * @default "icons"
  11017. */
  11018. defaultView : 'icons',
  11019. /**
  11020. * Hash of default directory path to open
  11021. *
  11022. * NOTE: This setting will be disabled if the target folder is specified in location.hash.
  11023. *
  11024. * If you want to find the hash in Javascript
  11025. * can be obtained with the following code. (In the case of a standard hashing method)
  11026. *
  11027. * var volumeId = 'l1_'; // volume id
  11028. * var path = 'path/to/target'; // without root path
  11029. * //var path = 'path\\to\\target'; // use \ on windows server
  11030. * var hash = volumeId + btoa(path).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '.').replace(/\.+$/, '');
  11031. *
  11032. * @type String
  11033. * @default ""
  11034. */
  11035. startPathHash : '',
  11036. /**
  11037. * Emit a sound when a file is deleted
  11038. * Sounds are in sounds/ folder
  11039. *
  11040. * @type Boolean
  11041. * @default true
  11042. */
  11043. sound : true,
  11044. /**
  11045. * UI plugins to load.
  11046. * Current dir ui and dialogs loads always.
  11047. * Here set not required plugins as folders tree/toolbar/statusbar etc.
  11048. *
  11049. * @type Array
  11050. * @default ['toolbar', 'places', 'tree', 'path', 'stat']
  11051. * @full ['toolbar', 'places', 'tree', 'path', 'stat']
  11052. */
  11053. ui : ['toolbar', 'places', 'tree', 'path', 'stat'],
  11054. /**
  11055. * Some UI plugins options.
  11056. * @type Object
  11057. */
  11058. uiOptions : {
  11059. // toolbar configuration
  11060. toolbar : [
  11061. ['home', 'back', 'forward', 'up', 'reload'],
  11062. ['netmount'],
  11063. ['mkdir', 'mkfile', 'upload'],
  11064. ['open', 'download', 'getfile'],
  11065. ['undo', 'redo'],
  11066. ['copy', 'cut', 'paste', 'rm', 'empty', 'hide'],
  11067. ['duplicate', 'rename', 'edit', 'resize', 'chmod'],
  11068. ['selectall', 'selectnone', 'selectinvert'],
  11069. ['quicklook', 'info'],
  11070. ['extract', 'archive'],
  11071. ['search'],
  11072. ['view', 'sort'],
  11073. ['preference', 'help'],
  11074. ['fullscreen']
  11075. ],
  11076. // toolbar extra options
  11077. toolbarExtra : {
  11078. // also displays the text label on the button (true / false / 'none')
  11079. displayTextLabel: false,
  11080. // Exclude `displayTextLabel` setting UA type
  11081. labelExcludeUA: ['Mobile'],
  11082. // auto hide on initial open
  11083. autoHideUA: ['Mobile'],
  11084. // Initial setting value of hide button in toolbar setting
  11085. defaultHides: ['home', 'reload'],
  11086. // show Preference button ('none', 'auto', 'always')
  11087. // If you do not include 'preference' in the context menu you should specify 'auto' or 'always'
  11088. showPreferenceButton: 'none',
  11089. // show Preference button into contextmenu of the toolbar (true / false)
  11090. preferenceInContextmenu: true
  11091. },
  11092. // directories tree options
  11093. tree : {
  11094. // set path info to attr title
  11095. attrTitle : true,
  11096. // expand current root on init
  11097. openRootOnLoad : true,
  11098. // expand current work directory on open
  11099. openCwdOnOpen : true,
  11100. // auto loading current directory parents and do expand their node.
  11101. syncTree : true,
  11102. // Maximum number of display of each child trees
  11103. // The tree of directories with children exceeding this number will be split
  11104. subTreeMax : 100,
  11105. // Numbar of max connctions of subdirs request
  11106. subdirsMaxConn : 2,
  11107. // Number of max simultaneous processing directory of subdirs
  11108. subdirsAtOnce : 5,
  11109. // Durations of each animations
  11110. durations : {
  11111. slideUpDown : 'fast',
  11112. autoScroll : 'fast'
  11113. }
  11114. // ,
  11115. // /**
  11116. // * Add CSS class name to navbar directories (optional)
  11117. // * see: https://github.com/Studio-42/elFinder/pull/1061,
  11118. // * https://github.com/Studio-42/elFinder/issues/1231
  11119. // *
  11120. // * @type Function
  11121. // */
  11122. // getClass: function(dir) {
  11123. // // e.g. This adds the directory's name (lowercase) with prefix as a CSS class
  11124. // return 'elfinder-tree-' + dir.name.replace(/[ "]/g, '').toLowerCase();
  11125. // }
  11126. },
  11127. // navbar options
  11128. navbar : {
  11129. minWidth : 150,
  11130. maxWidth : 500,
  11131. // auto hide on initial open
  11132. autoHideUA: [] // e.g. ['Mobile']
  11133. },
  11134. navdock : {
  11135. // disabled navdock ui
  11136. disabled : false,
  11137. // percentage of initial maximum height to work zone
  11138. initMaxHeight : '50%',
  11139. // percentage of maximum height to work zone by user resize action
  11140. maxHeight : '90%'
  11141. },
  11142. cwd : {
  11143. // display parent folder with ".." name :)
  11144. oldSchool : false,
  11145. // fm.UA types array to show item select checkboxes e.g. ['All'] or ['Mobile'] etc. default: ['Touch']
  11146. showSelectCheckboxUA : ['Touch'],
  11147. // Enable dragout by dragstart with Alt key or Shift key
  11148. metakeyDragout : true,
  11149. // file info columns displayed
  11150. listView : {
  11151. // name is always displayed, cols are ordered
  11152. // e.g. ['perm', 'date', 'size', 'kind', 'owner', 'group', 'mode']
  11153. // mode: 'mode'(by `fileModeStyle` setting), 'modestr'(rwxr-xr-x) , 'modeoct'(755), 'modeboth'(rwxr-xr-x (755))
  11154. // 'owner', 'group' and 'mode', It's necessary set volume driver option "statOwner" to `true`
  11155. // for custom, characters that can be used in the name is `a-z0-9_`
  11156. columns : ['perm', 'date', 'size', 'kind'],
  11157. // override this if you want custom columns name
  11158. // example
  11159. // columnsCustomName : {
  11160. // date : 'Last modification',
  11161. // kind : 'Mime type'
  11162. // }
  11163. columnsCustomName : {},
  11164. // fixed list header colmun
  11165. fixedHeader : true
  11166. },
  11167. // icons view setting
  11168. iconsView : {
  11169. // default icon size (0-3 in default CSS (cwd.css - elfinder-cwd-size[number]))
  11170. size: 0,
  11171. // number of maximum size (3 in default CSS (cwd.css - elfinder-cwd-size[number]))
  11172. // uses in preference.js
  11173. sizeMax: 3,
  11174. // Name of each size
  11175. sizeNames: {
  11176. 0: 'viewSmall',
  11177. 1: 'viewMedium',
  11178. 2: 'viewLarge',
  11179. 3: 'viewExtraLarge'
  11180. }
  11181. },
  11182. // /**
  11183. // * Add CSS class name to cwd directories (optional)
  11184. // * see: https://github.com/Studio-42/elFinder/pull/1061,
  11185. // * https://github.com/Studio-42/elFinder/issues/1231
  11186. // *
  11187. // * @type Function
  11188. // */
  11189. // ,
  11190. // getClass: function(file) {
  11191. // // e.g. This adds the directory's name (lowercase) with prefix as a CSS class
  11192. // return 'elfinder-cwd-' + file.name.replace(/[ "]/g, '').toLowerCase();
  11193. //}
  11194. //,
  11195. //// Template placeholders replacement rules for overwrite. see ui/cwd.js replacement
  11196. //replacement : {
  11197. // tooltip : function(f, fm) {
  11198. // var list = fm.viewType == 'list', // current view type
  11199. // query = fm.searchStatus.state == 2, // is in search results
  11200. // title = fm.formatDate(f) + (f.size > 0 ? ' ('+fm.formatSize(f.size)+')' : ''),
  11201. // info = '';
  11202. // if (query && f.path) {
  11203. // info = fm.escape(f.path.replace(/\/[^\/]*$/, ''));
  11204. // } else {
  11205. // info = f.tooltip? fm.escape(f.tooltip).replace(/\r/g, '&#13;') : '';
  11206. // }
  11207. // if (list) {
  11208. // info += (info? '&#13;' : '') + fm.escape(f.name);
  11209. // }
  11210. // return info? info + '&#13;' + title : title;
  11211. // }
  11212. //}
  11213. },
  11214. path : {
  11215. // Move to head of work zone without UI navbar
  11216. toWorkzoneWithoutNavbar : true
  11217. },
  11218. dialog : {
  11219. // Enable to auto focusing on mouse over in the target form element
  11220. focusOnMouseOver : true
  11221. },
  11222. toast : {
  11223. animate : {
  11224. // to show
  11225. showMethod: 'fadeIn', // fadeIn, slideDown, and show are built into jQuery
  11226. showDuration: 300, // milliseconds
  11227. showEasing: 'swing', // swing and linear are built into jQuery
  11228. // timeout to hide
  11229. timeOut: 3000,
  11230. // to hide
  11231. hideMethod: 'fadeOut',
  11232. hideDuration: 1500,
  11233. hideEasing: 'swing'
  11234. }
  11235. }
  11236. },
  11237. /**
  11238. * MIME regex of send HTTP header "Content-Disposition: inline" or allow preview in quicklook
  11239. * This option will overwrite by connector configuration
  11240. *
  11241. * @type String
  11242. * @default '^(?:(?:image|video|audio)|text/plain|application/pdf$)'
  11243. * @example
  11244. * dispInlineRegex : '.', // is allow inline of all of MIME types
  11245. * dispInlineRegex : '$^', // is not allow inline of all of MIME types
  11246. */
  11247. dispInlineRegex : '^(?:(?:image|video|audio)|application/(?:x-mpegURL|dash\+xml)|(?:text/plain|application/pdf)$)',
  11248. /**
  11249. * Display only required files by types
  11250. *
  11251. * @type Array
  11252. * @default []
  11253. * @example
  11254. * onlyMimes : ["image"] - display all images
  11255. * onlyMimes : ["image/png", "application/x-shockwave-flash"] - display png and flash
  11256. */
  11257. onlyMimes : [],
  11258. /**
  11259. * Custom files sort rules.
  11260. * All default rules (name/size/kind/date/perm/mode/owner/group) set in elFinder._sortRules
  11261. *
  11262. * @type {Object}
  11263. * @example
  11264. * sortRules : {
  11265. * name : function(file1, file2) { return file1.name.toLowerCase().localeCompare(file2.name.toLowerCase()); }
  11266. * }
  11267. */
  11268. sortRules : {},
  11269. /**
  11270. * Default sort type.
  11271. *
  11272. * @type {String}
  11273. */
  11274. sortType : 'name',
  11275. /**
  11276. * Default sort order.
  11277. *
  11278. * @type {String}
  11279. * @default "asc"
  11280. */
  11281. sortOrder : 'asc',
  11282. /**
  11283. * Display folders first?
  11284. *
  11285. * @type {Boolean}
  11286. * @default true
  11287. */
  11288. sortStickFolders : true,
  11289. /**
  11290. * Sort also applies to the treeview (null: disable this feature)
  11291. *
  11292. * @type Boolean|null
  11293. * @default false
  11294. */
  11295. sortAlsoTreeview : false,
  11296. /**
  11297. * If true - elFinder will formating dates itself,
  11298. * otherwise - backend date will be used.
  11299. *
  11300. * @type Boolean
  11301. */
  11302. clientFormatDate : true,
  11303. /**
  11304. * Show UTC dates.
  11305. * Required set clientFormatDate to true
  11306. *
  11307. * @type Boolean
  11308. */
  11309. UTCDate : false,
  11310. /**
  11311. * File modification datetime format.
  11312. * Value from selected language data is used by default.
  11313. * Set format here to overwrite it.
  11314. *
  11315. * @type String
  11316. * @default ""
  11317. */
  11318. dateFormat : '',
  11319. /**
  11320. * File modification datetime format in form "Yesterday 12:23:01".
  11321. * Value from selected language data is used by default.
  11322. * Set format here to overwrite it.
  11323. * Use $1 for "Today"/"Yesterday" placeholder
  11324. *
  11325. * @type String
  11326. * @default ""
  11327. * @example "$1 H:m:i"
  11328. */
  11329. fancyDateFormat : '',
  11330. /**
  11331. * Style of file mode at cwd-list, info dialog
  11332. * 'string' (ex. rwxr-xr-x) or 'octal' (ex. 755) or 'both' (ex. rwxr-xr-x (755))
  11333. *
  11334. * @type {String}
  11335. * @default 'both'
  11336. */
  11337. fileModeStyle : 'both',
  11338. /**
  11339. * elFinder width
  11340. *
  11341. * @type String|Number
  11342. * @default "auto"
  11343. */
  11344. width : 'auto',
  11345. /**
  11346. * elFinder node height
  11347. * Number: pixcel or String: Number + "%"
  11348. *
  11349. * @type Number | String
  11350. * @default 400
  11351. */
  11352. height : 400,
  11353. /**
  11354. * Base node object or selector
  11355. * Element which is the reference of the height percentage
  11356. *
  11357. * @type Object|String
  11358. * @default null | $(window) (if height is percentage)
  11359. **/
  11360. heightBase : null,
  11361. /**
  11362. * Make elFinder resizable if jquery ui resizable available
  11363. *
  11364. * @type Boolean
  11365. * @default true
  11366. */
  11367. resizable : true,
  11368. /**
  11369. * Timeout before open notifications dialogs
  11370. *
  11371. * @type Number
  11372. * @default 500 (.5 sec)
  11373. */
  11374. notifyDelay : 500,
  11375. /**
  11376. * Position CSS, Width of notifications dialogs
  11377. *
  11378. * @type Object
  11379. * @default {position: {}, width : null} - Apply CSS definition
  11380. * position: CSS object | null (null: position center & middle)
  11381. */
  11382. notifyDialog : {position : {}, width : null, canClose : false, hiddens : ['open']},
  11383. /**
  11384. * Dialog contained in the elFinder node
  11385. *
  11386. * @type Boolean
  11387. * @default false
  11388. */
  11389. dialogContained : false,
  11390. /**
  11391. * Allow shortcuts
  11392. *
  11393. * @type Boolean
  11394. * @default true
  11395. */
  11396. allowShortcuts : true,
  11397. /**
  11398. * Remeber last opened dir to open it after reload or in next session
  11399. *
  11400. * @type Boolean
  11401. * @default true
  11402. */
  11403. rememberLastDir : true,
  11404. /**
  11405. * Clear historys(elFinder) on reload(not browser) function
  11406. * Historys was cleared on Reload function on elFinder 2.0 (value is true)
  11407. *
  11408. * @type Boolean
  11409. * @default false
  11410. */
  11411. reloadClearHistory : false,
  11412. /**
  11413. * Use browser native history with supported browsers
  11414. *
  11415. * @type Boolean
  11416. * @default true
  11417. */
  11418. useBrowserHistory : true,
  11419. /**
  11420. * Lazy load config.
  11421. * How many files display at once?
  11422. *
  11423. * @type Number
  11424. * @default 50
  11425. */
  11426. showFiles : 50,
  11427. /**
  11428. * Lazy load config.
  11429. * Distance in px to cwd bottom edge to start display files
  11430. *
  11431. * @type Number
  11432. * @default 50
  11433. */
  11434. showThreshold : 50,
  11435. /**
  11436. * Additional rule to valid new file name.
  11437. * By default not allowed empty names or '..'
  11438. * This setting does not have a sense of security.
  11439. *
  11440. * @type false|RegExp|function
  11441. * @default false
  11442. * @example
  11443. * disable names with spaces:
  11444. * validName : /^[^\s]+$/,
  11445. */
  11446. validName : false,
  11447. /**
  11448. * Additional rule to filtering for browsing.
  11449. * This setting does not have a sense of security.
  11450. *
  11451. * The object `this` is elFinder instance object in this function
  11452. *
  11453. * @type false|RegExp|function
  11454. * @default false
  11455. * @example
  11456. * show only png and jpg files:
  11457. * fileFilter : /.*\.(png|jpg)$/i,
  11458. *
  11459. * show only image type files:
  11460. * fileFilter : function(file) { return file.mime && file.mime.match(/^image\//i); },
  11461. */
  11462. fileFilter : false,
  11463. /**
  11464. * Backup name suffix.
  11465. *
  11466. * @type String
  11467. * @default "~"
  11468. */
  11469. backupSuffix : '~',
  11470. /**
  11471. * Sync content interval
  11472. *
  11473. * @type Number
  11474. * @default 0 (do not sync)
  11475. */
  11476. sync : 0,
  11477. /**
  11478. * Sync start on load if sync value >= 1000
  11479. *
  11480. * @type Bool
  11481. * @default true
  11482. */
  11483. syncStart : true,
  11484. /**
  11485. * How many thumbnails create in one request
  11486. *
  11487. * @type Number
  11488. * @default 5
  11489. */
  11490. loadTmbs : 5,
  11491. /**
  11492. * Cookie option for browsersdoes not suppot localStorage
  11493. *
  11494. * @type Object
  11495. */
  11496. cookie : {
  11497. expires : 30,
  11498. domain : '',
  11499. path : '/',
  11500. secure : false
  11501. },
  11502. /**
  11503. * Contextmenu config
  11504. *
  11505. * @type Object
  11506. */
  11507. contextmenu : {
  11508. // navbarfolder menu
  11509. navbar : ['open', 'opennew', 'download', '|', 'upload', 'mkdir', '|', 'copy', 'cut', 'paste', 'duplicate', '|', 'rm', 'empty', 'hide', '|', 'rename', '|', 'archive', '|', 'places', 'info', 'chmod', 'netunmount'],
  11510. // current directory menu
  11511. cwd : ['undo', 'redo', '|', 'back', 'up', 'reload', '|', 'upload', 'mkdir', 'mkfile', 'paste', '|', 'empty', 'hide', '|', 'view', 'sort', 'selectall', 'colwidth', '|', 'places', 'info', 'chmod', 'netunmount', '|', 'fullscreen', '|', 'preference'],
  11512. // current directory file menu
  11513. files : ['getfile', '|' ,'open', 'opennew', 'download', 'opendir', 'quicklook', '|', 'upload', 'mkdir', '|', 'copy', 'cut', 'paste', 'duplicate', '|', 'rm', 'empty', 'hide', '|', 'rename', 'edit', 'resize', '|', 'archive', 'extract', '|', 'selectall', 'selectinvert', '|', 'places', 'info', 'chmod', 'netunmount']
  11514. },
  11515. /**
  11516. * elFinder node enable always
  11517. * This value will set to `true` if <body> has elFinder node only
  11518. *
  11519. * @type Bool
  11520. * @default false
  11521. */
  11522. enableAlways : false,
  11523. /**
  11524. * elFinder node enable by mouse over
  11525. *
  11526. * @type Bool
  11527. * @default true
  11528. */
  11529. enableByMouseOver : true,
  11530. /**
  11531. * Show window close confirm dialog
  11532. * Value is which state to show
  11533. * 'hasNotifyDialog', 'editingFile', 'hasSelectedItem' and 'hasClipboardData'
  11534. *
  11535. * @type Array
  11536. * @default ['hasNotifyDialog', 'editingFile']
  11537. */
  11538. windowCloseConfirm : ['hasNotifyDialog', 'editingFile'],
  11539. /**
  11540. * Function decoding 'raw' string converted to unicode
  11541. * It is used instead of fm.decodeRawString(str)
  11542. *
  11543. * @type Null|Function
  11544. */
  11545. rawStringDecoder : typeof Encoding === 'object' && $.isFunction(Encoding.convert)? function(str) {
  11546. return Encoding.convert(str, {
  11547. to: 'UNICODE',
  11548. type: 'string'
  11549. });
  11550. } : null,
  11551. /**
  11552. * Debug config
  11553. *
  11554. * @type Array|String('auto')|Boolean(true|false)
  11555. */
  11556. debug : ['error', 'warning', 'event-destroy'],
  11557. /**
  11558. * Show toast messeges of backend warning (if found data `debug.backendErrors` in backend results)
  11559. *
  11560. * @type Boolean|Object (toast options)
  11561. */
  11562. toastBackendWarn : true
  11563. };
  11564. /*
  11565. * File: /js/elFinder.options.netmount.js
  11566. */
  11567. /**
  11568. * Default elFinder config of commandsOptions.netmount
  11569. *
  11570. * @type Object
  11571. */
  11572. elFinder.prototype._options.commandsOptions.netmount = {
  11573. ftp: {
  11574. name : 'FTP',
  11575. inputs: {
  11576. host : $('<input type="text"/>'),
  11577. port : $('<input type="number" placeholder="21" class="elfinder-input-optional"/>'),
  11578. path : $('<input type="text" value="/"/>'),
  11579. user : $('<input type="text"/>'),
  11580. pass : $('<input type="password" autocomplete="new-password"/>'),
  11581. FTPS : $('<input type="checkbox" value="1" title="File Transfer Protocol over SSL/TLS"/>'),
  11582. encoding : $('<input type="text" placeholder="Optional" class="elfinder-input-optional"/>'),
  11583. locale : $('<input type="text" placeholder="Optional" class="elfinder-input-optional"/>')
  11584. }
  11585. },
  11586. dropbox2: elFinder.prototype.makeNetmountOptionOauth('dropbox2', 'Dropbox', 'Dropbox', {noOffline : true,
  11587. root : '/',
  11588. pathI18n : 'path',
  11589. integrate : {
  11590. title: 'Dropbox.com',
  11591. link: 'https://www.dropbox.com'
  11592. }
  11593. }),
  11594. googledrive: elFinder.prototype.makeNetmountOptionOauth('googledrive', 'Google Drive', 'Google', {
  11595. integrate : {
  11596. title: 'Google Drive',
  11597. link: 'https://www.google.com/drive/'
  11598. }
  11599. }),
  11600. onedrive: elFinder.prototype.makeNetmountOptionOauth('onedrive', 'One Drive', 'OneDrive', {
  11601. integrate : {
  11602. title: 'Microsoft OneDrive',
  11603. link: 'https://onedrive.live.com'
  11604. }
  11605. }),
  11606. box: elFinder.prototype.makeNetmountOptionOauth('box', 'Box', 'Box', {
  11607. noOffline : true,
  11608. integrate : {
  11609. title: 'Box.com',
  11610. link: 'https://www.box.com'
  11611. }
  11612. })
  11613. };
  11614. /*
  11615. * File: /js/elFinder.history.js
  11616. */
  11617. /**
  11618. * @class elFinder.history
  11619. * Store visited folders
  11620. * and provide "back" and "forward" methods
  11621. *
  11622. * @author Dmitry (dio) Levashov
  11623. */
  11624. elFinder.prototype.history = function(fm) {
  11625. var self = this,
  11626. /**
  11627. * Update history on "open" event?
  11628. *
  11629. * @type Boolean
  11630. */
  11631. update = true,
  11632. /**
  11633. * Directories hashes storage
  11634. *
  11635. * @type Array
  11636. */
  11637. history = [],
  11638. /**
  11639. * Current directory index in history
  11640. *
  11641. * @type Number
  11642. */
  11643. current,
  11644. /**
  11645. * Clear history
  11646. *
  11647. * @return void
  11648. */
  11649. reset = function() {
  11650. history = [fm.cwd().hash];
  11651. current = 0;
  11652. update = true;
  11653. },
  11654. /**
  11655. * Browser native history object
  11656. */
  11657. nativeHistory = (fm.options.useBrowserHistory && window.history && window.history.pushState)? window.history : null,
  11658. /**
  11659. * Open prev/next folder
  11660. *
  11661. * @Boolen open next folder?
  11662. * @return jQuery.Deferred
  11663. */
  11664. go = function(fwd) {
  11665. if ((fwd && self.canForward()) || (!fwd && self.canBack())) {
  11666. update = false;
  11667. return fm.exec('open', history[fwd ? ++current : --current]).fail(reset);
  11668. }
  11669. return $.Deferred().reject();
  11670. },
  11671. /**
  11672. * Sets the native history.
  11673. *
  11674. * @param String thash target hash
  11675. */
  11676. setNativeHistory = function(thash) {
  11677. if (nativeHistory && (! nativeHistory.state || nativeHistory.state.thash !== thash)) {
  11678. nativeHistory.pushState({thash: thash}, null, location.pathname + location.search + (thash? '#elf_' + thash : ''));
  11679. }
  11680. };
  11681. /**
  11682. * Return true if there is previous visited directories
  11683. *
  11684. * @return Boolen
  11685. */
  11686. this.canBack = function() {
  11687. return current > 0;
  11688. };
  11689. /**
  11690. * Return true if can go forward
  11691. *
  11692. * @return Boolen
  11693. */
  11694. this.canForward = function() {
  11695. return current < history.length - 1;
  11696. };
  11697. /**
  11698. * Go back
  11699. *
  11700. * @return void
  11701. */
  11702. this.back = go;
  11703. /**
  11704. * Go forward
  11705. *
  11706. * @return void
  11707. */
  11708. this.forward = function() {
  11709. return go(true);
  11710. };
  11711. // bind to elfinder events
  11712. fm.bind('init', function() {
  11713. if (nativeHistory && !nativeHistory.state) {
  11714. setNativeHistory(fm.startDir());
  11715. }
  11716. })
  11717. .open(function() {
  11718. var l = history.length,
  11719. cwd = fm.cwd().hash;
  11720. if (update) {
  11721. current >= 0 && l > current + 1 && history.splice(current+1);
  11722. history[history.length-1] != cwd && history.push(cwd);
  11723. current = history.length - 1;
  11724. }
  11725. update = true;
  11726. setNativeHistory(cwd);
  11727. })
  11728. .reload(fm.options.reloadClearHistory && reset);
  11729. };
  11730. /*
  11731. * File: /js/elFinder.command.js
  11732. */
  11733. /**
  11734. * elFinder command prototype
  11735. *
  11736. * @type elFinder.command
  11737. * @author Dmitry (dio) Levashov
  11738. */
  11739. elFinder.prototype.command = function(fm) {
  11740. /**
  11741. * elFinder instance
  11742. *
  11743. * @type elFinder
  11744. */
  11745. this.fm = fm;
  11746. /**
  11747. * Command name, same as class name
  11748. *
  11749. * @type String
  11750. */
  11751. this.name = '';
  11752. /**
  11753. * Dialog class name
  11754. *
  11755. * @type String
  11756. */
  11757. this.dialogClass = '';
  11758. /**
  11759. * Command icon class name with out 'elfinder-button-icon-'
  11760. * Use this.name if it is empty
  11761. *
  11762. * @type String
  11763. */
  11764. this.className = '';
  11765. /**
  11766. * Short command description
  11767. *
  11768. * @type String
  11769. */
  11770. this.title = '';
  11771. /**
  11772. * Linked(Child) commands name
  11773. * They are loaded together when tthis command is loaded.
  11774. *
  11775. * @type Array
  11776. */
  11777. this.linkedCmds = [];
  11778. /**
  11779. * Current command state
  11780. *
  11781. * @example
  11782. * this.state = -1; // command disabled
  11783. * this.state = 0; // command enabled
  11784. * this.state = 1; // command active (for example "fullscreen" command while elfinder in fullscreen mode)
  11785. * @default -1
  11786. * @type Number
  11787. */
  11788. this.state = -1;
  11789. /**
  11790. * If true, command can not be disabled by connector.
  11791. * @see this.update()
  11792. *
  11793. * @type Boolen
  11794. */
  11795. this.alwaysEnabled = false;
  11796. /**
  11797. * Do not change dirctory on removed current work directory
  11798. *
  11799. * @type Boolen
  11800. */
  11801. this.noChangeDirOnRemovedCwd = false;
  11802. /**
  11803. * If true, this means command was disabled by connector.
  11804. * @see this.update()
  11805. *
  11806. * @type Boolen
  11807. */
  11808. this._disabled = false;
  11809. /**
  11810. * If true, this command is disabled on serach results
  11811. *
  11812. * @type Boolean
  11813. */
  11814. this.disableOnSearch = false;
  11815. /**
  11816. * Call update() when event select fired
  11817. *
  11818. * @type Boolean
  11819. */
  11820. this.updateOnSelect = true;
  11821. /**
  11822. * Sync toolbar button title on change
  11823. *
  11824. * @type Boolean
  11825. */
  11826. this.syncTitleOnChange = false;
  11827. /**
  11828. * Keep display of the context menu when command execution
  11829. *
  11830. * @type Boolean
  11831. */
  11832. this.keepContextmenu = false;
  11833. /**
  11834. * elFinder events defaults handlers.
  11835. * Inside handlers "this" is current command object
  11836. *
  11837. * @type Object
  11838. */
  11839. this._handlers = {
  11840. enable : function() { this.update(void(0), this.value); },
  11841. disable : function() { this.update(-1, this.value); },
  11842. 'open reload load sync' : function() {
  11843. this._disabled = !(this.alwaysEnabled || this.fm.isCommandEnabled(this.name));
  11844. this.update(void(0), this.value);
  11845. this.change();
  11846. }
  11847. };
  11848. /**
  11849. * elFinder events handlers.
  11850. * Inside handlers "this" is current command object
  11851. *
  11852. * @type Object
  11853. */
  11854. this.handlers = {};
  11855. /**
  11856. * Shortcuts
  11857. *
  11858. * @type Array
  11859. */
  11860. this.shortcuts = [];
  11861. /**
  11862. * Command options
  11863. *
  11864. * @type Object
  11865. */
  11866. this.options = {ui : 'button'};
  11867. /**
  11868. * Callback functions on `change` event
  11869. *
  11870. * @type Array
  11871. */
  11872. this.listeners = [];
  11873. /**
  11874. * Prepare object -
  11875. * bind events and shortcuts
  11876. *
  11877. * @return void
  11878. */
  11879. this.setup = function(name, opts) {
  11880. var self = this,
  11881. fm = this.fm,
  11882. setCallback = function(s) {
  11883. var cb = s.callback || function(e) {
  11884. fm.exec(self.name, void(0), {
  11885. _userAction: true,
  11886. _currentType: 'shortcut'
  11887. });
  11888. };
  11889. s.callback = function(e) {
  11890. var enabled, checks = {};
  11891. if (self.enabled()) {
  11892. if (fm.searchStatus.state < 2) {
  11893. enabled = fm.isCommandEnabled(self.name);
  11894. } else {
  11895. $.each(fm.selected(), function(i, h) {
  11896. if (fm.optionsByHashes[h]) {
  11897. checks[h] = true;
  11898. } else {
  11899. $.each(fm.volOptions, function(id) {
  11900. if (!checks[id] && h.indexOf(id) === 0) {
  11901. checks[id] = true;
  11902. return false;
  11903. }
  11904. });
  11905. }
  11906. });
  11907. $.each(checks, function(h) {
  11908. enabled = fm.isCommandEnabled(self.name, h);
  11909. if (! enabled) {
  11910. return false;
  11911. }
  11912. });
  11913. }
  11914. if (enabled) {
  11915. self.event = e;
  11916. cb.call(self);
  11917. delete self.event;
  11918. }
  11919. }
  11920. };
  11921. },
  11922. i, s, sc;
  11923. this.name = name;
  11924. this.title = fm.messages['cmd'+name] ? fm.i18n('cmd'+name)
  11925. : ((this.extendsCmd && fm.messages['cmd'+this.extendsCmd]) ? fm.i18n('cmd'+this.extendsCmd) : name);
  11926. this.options = Object.assign({}, this.options, opts);
  11927. this.listeners = [];
  11928. this.dialogClass = 'elfinder-dialog-' + name;
  11929. if (opts.shortcuts) {
  11930. if (typeof opts.shortcuts === 'function') {
  11931. sc = opts.shortcuts(this.fm, this.shortcuts);
  11932. } else if (Array.isArray(opts.shortcuts)) {
  11933. sc = opts.shortcuts;
  11934. }
  11935. this.shortcuts = sc || [];
  11936. }
  11937. if (this.updateOnSelect) {
  11938. this._handlers.select = function() { this.update(void(0), this.value); };
  11939. }
  11940. $.each(Object.assign({}, self._handlers, self.handlers), function(cmd, handler) {
  11941. fm.bind(cmd, $.proxy(handler, self));
  11942. });
  11943. for (i = 0; i < this.shortcuts.length; i++) {
  11944. s = this.shortcuts[i];
  11945. setCallback(s);
  11946. !s.description && (s.description = this.title);
  11947. fm.shortcut(s);
  11948. }
  11949. if (this.disableOnSearch) {
  11950. fm.bind('search searchend', function() {
  11951. self._disabled = this.type === 'search'? true : ! (this.alwaysEnabled || fm.isCommandEnabled(name));
  11952. self.update(void(0), self.value);
  11953. });
  11954. }
  11955. this.init();
  11956. };
  11957. /**
  11958. * Command specific init stuffs
  11959. *
  11960. * @return void
  11961. */
  11962. this.init = function() {};
  11963. /**
  11964. * Exec command
  11965. *
  11966. * @param Array target files hashes
  11967. * @param Array|Object command value
  11968. * @return $.Deferred
  11969. */
  11970. this.exec = function(files, opts) {
  11971. return $.Deferred().reject();
  11972. };
  11973. this.getUndo = function(opts, resData) {
  11974. return false;
  11975. };
  11976. /**
  11977. * Return true if command disabled.
  11978. *
  11979. * @return Boolen
  11980. */
  11981. this.disabled = function() {
  11982. return this.state < 0;
  11983. };
  11984. /**
  11985. * Return true if command enabled.
  11986. *
  11987. * @return Boolen
  11988. */
  11989. this.enabled = function() {
  11990. return this.state > -1;
  11991. };
  11992. /**
  11993. * Return true if command active.
  11994. *
  11995. * @return Boolen
  11996. */
  11997. this.active = function() {
  11998. return this.state > 0;
  11999. };
  12000. /**
  12001. * Return current command state.
  12002. * Must be overloaded in most commands
  12003. *
  12004. * @return Number
  12005. */
  12006. this.getstate = function() {
  12007. return -1;
  12008. };
  12009. /**
  12010. * Update command state/value
  12011. * and rize 'change' event if smth changed
  12012. *
  12013. * @param Number new state or undefined to auto update state
  12014. * @param mixed new value
  12015. * @return void
  12016. */
  12017. this.update = function(s, v) {
  12018. var state = this.state,
  12019. value = this.value;
  12020. if (this._disabled && this.fm.searchStatus === 0) {
  12021. this.state = -1;
  12022. } else {
  12023. this.state = s !== void(0) ? s : this.getstate();
  12024. }
  12025. this.value = v;
  12026. if (state != this.state || value != this.value) {
  12027. this.change();
  12028. }
  12029. };
  12030. /**
  12031. * Bind handler / fire 'change' event.
  12032. *
  12033. * @param Function|undefined event callback
  12034. * @return void
  12035. */
  12036. this.change = function(c) {
  12037. var cmd, i;
  12038. if (typeof(c) === 'function') {
  12039. this.listeners.push(c);
  12040. } else {
  12041. for (i = 0; i < this.listeners.length; i++) {
  12042. cmd = this.listeners[i];
  12043. try {
  12044. cmd(this.state, this.value);
  12045. } catch (e) {
  12046. this.fm.debug('error', e);
  12047. }
  12048. }
  12049. }
  12050. return this;
  12051. };
  12052. /**
  12053. * With argument check given files hashes and return list of existed files hashes.
  12054. * Without argument return selected files hashes.
  12055. *
  12056. * @param Array|String|void hashes
  12057. * @return Array
  12058. */
  12059. this.hashes = function(hashes) {
  12060. return hashes
  12061. ? $.grep(Array.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) ? true : false; })
  12062. : fm.selected();
  12063. };
  12064. /**
  12065. * Return only existed files from given fils hashes | selected files
  12066. *
  12067. * @param Array|String|void hashes
  12068. * @return Array
  12069. */
  12070. this.files = function(hashes) {
  12071. var fm = this.fm;
  12072. return hashes
  12073. ? $.map(Array.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) || null; })
  12074. : fm.selectedFiles();
  12075. };
  12076. /**
  12077. * Wrapper to fm.dialog()
  12078. *
  12079. * @param String|DOMElement content
  12080. * @param Object options
  12081. * @return Object jQuery element object
  12082. */
  12083. this.fmDialog = function(content, options) {
  12084. if (options.cssClass) {
  12085. options.cssClass += ' ' + this.dialogClass;
  12086. } else {
  12087. options.cssClass = this.dialogClass;
  12088. }
  12089. return this.fm.dialog(content, options);
  12090. };
  12091. };
  12092. /*
  12093. * File: /js/elFinder.resources.js
  12094. */
  12095. /**
  12096. * elFinder resources registry.
  12097. * Store shared data
  12098. *
  12099. * @type Object
  12100. * @author Dmitry (dio) Levashov
  12101. **/
  12102. elFinder.prototype.resources = {
  12103. 'class' : {
  12104. hover : 'ui-state-hover',
  12105. active : 'ui-state-active',
  12106. disabled : 'ui-state-disabled',
  12107. draggable : 'ui-draggable',
  12108. droppable : 'ui-droppable',
  12109. adroppable : 'elfinder-droppable-active',
  12110. cwdfile : 'elfinder-cwd-file',
  12111. cwd : 'elfinder-cwd',
  12112. tree : 'elfinder-tree',
  12113. treeroot : 'elfinder-navbar-root',
  12114. navdir : 'elfinder-navbar-dir',
  12115. navdirwrap : 'elfinder-navbar-dir-wrapper',
  12116. navarrow : 'elfinder-navbar-arrow',
  12117. navsubtree : 'elfinder-navbar-subtree',
  12118. navcollapse : 'elfinder-navbar-collapsed',
  12119. navexpand : 'elfinder-navbar-expanded',
  12120. treedir : 'elfinder-tree-dir',
  12121. placedir : 'elfinder-place-dir',
  12122. searchbtn : 'elfinder-button-search',
  12123. editing : 'elfinder-to-editing',
  12124. preventback : 'elfinder-prevent-back',
  12125. tabstab : 'ui-state-default ui-tabs-tab ui-corner-top ui-tab',
  12126. tabsactive : 'ui-tabs-active ui-state-active'
  12127. },
  12128. tpl : {
  12129. perms : '<span class="elfinder-perms"></span>',
  12130. lock : '<span class="elfinder-lock"></span>',
  12131. symlink : '<span class="elfinder-symlink"></span>',
  12132. navicon : '<span class="elfinder-nav-icon"></span>',
  12133. navspinner : '<span class="elfinder-spinner elfinder-navbar-spinner"></span>',
  12134. navdir : '<div class="elfinder-navbar-wrapper{root}"><span id="{id}" class="ui-corner-all elfinder-navbar-dir {cssclass}"{title}><span class="elfinder-navbar-arrow"></span><span class="elfinder-navbar-icon" {style}></span>{symlink}{permissions}{name}</span><div class="elfinder-navbar-subtree" style="display:none"></div></div>',
  12135. placedir : '<div class="elfinder-navbar-wrapper"><span id="{id}" class="ui-corner-all elfinder-navbar-dir {cssclass}"{title}><span class="elfinder-navbar-arrow"></span><span class="elfinder-navbar-icon" {style}></span>{symlink}{permissions}{name}</span><div class="elfinder-navbar-subtree" style="display:none"></div></div>'
  12136. },
  12137. // mimes.text will be overwritten with connector config if `textMimes` is included in initial response
  12138. // @see php/elFInder.class.php `public static $textMimes`
  12139. mimes : {
  12140. text : [
  12141. 'application/dash+xml',
  12142. 'application/docbook+xml',
  12143. 'application/javascript',
  12144. 'application/json',
  12145. 'application/plt',
  12146. 'application/sat',
  12147. 'application/sql',
  12148. 'application/step',
  12149. 'application/vnd.hp-hpgl',
  12150. 'application/x-awk',
  12151. 'application/x-config',
  12152. 'application/x-csh',
  12153. 'application/x-empty',
  12154. 'application/x-mpegurl',
  12155. 'application/x-perl',
  12156. 'application/x-php',
  12157. 'application/x-web-config',
  12158. 'application/xhtml+xml',
  12159. 'application/xml',
  12160. 'audio/x-mp3-playlist',
  12161. 'image/cgm',
  12162. 'image/svg+xml',
  12163. 'image/vnd.dxf',
  12164. 'model/iges'
  12165. ]
  12166. },
  12167. mixin : {
  12168. make : function() {
  12169. var self = this,
  12170. fm = this.fm,
  12171. cmd = this.name,
  12172. req = this.requestCmd || cmd,
  12173. wz = fm.getUI('workzone'),
  12174. org = (this.origin && this.origin === 'navbar')? 'tree' : 'cwd',
  12175. tree = (org === 'tree'),
  12176. find = tree? 'navHash2Elm' : 'cwdHash2Elm',
  12177. tarea= (! tree && fm.storage('view') != 'list'),
  12178. sel = fm.selected(),
  12179. move = this.move || false,
  12180. empty= wz.hasClass('elfinder-cwd-wrapper-empty'),
  12181. unselect = function() {
  12182. requestAnimationFrame(function() {
  12183. input && input.trigger('blur');
  12184. });
  12185. },
  12186. rest = function(){
  12187. if (!overlay.is(':hidden')) {
  12188. overlay.elfinderoverlay('hide').off('click close', cancel);
  12189. }
  12190. if (nnode) {
  12191. pnode.removeClass('ui-front')
  12192. .css('position', '')
  12193. .off('unselect.'+fm.namespace, unselect);
  12194. if (tarea) {
  12195. nnode && nnode.css('max-height', '');
  12196. } else if (!tree) {
  12197. pnode.css('width', '')
  12198. .parent('td').css('overflow', '');
  12199. }
  12200. }
  12201. }, colwidth,
  12202. dfrd = $.Deferred()
  12203. .fail(function(error) {
  12204. dstCls && dst.attr('class', dstCls);
  12205. empty && wz.addClass('elfinder-cwd-wrapper-empty');
  12206. if (sel) {
  12207. move && fm.trigger('unlockfiles', {files: sel});
  12208. fm.clipboard([]);
  12209. fm.trigger('selectfiles', { files: sel });
  12210. }
  12211. error && fm.error(error);
  12212. })
  12213. .always(function() {
  12214. rest();
  12215. cleanup();
  12216. fm.enable().unbind('open', openCallback).trigger('resMixinMake');
  12217. }),
  12218. id = 'tmp_'+parseInt(Math.random()*100000),
  12219. phash = this.data && this.data.target? this.data.target : (tree? fm.file(sel[0]).hash : fm.cwd().hash),
  12220. date = new Date(),
  12221. file = {
  12222. hash : id,
  12223. phash : phash,
  12224. name : fm.uniqueName(this.prefix, phash),
  12225. mime : this.mime,
  12226. read : true,
  12227. write : true,
  12228. date : 'Today '+date.getHours()+':'+date.getMinutes(),
  12229. move : move
  12230. },
  12231. dum = fm.getUI(org).trigger('create.'+fm.namespace, file),
  12232. data = this.data || {},
  12233. node = fm[find](id),
  12234. nnode, pnode,
  12235. overlay = fm.getUI('overlay'),
  12236. cleanup = function() {
  12237. if (node && node.length) {
  12238. input.off();
  12239. node.hide();
  12240. fm.unselectfiles({files : [id]}).unbind('resize', resize);
  12241. requestAnimationFrame(function() {
  12242. if (tree) {
  12243. node.closest('.elfinder-navbar-wrapper').remove();
  12244. } else {
  12245. node.remove();
  12246. }
  12247. });
  12248. }
  12249. },
  12250. cancel = function(e) {
  12251. if (!overlay.is(':hidden')) {
  12252. pnode.css('z-index', '');
  12253. }
  12254. if (! inError) {
  12255. cleanup();
  12256. dfrd.reject();
  12257. if (e) {
  12258. e.stopPropagation();
  12259. e.preventDefault();
  12260. }
  12261. }
  12262. },
  12263. input = $(tarea? '<textarea></textarea>' : '<input type="text"/>')
  12264. .on('keyup text', function(){
  12265. if (tarea) {
  12266. this.style.height = '1px';
  12267. this.style.height = this.scrollHeight + 'px';
  12268. } else if (colwidth) {
  12269. this.style.width = colwidth + 'px';
  12270. if (this.scrollWidth > colwidth) {
  12271. this.style.width = this.scrollWidth + 10 + 'px';
  12272. }
  12273. }
  12274. })
  12275. .on('keydown', function(e) {
  12276. e.stopImmediatePropagation();
  12277. if (e.keyCode == $.ui.keyCode.ESCAPE) {
  12278. dfrd.reject();
  12279. } else if (e.keyCode == $.ui.keyCode.ENTER) {
  12280. e.preventDefault();
  12281. input.trigger('blur');
  12282. }
  12283. })
  12284. .on('mousedown click dblclick', function(e) {
  12285. e.stopPropagation();
  12286. if (e.type === 'dblclick') {
  12287. e.preventDefault();
  12288. }
  12289. })
  12290. .on('blur', function() {
  12291. var name = $.trim(input.val()),
  12292. parent = input.parent(),
  12293. valid = true,
  12294. cut;
  12295. if (!overlay.is(':hidden')) {
  12296. pnode.css('z-index', '');
  12297. }
  12298. if (name === '') {
  12299. return cancel();
  12300. }
  12301. if (!inError && parent.length) {
  12302. if (fm.options.validName && fm.options.validName.test) {
  12303. try {
  12304. valid = fm.options.validName.test(name);
  12305. } catch(e) {
  12306. valid = false;
  12307. }
  12308. }
  12309. if (!name || name === '.' || name === '..' || !valid) {
  12310. inError = true;
  12311. fm.error(file.mime === 'directory'? 'errInvDirname' : 'errInvName', {modal: true, close: function(){setTimeout(select, 120);}});
  12312. return false;
  12313. }
  12314. if (fm.fileByName(name, phash)) {
  12315. inError = true;
  12316. fm.error(['errExists', name], {modal: true, close: function(){setTimeout(select, 120);}});
  12317. return false;
  12318. }
  12319. cut = (sel && move)? fm.exec('cut', sel) : null;
  12320. $.when(cut)
  12321. .done(function() {
  12322. var toast = {},
  12323. nextAct = {};
  12324. rest();
  12325. input.hide().before($('<span>').text(name));
  12326. fm.lockfiles({files : [id]});
  12327. fm.request({
  12328. data : Object.assign({cmd : req, name : name, target : phash}, data || {}),
  12329. notify : {type : req, cnt : 1},
  12330. preventFail : true,
  12331. syncOnFail : true,
  12332. navigate : {toast : toast},
  12333. })
  12334. .fail(function(error) {
  12335. fm.unlockfiles({files : [id]});
  12336. inError = true;
  12337. input.show().prev().remove();
  12338. fm.error(error, {
  12339. modal: true,
  12340. close: function() {
  12341. if (Array.isArray(error) && $.inArray('errUploadMime', error) !== -1) {
  12342. dfrd.notify('errUploadMime').reject();
  12343. } else {
  12344. setTimeout(select, 120);
  12345. }
  12346. }
  12347. });
  12348. })
  12349. .done(function(data) {
  12350. if (data && data.added && data.added[0]) {
  12351. var item = data.added[0],
  12352. dirhash = item.hash,
  12353. newItem = fm[find](dirhash),
  12354. acts = {
  12355. 'directory' : { cmd: 'open', msg: 'cmdopendir' },
  12356. 'text' : { cmd: 'edit', msg: 'cmdedit' },
  12357. 'default' : { cmd: 'open', msg: 'cmdopen' }
  12358. },
  12359. tmpMimes;
  12360. if (sel && move) {
  12361. fm.one(req+'done', function() {
  12362. fm.exec('paste', dirhash);
  12363. });
  12364. }
  12365. if (!move) {
  12366. if (fm.mimeIsText(item.mime) && !fm.mimesCanMakeEmpty[item.mime] && fm.mimeTypes[item.mime]) {
  12367. fm.trigger('canMakeEmptyFile', {mimes: [item.mime], unshift: true});
  12368. tmpMimes = {};
  12369. tmpMimes[item.mime] = fm.mimeTypes[item.mime];
  12370. fm.storage('mkfileTextMimes', Object.assign(tmpMimes, fm.storage('mkfileTextMimes') || {}));
  12371. }
  12372. Object.assign(nextAct, nextAction || acts[item.mime] || acts[item.mime.split('/')[0]] || acts[(fm.mimesCanMakeEmpty[item.mime] || $.inArray(item.mime, fm.resources.mimes.text) !== -1) ? 'text' : 'none'] || acts['default']);
  12373. Object.assign(toast, nextAct.cmd ? {
  12374. incwd : {msg: fm.i18n(['complete', fm.i18n('cmd'+cmd)]), action: nextAct},
  12375. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmd'+cmd)]), action: nextAct}
  12376. } : {
  12377. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmd'+cmd)])}
  12378. });
  12379. }
  12380. }
  12381. dfrd.resolve(data);
  12382. });
  12383. })
  12384. .fail(function() {
  12385. dfrd.reject();
  12386. });
  12387. }
  12388. })
  12389. .on('dragenter dragleave dragover drop', function(e) {
  12390. // stop bubbling to prevent upload with native drop event
  12391. e.stopPropagation();
  12392. }),
  12393. select = function() {
  12394. var name = fm.splitFileExtention(input.val())[0];
  12395. if (!inError && fm.UA.Mobile && !fm.UA.iOS) { // since iOS has a bug? (z-index not effect) so disable it
  12396. overlay.on('click close', cancel).elfinderoverlay('show');
  12397. pnode.css('z-index', overlay.css('z-index') + 1);
  12398. }
  12399. inError = false;
  12400. ! fm.enabled() && fm.enable();
  12401. input.trigger('focus').trigger('select');
  12402. input[0].setSelectionRange && input[0].setSelectionRange(0, name.length);
  12403. },
  12404. resize = function() {
  12405. node.trigger('scrolltoview', {blink : false});
  12406. },
  12407. openCallback = function() {
  12408. dfrd && (dfrd.state() === 'pending') && dfrd.reject();
  12409. },
  12410. inError = false,
  12411. nextAction,
  12412. // for tree
  12413. dst, dstCls, collapsed, expanded, arrow, subtree;
  12414. if (!fm.isCommandEnabled(req, phash) || !node.length) {
  12415. return dfrd.reject();
  12416. }
  12417. if ($.isPlainObject(self.nextAction)){
  12418. nextAction = Object.assign({}, self.nextAction);
  12419. }
  12420. if (tree) {
  12421. dst = fm[find](phash);
  12422. collapsed = fm.res('class', 'navcollapse');
  12423. expanded = fm.res('class', 'navexpand');
  12424. arrow = fm.res('class', 'navarrow');
  12425. subtree = fm.res('class', 'navsubtree');
  12426. node.closest('.'+subtree).show();
  12427. if (! dst.hasClass(collapsed)) {
  12428. dstCls = dst.attr('class');
  12429. dst.addClass(collapsed+' '+expanded+' elfinder-subtree-loaded');
  12430. }
  12431. if (dst.is('.'+collapsed+':not(.'+expanded+')')) {
  12432. dst.children('.'+arrow).trigger('click').data('dfrd').done(function() {
  12433. if (input.val() === file.name) {
  12434. input.val(fm.uniqueName(self.prefix, phash)).trigger('select').trigger('focus');
  12435. }
  12436. });
  12437. }
  12438. nnode = node.contents().filter(function(){ return this.nodeType==3 && $(this).parent().attr('id') === fm.navHash2Id(file.hash); });
  12439. pnode = nnode.parent();
  12440. nnode.replaceWith(input.val(file.name));
  12441. } else {
  12442. empty && wz.removeClass('elfinder-cwd-wrapper-empty');
  12443. nnode = node.find('.elfinder-cwd-filename');
  12444. pnode = nnode.parent();
  12445. if (tarea) {
  12446. nnode.css('max-height', 'none');
  12447. } else {
  12448. colwidth = pnode.width();
  12449. pnode.width(colwidth - 15)
  12450. .parent('td').css('overflow', 'visible');
  12451. }
  12452. nnode.empty().append(input.val(file.name));
  12453. }
  12454. pnode.addClass('ui-front')
  12455. .css('position', 'relative')
  12456. .on('unselect.'+fm.namespace, unselect);
  12457. fm.bind('resize', resize).one('open', openCallback);
  12458. input.trigger('keyup');
  12459. select();
  12460. return dfrd;
  12461. }
  12462. },
  12463. blink: function(elm, mode) {
  12464. var acts = {
  12465. slowonce : function(){elm.hide().delay(250).fadeIn(750).delay(500).fadeOut(3500);},
  12466. lookme : function(){elm.show().fadeOut(500).fadeIn(750);}
  12467. }, func;
  12468. mode = mode || 'slowonce';
  12469. func = acts[mode] || acts['lookme'];
  12470. elm.stop(true, true);
  12471. func();
  12472. }
  12473. };
  12474. /*
  12475. * File: /js/jquery.dialogelfinder.js
  12476. */
  12477. /**
  12478. * @class dialogelfinder - open elFinder in dialog window
  12479. *
  12480. * @param Object elFinder options with dialog options
  12481. * @example
  12482. * $(selector).dialogelfinder({
  12483. * // some elfinder options
  12484. * title : 'My files', // dialog title, default = "Files"
  12485. * width : 850, // dialog width, default 840
  12486. * autoOpen : false, // if false - dialog will not be opened after init, default = true
  12487. * destroyOnClose : true // destroy elFinder on close dialog, default = false
  12488. * })
  12489. * @author Dmitry (dio) Levashov
  12490. **/
  12491. $.fn.dialogelfinder = function(opts, opts2) {
  12492. var position = 'elfinderPosition',
  12493. destroy = 'elfinderDestroyOnClose',
  12494. node, pos;
  12495. if ($.isPlainObject(opts)) {
  12496. this.not('.elfinder').each(function() {
  12497. opts.handlers = opts.handlers || {};
  12498. var node = $(this),
  12499. doc = $(document),
  12500. toolbar = $('<div class="ui-widget-header dialogelfinder-drag ui-corner-top">'+(opts.title || 'Files')+'</div>'),
  12501. button = $('<a href="#" class="dialogelfinder-drag-close ui-corner-all"><span class="ui-icon ui-icon-closethick"> </span></a>')
  12502. .appendTo(toolbar)
  12503. .on('click', function(e) {
  12504. e.preventDefault();
  12505. node.dialogelfinder('close');
  12506. }),
  12507. init = opts.handlers.init,
  12508. elfinder;
  12509. opts.handlers.init = function(e, fm) {
  12510. node.prepend(toolbar);
  12511. init && init(e, fm);
  12512. };
  12513. elfinder = node.addClass('elfinder dialogelfinder touch-punch')
  12514. .css('position', 'absolute')
  12515. .hide()
  12516. .appendTo('body')
  12517. .draggable({
  12518. handle : '.dialogelfinder-drag',
  12519. containment : 'window',
  12520. stop : function() {
  12521. node.trigger('resize');
  12522. elfinder.trigger('resize');
  12523. }
  12524. })
  12525. .elfinder(opts, opts2)
  12526. .elfinder('instance');
  12527. elfinder.reloadCallback = function(o, o2) {
  12528. elfinder.destroy();
  12529. o.handlers.init = init;
  12530. node.dialogelfinder(o, o2).dialogelfinder('open');
  12531. };
  12532. node.width(parseInt(node.width()) || 840) // fix width if set to "auto"
  12533. .data(destroy, !!opts.destroyOnClose)
  12534. .find('.elfinder-toolbar').removeClass('ui-corner-top');
  12535. opts.position && node.data(position, opts.position);
  12536. opts.autoOpen !== false && $(this).dialogelfinder('open');
  12537. });
  12538. } else {
  12539. if (opts === 'open') {
  12540. node = $(this);
  12541. pos = node.data(position) || {
  12542. top : parseInt($(document).scrollTop() + ($(window).height() < node.height() ? 2 : ($(window).height() - node.height())/2)),
  12543. left : parseInt($(document).scrollLeft() + ($(window).width() < node.width() ? 2 : ($(window).width() - node.width())/2))
  12544. };
  12545. if (node.is(':hidden')) {
  12546. node.addClass('ui-front').css(pos).show().trigger('resize');
  12547. setTimeout(function() {
  12548. // fix resize icon position and make elfinder active
  12549. node.trigger('resize').trigger('mousedown');
  12550. }, 200);
  12551. }
  12552. } else if (opts === 'close') {
  12553. node = $(this).removeClass('ui-front');
  12554. if (node.is(':visible')) {
  12555. !!node.data(destroy)
  12556. ? node.elfinder('destroy').remove()
  12557. : node.elfinder('close');
  12558. }
  12559. } else if (opts === 'instance') {
  12560. return $(this).getElFinder();
  12561. }
  12562. }
  12563. return this;
  12564. };
  12565. /*
  12566. * File: /js/i18n/elfinder.en.js
  12567. */
  12568. /**
  12569. * English translation
  12570. * @author Troex Nevelin <troex@fury.scancode.ru>
  12571. * @author Naoki Sawada <hypweb+elfinder@gmail.com>
  12572. * @version 2020-01-16
  12573. */
  12574. // elfinder.en.js is integrated into elfinder.(full|min).js by jake build
  12575. if (typeof elFinder === 'function' && elFinder.prototype.i18) {
  12576. elFinder.prototype.i18.en = {
  12577. translator : 'Troex Nevelin &lt;troex@fury.scancode.ru&gt;, Naoki Sawada &lt;hypweb+elfinder@gmail.com&gt;',
  12578. language : 'English',
  12579. direction : 'ltr',
  12580. dateFormat : 'M d, Y h:i A', // will show like: Aug 24, 2018 04:39 PM
  12581. fancyDateFormat : '$1 h:i A', // will show like: Today 04:39 PM
  12582. nonameDateFormat : 'ymd-His', // noname upload will show like: 180824-163916
  12583. messages : {
  12584. /********************************** errors **********************************/
  12585. 'error' : 'Error',
  12586. 'errUnknown' : 'Unknown error.',
  12587. 'errUnknownCmd' : 'Unknown command.',
  12588. 'errJqui' : 'Invalid jQuery UI configuration. Selectable, draggable and droppable components must be included.',
  12589. 'errNode' : 'elFinder requires DOM Element to be created.',
  12590. 'errURL' : 'Invalid elFinder configuration! URL option is not set.',
  12591. 'errAccess' : 'Access denied.',
  12592. 'errConnect' : 'Unable to connect to backend.',
  12593. 'errAbort' : 'Connection aborted.',
  12594. 'errTimeout' : 'Connection timeout.',
  12595. 'errNotFound' : 'Backend not found.',
  12596. 'errResponse' : 'Invalid backend response.',
  12597. 'errConf' : 'Invalid backend configuration.',
  12598. 'errJSON' : 'PHP JSON module not installed.',
  12599. 'errNoVolumes' : 'Readable volumes not available.',
  12600. 'errCmdParams' : 'Invalid parameters for command "$1".',
  12601. 'errDataNotJSON' : 'Data is not JSON.',
  12602. 'errDataEmpty' : 'Data is empty.',
  12603. 'errCmdReq' : 'Backend request requires command name.',
  12604. 'errOpen' : 'Unable to open "$1".',
  12605. 'errNotFolder' : 'Object is not a folder.',
  12606. 'errNotFile' : 'Object is not a file.',
  12607. 'errRead' : 'Unable to read "$1".',
  12608. 'errWrite' : 'Unable to write into "$1".',
  12609. 'errPerm' : 'Permission denied.',
  12610. 'errLocked' : '"$1" is locked and can not be renamed, moved or removed.',
  12611. 'errExists' : 'Item named "$1" already exists.',
  12612. 'errInvName' : 'Invalid file name.',
  12613. 'errInvDirname' : 'Invalid folder name.', // from v2.1.24 added 12.4.2017
  12614. 'errFolderNotFound' : 'Folder not found.',
  12615. 'errFileNotFound' : 'File not found.',
  12616. 'errTrgFolderNotFound' : 'Target folder "$1" not found.',
  12617. 'errPopup' : 'Browser prevented opening popup window. To open file enable it in browser options.',
  12618. 'errMkdir' : 'Unable to create folder "$1".',
  12619. 'errMkfile' : 'Unable to create file "$1".',
  12620. 'errRename' : 'Unable to rename "$1".',
  12621. 'errCopyFrom' : 'Copying files from volume "$1" not allowed.',
  12622. 'errCopyTo' : 'Copying files to volume "$1" not allowed.',
  12623. 'errMkOutLink' : 'Unable to create a link to outside the volume root.', // from v2.1 added 03.10.2015
  12624. 'errUpload' : 'Upload error.', // old name - errUploadCommon
  12625. 'errUploadFile' : 'Unable to upload "$1".', // old name - errUpload
  12626. 'errUploadNoFiles' : 'No files found for upload.',
  12627. 'errUploadTotalSize' : 'Data exceeds the maximum allowed size.', // old name - errMaxSize
  12628. 'errUploadFileSize' : 'File exceeds maximum allowed size.', // old name - errFileMaxSize
  12629. 'errUploadMime' : 'File type not allowed.',
  12630. 'errUploadTransfer' : '"$1" transfer error.',
  12631. 'errUploadTemp' : 'Unable to make temporary file for upload.', // from v2.1 added 26.09.2015
  12632. 'errNotReplace' : 'Object "$1" already exists at this location and can not be replaced by object with another type.', // new
  12633. 'errReplace' : 'Unable to replace "$1".',
  12634. 'errSave' : 'Unable to save "$1".',
  12635. 'errCopy' : 'Unable to copy "$1".',
  12636. 'errMove' : 'Unable to move "$1".',
  12637. 'errCopyInItself' : 'Unable to copy "$1" into itself.',
  12638. 'errRm' : 'Unable to remove "$1".',
  12639. 'errTrash' : 'Unable into trash.', // from v2.1.24 added 30.4.2017
  12640. 'errRmSrc' : 'Unable remove source file(s).',
  12641. 'errExtract' : 'Unable to extract files from "$1".',
  12642. 'errArchive' : 'Unable to create archive.',
  12643. 'errArcType' : 'Unsupported archive type.',
  12644. 'errNoArchive' : 'File is not archive or has unsupported archive type.',
  12645. 'errCmdNoSupport' : 'Backend does not support this command.',
  12646. 'errReplByChild' : 'The folder "$1" can\'t be replaced by an item it contains.',
  12647. 'errArcSymlinks' : 'For security reason denied to unpack archives contains symlinks or files with not allowed names.', // edited 24.06.2012
  12648. 'errArcMaxSize' : 'Archive files exceeds maximum allowed size.',
  12649. 'errResize' : 'Unable to resize "$1".',
  12650. 'errResizeDegree' : 'Invalid rotate degree.', // added 7.3.2013
  12651. 'errResizeRotate' : 'Unable to rotate image.', // added 7.3.2013
  12652. 'errResizeSize' : 'Invalid image size.', // added 7.3.2013
  12653. 'errResizeNoChange' : 'Image size not changed.', // added 7.3.2013
  12654. 'errUsupportType' : 'Unsupported file type.',
  12655. 'errNotUTF8Content' : 'File "$1" is not in UTF-8 and cannot be edited.', // added 9.11.2011
  12656. 'errNetMount' : 'Unable to mount "$1".', // added 17.04.2012
  12657. 'errNetMountNoDriver' : 'Unsupported protocol.', // added 17.04.2012
  12658. 'errNetMountFailed' : 'Mount failed.', // added 17.04.2012
  12659. 'errNetMountHostReq' : 'Host required.', // added 18.04.2012
  12660. 'errSessionExpires' : 'Your session has expired due to inactivity.',
  12661. 'errCreatingTempDir' : 'Unable to create temporary directory: "$1"',
  12662. 'errFtpDownloadFile' : 'Unable to download file from FTP: "$1"',
  12663. 'errFtpUploadFile' : 'Unable to upload file to FTP: "$1"',
  12664. 'errFtpMkdir' : 'Unable to create remote directory on FTP: "$1"',
  12665. 'errArchiveExec' : 'Error while archiving files: "$1"',
  12666. 'errExtractExec' : 'Error while extracting files: "$1"',
  12667. 'errNetUnMount' : 'Unable to unmount.', // from v2.1 added 30.04.2012
  12668. 'errConvUTF8' : 'Not convertible to UTF-8', // from v2.1 added 08.04.2014
  12669. 'errFolderUpload' : 'Try the modern browser, If you\'d like to upload the folder.', // from v2.1 added 26.6.2015
  12670. 'errSearchTimeout' : 'Timed out while searching "$1". Search result is partial.', // from v2.1 added 12.1.2016
  12671. 'errReauthRequire' : 'Re-authorization is required.', // from v2.1.10 added 24.3.2016
  12672. 'errMaxTargets' : 'Max number of selectable items is $1.', // from v2.1.17 added 17.10.2016
  12673. 'errRestore' : 'Unable to restore from the trash. Can\'t identify the restore destination.', // from v2.1.24 added 3.5.2017
  12674. 'errEditorNotFound' : 'Editor not found to this file type.', // from v2.1.25 added 23.5.2017
  12675. 'errServerError' : 'Error occurred on the server side.', // from v2.1.25 added 16.6.2017
  12676. 'errEmpty' : 'Unable to empty folder "$1".', // from v2.1.25 added 22.6.2017
  12677. 'moreErrors' : 'There are $1 more errors.', // from v2.1.44 added 9.12.2018
  12678. /******************************* commands names ********************************/
  12679. 'cmdarchive' : 'Create archive',
  12680. 'cmdback' : 'Back',
  12681. 'cmdcopy' : 'Copy',
  12682. 'cmdcut' : 'Cut',
  12683. 'cmddownload' : 'Download',
  12684. 'cmdduplicate' : 'Duplicate',
  12685. 'cmdedit' : 'Edit file',
  12686. 'cmdextract' : 'Extract files from archive',
  12687. 'cmdforward' : 'Forward',
  12688. 'cmdgetfile' : 'Select files',
  12689. 'cmdhelp' : 'About this software',
  12690. 'cmdhome' : 'Root',
  12691. 'cmdinfo' : 'Get info',
  12692. 'cmdmkdir' : 'New folder',
  12693. 'cmdmkdirin' : 'Into New Folder', // from v2.1.7 added 19.2.2016
  12694. 'cmdmkfile' : 'New file',
  12695. 'cmdopen' : 'Open',
  12696. 'cmdpaste' : 'Paste',
  12697. 'cmdquicklook' : 'Preview',
  12698. 'cmdreload' : 'Reload',
  12699. 'cmdrename' : 'Rename',
  12700. 'cmdrm' : 'Delete',
  12701. 'cmdtrash' : 'Into trash', //from v2.1.24 added 29.4.2017
  12702. 'cmdrestore' : 'Restore', //from v2.1.24 added 3.5.2017
  12703. 'cmdsearch' : 'Find files',
  12704. 'cmdup' : 'Go to parent folder',
  12705. 'cmdupload' : 'Upload files',
  12706. 'cmdview' : 'View',
  12707. 'cmdresize' : 'Resize & Rotate',
  12708. 'cmdsort' : 'Sort',
  12709. 'cmdnetmount' : 'Mount network volume', // added 18.04.2012
  12710. 'cmdnetunmount': 'Unmount', // from v2.1 added 30.04.2012
  12711. 'cmdplaces' : 'To Places', // added 28.12.2014
  12712. 'cmdchmod' : 'Change mode', // from v2.1 added 20.6.2015
  12713. 'cmdopendir' : 'Open a folder', // from v2.1 added 13.1.2016
  12714. 'cmdcolwidth' : 'Reset column width', // from v2.1.13 added 12.06.2016
  12715. 'cmdfullscreen': 'Full Screen', // from v2.1.15 added 03.08.2016
  12716. 'cmdmove' : 'Move', // from v2.1.15 added 21.08.2016
  12717. 'cmdempty' : 'Empty the folder', // from v2.1.25 added 22.06.2017
  12718. 'cmdundo' : 'Undo', // from v2.1.27 added 31.07.2017
  12719. 'cmdredo' : 'Redo', // from v2.1.27 added 31.07.2017
  12720. 'cmdpreference': 'Preferences', // from v2.1.27 added 03.08.2017
  12721. 'cmdselectall' : 'Select all', // from v2.1.28 added 15.08.2017
  12722. 'cmdselectnone': 'Select none', // from v2.1.28 added 15.08.2017
  12723. 'cmdselectinvert': 'Invert selection', // from v2.1.28 added 15.08.2017
  12724. 'cmdopennew' : 'Open in new window', // from v2.1.38 added 3.4.2018
  12725. 'cmdhide' : 'Hide (Preference)', // from v2.1.41 added 24.7.2018
  12726. /*********************************** buttons ***********************************/
  12727. 'btnClose' : 'Close',
  12728. 'btnSave' : 'Save',
  12729. 'btnRm' : 'Remove',
  12730. 'btnApply' : 'Apply',
  12731. 'btnCancel' : 'Cancel',
  12732. 'btnNo' : 'No',
  12733. 'btnYes' : 'Yes',
  12734. 'btnMount' : 'Mount', // added 18.04.2012
  12735. 'btnApprove': 'Goto $1 & approve', // from v2.1 added 26.04.2012
  12736. 'btnUnmount': 'Unmount', // from v2.1 added 30.04.2012
  12737. 'btnConv' : 'Convert', // from v2.1 added 08.04.2014
  12738. 'btnCwd' : 'Here', // from v2.1 added 22.5.2015
  12739. 'btnVolume' : 'Volume', // from v2.1 added 22.5.2015
  12740. 'btnAll' : 'All', // from v2.1 added 22.5.2015
  12741. 'btnMime' : 'MIME Type', // from v2.1 added 22.5.2015
  12742. 'btnFileName':'Filename', // from v2.1 added 22.5.2015
  12743. 'btnSaveClose': 'Save & Close', // from v2.1 added 12.6.2015
  12744. 'btnBackup' : 'Backup', // fromv2.1 added 28.11.2015
  12745. 'btnRename' : 'Rename', // from v2.1.24 added 6.4.2017
  12746. 'btnRenameAll' : 'Rename(All)', // from v2.1.24 added 6.4.2017
  12747. 'btnPrevious' : 'Prev ($1/$2)', // from v2.1.24 added 11.5.2017
  12748. 'btnNext' : 'Next ($1/$2)', // from v2.1.24 added 11.5.2017
  12749. 'btnSaveAs' : 'Save As', // from v2.1.25 added 24.5.2017
  12750. /******************************** notifications ********************************/
  12751. 'ntfopen' : 'Open folder',
  12752. 'ntffile' : 'Open file',
  12753. 'ntfreload' : 'Reload folder content',
  12754. 'ntfmkdir' : 'Creating folder',
  12755. 'ntfmkfile' : 'Creating files',
  12756. 'ntfrm' : 'Delete items',
  12757. 'ntfcopy' : 'Copy items',
  12758. 'ntfmove' : 'Move items',
  12759. 'ntfprepare' : 'Checking existing items',
  12760. 'ntfrename' : 'Rename files',
  12761. 'ntfupload' : 'Uploading files',
  12762. 'ntfdownload' : 'Downloading files',
  12763. 'ntfsave' : 'Save files',
  12764. 'ntfarchive' : 'Creating archive',
  12765. 'ntfextract' : 'Extracting files from archive',
  12766. 'ntfsearch' : 'Searching files',
  12767. 'ntfresize' : 'Resizing images',
  12768. 'ntfsmth' : 'Doing something',
  12769. 'ntfloadimg' : 'Loading image',
  12770. 'ntfnetmount' : 'Mounting network volume', // added 18.04.2012
  12771. 'ntfnetunmount': 'Unmounting network volume', // from v2.1 added 30.04.2012
  12772. 'ntfdim' : 'Acquiring image dimension', // added 20.05.2013
  12773. 'ntfreaddir' : 'Reading folder infomation', // from v2.1 added 01.07.2013
  12774. 'ntfurl' : 'Getting URL of link', // from v2.1 added 11.03.2014
  12775. 'ntfchmod' : 'Changing file mode', // from v2.1 added 20.6.2015
  12776. 'ntfpreupload': 'Verifying upload file name', // from v2.1 added 31.11.2015
  12777. 'ntfzipdl' : 'Creating a file for download', // from v2.1.7 added 23.1.2016
  12778. 'ntfparents' : 'Getting path infomation', // from v2.1.17 added 2.11.2016
  12779. 'ntfchunkmerge': 'Processing the uploaded file', // from v2.1.17 added 2.11.2016
  12780. 'ntftrash' : 'Doing throw in the trash', // from v2.1.24 added 2.5.2017
  12781. 'ntfrestore' : 'Doing restore from the trash', // from v2.1.24 added 3.5.2017
  12782. 'ntfchkdir' : 'Checking destination folder', // from v2.1.24 added 3.5.2017
  12783. 'ntfundo' : 'Undoing previous operation', // from v2.1.27 added 31.07.2017
  12784. 'ntfredo' : 'Redoing previous undone', // from v2.1.27 added 31.07.2017
  12785. 'ntfchkcontent' : 'Checking contents', // from v2.1.41 added 3.8.2018
  12786. /*********************************** volumes *********************************/
  12787. 'volume_Trash' : 'Trash', //from v2.1.24 added 29.4.2017
  12788. /************************************ dates **********************************/
  12789. 'dateUnknown' : 'unknown',
  12790. 'Today' : 'Today',
  12791. 'Yesterday' : 'Yesterday',
  12792. 'msJan' : 'Jan',
  12793. 'msFeb' : 'Feb',
  12794. 'msMar' : 'Mar',
  12795. 'msApr' : 'Apr',
  12796. 'msMay' : 'May',
  12797. 'msJun' : 'Jun',
  12798. 'msJul' : 'Jul',
  12799. 'msAug' : 'Aug',
  12800. 'msSep' : 'Sep',
  12801. 'msOct' : 'Oct',
  12802. 'msNov' : 'Nov',
  12803. 'msDec' : 'Dec',
  12804. 'January' : 'January',
  12805. 'February' : 'February',
  12806. 'March' : 'March',
  12807. 'April' : 'April',
  12808. 'May' : 'May',
  12809. 'June' : 'June',
  12810. 'July' : 'July',
  12811. 'August' : 'August',
  12812. 'September' : 'September',
  12813. 'October' : 'October',
  12814. 'November' : 'November',
  12815. 'December' : 'December',
  12816. 'Sunday' : 'Sunday',
  12817. 'Monday' : 'Monday',
  12818. 'Tuesday' : 'Tuesday',
  12819. 'Wednesday' : 'Wednesday',
  12820. 'Thursday' : 'Thursday',
  12821. 'Friday' : 'Friday',
  12822. 'Saturday' : 'Saturday',
  12823. 'Sun' : 'Sun',
  12824. 'Mon' : 'Mon',
  12825. 'Tue' : 'Tue',
  12826. 'Wed' : 'Wed',
  12827. 'Thu' : 'Thu',
  12828. 'Fri' : 'Fri',
  12829. 'Sat' : 'Sat',
  12830. /******************************** sort variants ********************************/
  12831. 'sortname' : 'by name',
  12832. 'sortkind' : 'by kind',
  12833. 'sortsize' : 'by size',
  12834. 'sortdate' : 'by date',
  12835. 'sortFoldersFirst' : 'Folders first',
  12836. 'sortperm' : 'by permission', // from v2.1.13 added 13.06.2016
  12837. 'sortmode' : 'by mode', // from v2.1.13 added 13.06.2016
  12838. 'sortowner' : 'by owner', // from v2.1.13 added 13.06.2016
  12839. 'sortgroup' : 'by group', // from v2.1.13 added 13.06.2016
  12840. 'sortAlsoTreeview' : 'Also Treeview', // from v2.1.15 added 01.08.2016
  12841. /********************************** new items **********************************/
  12842. 'untitled file.txt' : 'NewFile.txt', // added 10.11.2015
  12843. 'untitled folder' : 'NewFolder', // added 10.11.2015
  12844. 'Archive' : 'NewArchive', // from v2.1 added 10.11.2015
  12845. 'untitled file' : 'NewFile.$1', // from v2.1.41 added 6.8.2018
  12846. 'extentionfile' : '$1: File', // from v2.1.41 added 6.8.2018
  12847. 'extentiontype' : '$1: $2', // from v2.1.43 added 17.10.2018
  12848. /********************************** messages **********************************/
  12849. 'confirmReq' : 'Confirmation required',
  12850. 'confirmRm' : 'Are you sure you want to permanently remove items?<br/>This cannot be undone!',
  12851. 'confirmRepl' : 'Replace old file with new one? (If it contains folders, it will be merged. To backup and replace, select Backup.)',
  12852. 'confirmRest' : 'Replace existing item with the item in trash?', // fromv2.1.24 added 5.5.2017
  12853. 'confirmConvUTF8' : 'Not in UTF-8<br/>Convert to UTF-8?<br/>Contents become UTF-8 by saving after conversion.', // from v2.1 added 08.04.2014
  12854. 'confirmNonUTF8' : 'Character encoding of this file couldn\'t be detected. It need to temporarily convert to UTF-8 for editting.<br/>Please select character encoding of this file.', // from v2.1.19 added 28.11.2016
  12855. 'confirmNotSave' : 'It has been modified.<br/>Losing work if you do not save changes.', // from v2.1 added 15.7.2015
  12856. 'confirmTrash' : 'Are you sure you want to move items to trash bin?', //from v2.1.24 added 29.4.2017
  12857. 'confirmMove' : 'Are you sure you want to move items to "$1"?', //from v2.1.50 added 27.7.2019
  12858. 'apllyAll' : 'Apply to all',
  12859. 'name' : 'Name',
  12860. 'size' : 'Size',
  12861. 'perms' : 'Permissions',
  12862. 'modify' : 'Modified',
  12863. 'kind' : 'Kind',
  12864. 'read' : 'read',
  12865. 'write' : 'write',
  12866. 'noaccess' : 'no access',
  12867. 'and' : 'and',
  12868. 'unknown' : 'unknown',
  12869. 'selectall' : 'Select all items',
  12870. 'selectfiles' : 'Select item(s)',
  12871. 'selectffile' : 'Select first item',
  12872. 'selectlfile' : 'Select last item',
  12873. 'viewlist' : 'List view',
  12874. 'viewicons' : 'Icons view',
  12875. 'viewSmall' : 'Small icons', // from v2.1.39 added 22.5.2018
  12876. 'viewMedium' : 'Medium icons', // from v2.1.39 added 22.5.2018
  12877. 'viewLarge' : 'Large icons', // from v2.1.39 added 22.5.2018
  12878. 'viewExtraLarge' : 'Extra large icons', // from v2.1.39 added 22.5.2018
  12879. 'places' : 'Places',
  12880. 'calc' : 'Calculate',
  12881. 'path' : 'Path',
  12882. 'aliasfor' : 'Alias for',
  12883. 'locked' : 'Locked',
  12884. 'dim' : 'Dimensions',
  12885. 'files' : 'Files',
  12886. 'folders' : 'Folders',
  12887. 'items' : 'Items',
  12888. 'yes' : 'yes',
  12889. 'no' : 'no',
  12890. 'link' : 'Link',
  12891. 'searcresult' : 'Search results',
  12892. 'selected' : 'selected items',
  12893. 'about' : 'About',
  12894. 'shortcuts' : 'Shortcuts',
  12895. 'help' : 'Help',
  12896. 'webfm' : 'Web file manager',
  12897. 'ver' : 'Version',
  12898. 'protocolver' : 'protocol version',
  12899. 'homepage' : 'Project home',
  12900. 'docs' : 'Documentation',
  12901. 'github' : 'Fork us on GitHub',
  12902. 'twitter' : 'Follow us on Twitter',
  12903. 'facebook' : 'Join us on Facebook',
  12904. 'team' : 'Team',
  12905. 'chiefdev' : 'chief developer',
  12906. 'developer' : 'developer',
  12907. 'contributor' : 'contributor',
  12908. 'maintainer' : 'maintainer',
  12909. 'translator' : 'translator',
  12910. 'icons' : 'Icons',
  12911. 'dontforget' : 'and don\'t forget to take your towel',
  12912. 'shortcutsof' : 'Shortcuts disabled',
  12913. 'dropFiles' : 'Drop files here',
  12914. 'or' : 'or',
  12915. 'selectForUpload' : 'Select files',
  12916. 'moveFiles' : 'Move items',
  12917. 'copyFiles' : 'Copy items',
  12918. 'restoreFiles' : 'Restore items', // from v2.1.24 added 5.5.2017
  12919. 'rmFromPlaces' : 'Remove from places',
  12920. 'aspectRatio' : 'Aspect ratio',
  12921. 'scale' : 'Scale',
  12922. 'width' : 'Width',
  12923. 'height' : 'Height',
  12924. 'resize' : 'Resize',
  12925. 'crop' : 'Crop',
  12926. 'rotate' : 'Rotate',
  12927. 'rotate-cw' : 'Rotate 90 degrees CW',
  12928. 'rotate-ccw' : 'Rotate 90 degrees CCW',
  12929. 'degree' : '°',
  12930. 'netMountDialogTitle' : 'Mount network volume', // added 18.04.2012
  12931. 'protocol' : 'Protocol', // added 18.04.2012
  12932. 'host' : 'Host', // added 18.04.2012
  12933. 'port' : 'Port', // added 18.04.2012
  12934. 'user' : 'User', // added 18.04.2012
  12935. 'pass' : 'Password', // added 18.04.2012
  12936. 'confirmUnmount' : 'Are you sure to unmount $1?', // from v2.1 added 30.04.2012
  12937. 'dropFilesBrowser': 'Drop or Paste files from browser', // from v2.1 added 30.05.2012
  12938. 'dropPasteFiles' : 'Drop files, Paste URLs or images(clipboard) here', // from v2.1 added 07.04.2014
  12939. 'encoding' : 'Encoding', // from v2.1 added 19.12.2014
  12940. 'locale' : 'Locale', // from v2.1 added 19.12.2014
  12941. 'searchTarget' : 'Target: $1', // from v2.1 added 22.5.2015
  12942. 'searchMime' : 'Search by input MIME Type', // from v2.1 added 22.5.2015
  12943. 'owner' : 'Owner', // from v2.1 added 20.6.2015
  12944. 'group' : 'Group', // from v2.1 added 20.6.2015
  12945. 'other' : 'Other', // from v2.1 added 20.6.2015
  12946. 'execute' : 'Execute', // from v2.1 added 20.6.2015
  12947. 'perm' : 'Permission', // from v2.1 added 20.6.2015
  12948. 'mode' : 'Mode', // from v2.1 added 20.6.2015
  12949. 'emptyFolder' : 'Folder is empty', // from v2.1.6 added 30.12.2015
  12950. 'emptyFolderDrop' : 'Folder is empty\\A Drop to add items', // from v2.1.6 added 30.12.2015
  12951. 'emptyFolderLTap' : 'Folder is empty\\A Long tap to add items', // from v2.1.6 added 30.12.2015
  12952. 'quality' : 'Quality', // from v2.1.6 added 5.1.2016
  12953. 'autoSync' : 'Auto sync', // from v2.1.6 added 10.1.2016
  12954. 'moveUp' : 'Move up', // from v2.1.6 added 18.1.2016
  12955. 'getLink' : 'Get URL link', // from v2.1.7 added 9.2.2016
  12956. 'selectedItems' : 'Selected items ($1)', // from v2.1.7 added 2.19.2016
  12957. 'folderId' : 'Folder ID', // from v2.1.10 added 3.25.2016
  12958. 'offlineAccess' : 'Allow offline access', // from v2.1.10 added 3.25.2016
  12959. 'reAuth' : 'To re-authenticate', // from v2.1.10 added 3.25.2016
  12960. 'nowLoading' : 'Now loading...', // from v2.1.12 added 4.26.2016
  12961. 'openMulti' : 'Open multiple files', // from v2.1.12 added 5.14.2016
  12962. 'openMultiConfirm': 'You are trying to open the $1 files. Are you sure you want to open in browser?', // from v2.1.12 added 5.14.2016
  12963. 'emptySearch' : 'Search results is empty in search target.', // from v2.1.12 added 5.16.2016
  12964. 'editingFile' : 'It is editing a file.', // from v2.1.13 added 6.3.2016
  12965. 'hasSelected' : 'You have selected $1 items.', // from v2.1.13 added 6.3.2016
  12966. 'hasClipboard' : 'You have $1 items in the clipboard.', // from v2.1.13 added 6.3.2016
  12967. 'incSearchOnly' : 'Incremental search is only from the current view.', // from v2.1.13 added 6.30.2016
  12968. 'reinstate' : 'Reinstate', // from v2.1.15 added 3.8.2016
  12969. 'complete' : '$1 complete', // from v2.1.15 added 21.8.2016
  12970. 'contextmenu' : 'Context menu', // from v2.1.15 added 9.9.2016
  12971. 'pageTurning' : 'Page turning', // from v2.1.15 added 10.9.2016
  12972. 'volumeRoots' : 'Volume roots', // from v2.1.16 added 16.9.2016
  12973. 'reset' : 'Reset', // from v2.1.16 added 1.10.2016
  12974. 'bgcolor' : 'Background color', // from v2.1.16 added 1.10.2016
  12975. 'colorPicker' : 'Color picker', // from v2.1.16 added 1.10.2016
  12976. '8pxgrid' : '8px Grid', // from v2.1.16 added 4.10.2016
  12977. 'enabled' : 'Enabled', // from v2.1.16 added 4.10.2016
  12978. 'disabled' : 'Disabled', // from v2.1.16 added 4.10.2016
  12979. 'emptyIncSearch' : 'Search results is empty in current view.\\A Press [Enter] to expand search target.', // from v2.1.16 added 5.10.2016
  12980. 'emptyLetSearch' : 'First letter search results is empty in current view.', // from v2.1.23 added 24.3.2017
  12981. 'textLabel' : 'Text label', // from v2.1.17 added 13.10.2016
  12982. 'minsLeft' : '$1 mins left', // from v2.1.17 added 13.11.2016
  12983. 'openAsEncoding' : 'Reopen with selected encoding', // from v2.1.19 added 2.12.2016
  12984. 'saveAsEncoding' : 'Save with the selected encoding', // from v2.1.19 added 2.12.2016
  12985. 'selectFolder' : 'Select folder', // from v2.1.20 added 13.12.2016
  12986. 'firstLetterSearch': 'First letter search', // from v2.1.23 added 24.3.2017
  12987. 'presets' : 'Presets', // from v2.1.25 added 26.5.2017
  12988. 'tooManyToTrash' : 'It\'s too many items so it can\'t into trash.', // from v2.1.25 added 9.6.2017
  12989. 'TextArea' : 'TextArea', // from v2.1.25 added 14.6.2017
  12990. 'folderToEmpty' : 'Empty the folder "$1".', // from v2.1.25 added 22.6.2017
  12991. 'filderIsEmpty' : 'There are no items in a folder "$1".', // from v2.1.25 added 22.6.2017
  12992. 'preference' : 'Preference', // from v2.1.26 added 28.6.2017
  12993. 'language' : 'Language', // from v2.1.26 added 28.6.2017
  12994. 'clearBrowserData': 'Initialize the settings saved in this browser', // from v2.1.26 added 28.6.2017
  12995. 'toolbarPref' : 'Toolbar settings', // from v2.1.27 added 2.8.2017
  12996. 'charsLeft' : '... $1 chars left.', // from v2.1.29 added 30.8.2017
  12997. 'linesLeft' : '... $1 lines left.', // from v2.1.52 added 16.1.2020
  12998. 'sum' : 'Sum', // from v2.1.29 added 28.9.2017
  12999. 'roughFileSize' : 'Rough file size', // from v2.1.30 added 2.11.2017
  13000. 'autoFocusDialog' : 'Focus on the element of dialog with mouseover', // from v2.1.30 added 2.11.2017
  13001. 'select' : 'Select', // from v2.1.30 added 23.11.2017
  13002. 'selectAction' : 'Action when select file', // from v2.1.30 added 23.11.2017
  13003. 'useStoredEditor' : 'Open with the editor used last time', // from v2.1.30 added 23.11.2017
  13004. 'selectinvert' : 'Invert selection', // from v2.1.30 added 25.11.2017
  13005. 'renameMultiple' : 'Are you sure you want to rename $1 selected items like $2?<br/>This cannot be undone!', // from v2.1.31 added 4.12.2017
  13006. 'batchRename' : 'Batch rename', // from v2.1.31 added 8.12.2017
  13007. 'plusNumber' : '+ Number', // from v2.1.31 added 8.12.2017
  13008. 'asPrefix' : 'Add prefix', // from v2.1.31 added 8.12.2017
  13009. 'asSuffix' : 'Add suffix', // from v2.1.31 added 8.12.2017
  13010. 'changeExtention' : 'Change extention', // from v2.1.31 added 8.12.2017
  13011. 'columnPref' : 'Columns settings (List view)', // from v2.1.32 added 6.2.2018
  13012. 'reflectOnImmediate' : 'All changes will reflect immediately to the archive.', // from v2.1.33 added 2.3.2018
  13013. 'reflectOnUnmount' : 'Any changes will not reflect until un-mount this volume.', // from v2.1.33 added 2.3.2018
  13014. 'unmountChildren' : 'The following volume(s) mounted on this volume also unmounted. Are you sure to unmount it?', // from v2.1.33 added 5.3.2018
  13015. 'selectionInfo' : 'Selection Info', // from v2.1.33 added 7.3.2018
  13016. 'hashChecker' : 'Algorithms to show the file hash', // from v2.1.33 added 10.3.2018
  13017. 'infoItems' : 'Info Items (Selection Info Panel)', // from v2.1.38 added 28.3.2018
  13018. 'pressAgainToExit': 'Press again to exit.', // from v2.1.38 added 1.4.2018
  13019. 'toolbar' : 'Toolbar', // from v2.1.38 added 4.4.2018
  13020. 'workspace' : 'Work Space', // from v2.1.38 added 4.4.2018
  13021. 'dialog' : 'Dialog', // from v2.1.38 added 4.4.2018
  13022. 'all' : 'All', // from v2.1.38 added 4.4.2018
  13023. 'iconSize' : 'Icon Size (Icons view)', // from v2.1.39 added 7.5.2018
  13024. 'editorMaximized' : 'Open the maximized editor window', // from v2.1.40 added 30.6.2018
  13025. 'editorConvNoApi' : 'Because conversion by API is not currently available, please convert on the website.', //from v2.1.40 added 8.7.2018
  13026. 'editorConvNeedUpload' : 'After conversion, you must be upload with the item URL or a downloaded file to save the converted file.', //from v2.1.40 added 8.7.2018
  13027. 'convertOn' : 'Convert on the site of $1', // from v2.1.40 added 10.7.2018
  13028. 'integrations' : 'Integrations', // from v2.1.40 added 11.7.2018
  13029. 'integrationWith' : 'This elFinder has the following external services integrated. Please check the terms of use, privacy policy, etc. before using it.', // from v2.1.40 added 11.7.2018
  13030. 'showHidden' : 'Show hidden items', // from v2.1.41 added 24.7.2018
  13031. 'hideHidden' : 'Hide hidden items', // from v2.1.41 added 24.7.2018
  13032. 'toggleHidden' : 'Show/Hide hidden items', // from v2.1.41 added 24.7.2018
  13033. 'makefileTypes' : 'File types to enable with "New file"', // from v2.1.41 added 7.8.2018
  13034. 'typeOfTextfile' : 'Type of the Text file', // from v2.1.41 added 7.8.2018
  13035. 'add' : 'Add', // from v2.1.41 added 7.8.2018
  13036. 'theme' : 'Theme', // from v2.1.43 added 19.10.2018
  13037. 'default' : 'Default', // from v2.1.43 added 19.10.2018
  13038. 'description' : 'Description', // from v2.1.43 added 19.10.2018
  13039. 'website' : 'Website', // from v2.1.43 added 19.10.2018
  13040. 'author' : 'Author', // from v2.1.43 added 19.10.2018
  13041. 'email' : 'Email', // from v2.1.43 added 19.10.2018
  13042. 'license' : 'License', // from v2.1.43 added 19.10.2018
  13043. 'exportToSave' : 'This item can\'t be saved. To avoid losing the edits you need to export to your PC.', // from v2.1.44 added 1.12.2018
  13044. 'dblclickToSelect': 'Double click on the file to select it.', // from v2.1.47 added 22.1.2019
  13045. 'useFullscreen' : 'Use fullscreen mode', // from v2.1.47 added 19.2.2019
  13046. /********************************** mimetypes **********************************/
  13047. 'kindUnknown' : 'Unknown',
  13048. 'kindRoot' : 'Volume Root', // from v2.1.16 added 16.10.2016
  13049. 'kindFolder' : 'Folder',
  13050. 'kindSelects' : 'Selections', // from v2.1.29 added 29.8.2017
  13051. 'kindAlias' : 'Alias',
  13052. 'kindAliasBroken' : 'Broken alias',
  13053. // applications
  13054. 'kindApp' : 'Application',
  13055. 'kindPostscript' : 'Postscript document',
  13056. 'kindMsOffice' : 'Microsoft Office document',
  13057. 'kindMsWord' : 'Microsoft Word document',
  13058. 'kindMsExcel' : 'Microsoft Excel document',
  13059. 'kindMsPP' : 'Microsoft Powerpoint presentation',
  13060. 'kindOO' : 'Open Office document',
  13061. 'kindAppFlash' : 'Flash application',
  13062. 'kindPDF' : 'Portable Document Format (PDF)',
  13063. 'kindTorrent' : 'Bittorrent file',
  13064. 'kind7z' : '7z archive',
  13065. 'kindTAR' : 'TAR archive',
  13066. 'kindGZIP' : 'GZIP archive',
  13067. 'kindBZIP' : 'BZIP archive',
  13068. 'kindXZ' : 'XZ archive',
  13069. 'kindZIP' : 'ZIP archive',
  13070. 'kindRAR' : 'RAR archive',
  13071. 'kindJAR' : 'Java JAR file',
  13072. 'kindTTF' : 'True Type font',
  13073. 'kindOTF' : 'Open Type font',
  13074. 'kindRPM' : 'RPM package',
  13075. // texts
  13076. 'kindText' : 'Text document',
  13077. 'kindTextPlain' : 'Plain text',
  13078. 'kindPHP' : 'PHP source',
  13079. 'kindCSS' : 'Cascading style sheet',
  13080. 'kindHTML' : 'HTML document',
  13081. 'kindJS' : 'Javascript source',
  13082. 'kindRTF' : 'Rich Text Format',
  13083. 'kindC' : 'C source',
  13084. 'kindCHeader' : 'C header source',
  13085. 'kindCPP' : 'C++ source',
  13086. 'kindCPPHeader' : 'C++ header source',
  13087. 'kindShell' : 'Unix shell script',
  13088. 'kindPython' : 'Python source',
  13089. 'kindJava' : 'Java source',
  13090. 'kindRuby' : 'Ruby source',
  13091. 'kindPerl' : 'Perl script',
  13092. 'kindSQL' : 'SQL source',
  13093. 'kindXML' : 'XML document',
  13094. 'kindAWK' : 'AWK source',
  13095. 'kindCSV' : 'Comma separated values',
  13096. 'kindDOCBOOK' : 'Docbook XML document',
  13097. 'kindMarkdown' : 'Markdown text', // added 20.7.2015
  13098. // images
  13099. 'kindImage' : 'Image',
  13100. 'kindBMP' : 'BMP image',
  13101. 'kindJPEG' : 'JPEG image',
  13102. 'kindGIF' : 'GIF Image',
  13103. 'kindPNG' : 'PNG Image',
  13104. 'kindTIFF' : 'TIFF image',
  13105. 'kindTGA' : 'TGA image',
  13106. 'kindPSD' : 'Adobe Photoshop image',
  13107. 'kindXBITMAP' : 'X bitmap image',
  13108. 'kindPXM' : 'Pixelmator image',
  13109. // media
  13110. 'kindAudio' : 'Audio media',
  13111. 'kindAudioMPEG' : 'MPEG audio',
  13112. 'kindAudioMPEG4' : 'MPEG-4 audio',
  13113. 'kindAudioMIDI' : 'MIDI audio',
  13114. 'kindAudioOGG' : 'Ogg Vorbis audio',
  13115. 'kindAudioWAV' : 'WAV audio',
  13116. 'AudioPlaylist' : 'MP3 playlist',
  13117. 'kindVideo' : 'Video media',
  13118. 'kindVideoDV' : 'DV movie',
  13119. 'kindVideoMPEG' : 'MPEG movie',
  13120. 'kindVideoMPEG4' : 'MPEG-4 movie',
  13121. 'kindVideoAVI' : 'AVI movie',
  13122. 'kindVideoMOV' : 'Quick Time movie',
  13123. 'kindVideoWM' : 'Windows Media movie',
  13124. 'kindVideoFlash' : 'Flash movie',
  13125. 'kindVideoMKV' : 'Matroska movie',
  13126. 'kindVideoOGG' : 'Ogg movie'
  13127. }
  13128. };
  13129. }
  13130. /*
  13131. * File: /js/ui/button.js
  13132. */
  13133. /**
  13134. * @class elFinder toolbar button widget.
  13135. * If command has variants - create menu
  13136. *
  13137. * @author Dmitry (dio) Levashov
  13138. **/
  13139. $.fn.elfinderbutton = function(cmd) {
  13140. return this.each(function() {
  13141. var c = 'class',
  13142. fm = cmd.fm,
  13143. disabled = fm.res(c, 'disabled'),
  13144. active = fm.res(c, 'active'),
  13145. hover = fm.res(c, 'hover'),
  13146. item = 'elfinder-button-menu-item',
  13147. selected = 'elfinder-button-menu-item-selected',
  13148. menu,
  13149. text = $('<span class="elfinder-button-text">'+cmd.title+'</span>'),
  13150. prvCname = cmd.className? cmd.className : cmd.name,
  13151. button = $(this).addClass('ui-state-default elfinder-button')
  13152. .attr('title', cmd.title)
  13153. .append('<span class="elfinder-button-icon elfinder-button-icon-' + prvCname + '"></span>', text)
  13154. .on('mouseenter mouseleave', function(e) { !button.hasClass(disabled) && button[e.type == 'mouseleave' ? 'removeClass' : 'addClass'](hover);})
  13155. .on('click', function(e) {
  13156. if (!button.hasClass(disabled)) {
  13157. if (menu && cmd.variants.length >= 1) {
  13158. // close other menus
  13159. menu.is(':hidden') && fm.getUI().click();
  13160. e.stopPropagation();
  13161. menu.css(getMenuOffset()).slideToggle({
  13162. duration: 100,
  13163. done: function(e) {
  13164. fm[menu.is(':visible')? 'toFront' : 'toHide'](menu);
  13165. }
  13166. });
  13167. } else {
  13168. fm.exec(cmd.name, getSelected(), {_userAction: true, _currentType: 'toolbar', _currentNode: button });
  13169. }
  13170. }
  13171. }),
  13172. hideMenu = function() {
  13173. fm.toHide(menu);
  13174. },
  13175. getMenuOffset = function() {
  13176. var fmNode = fm.getUI(),
  13177. baseOffset = fmNode.offset(),
  13178. buttonOffset = button.offset();
  13179. return {
  13180. top : buttonOffset.top - baseOffset.top,
  13181. left : buttonOffset.left - baseOffset.left,
  13182. maxHeight : fmNode.height() - 40
  13183. };
  13184. },
  13185. getSelected = function() {
  13186. var sel = fm.selected(),
  13187. cwd;
  13188. if (!sel.length) {
  13189. if (cwd = fm.cwd()) {
  13190. sel = [ fm.cwd().hash ];
  13191. } else {
  13192. sel = void(0);
  13193. }
  13194. }
  13195. return sel;
  13196. },
  13197. tm;
  13198. text.hide();
  13199. // set self button object to cmd object
  13200. cmd.button = button;
  13201. // if command has variants create menu
  13202. if (Array.isArray(cmd.variants)) {
  13203. button.addClass('elfinder-menubutton');
  13204. menu = $('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu elfinder-button-' + prvCname + '-menu ui-corner-all"></div>')
  13205. .hide()
  13206. .appendTo(fm.getUI())
  13207. .on('mouseenter mouseleave', '.'+item, function() { $(this).toggleClass(hover); })
  13208. .on('click', '.'+item, function(e) {
  13209. var opts = $(this).data('value');
  13210. e.preventDefault();
  13211. e.stopPropagation();
  13212. button.removeClass(hover);
  13213. fm.toHide(menu);
  13214. if (typeof opts === 'undefined') {
  13215. opts = {};
  13216. }
  13217. if (typeof opts === 'object') {
  13218. opts._userAction = true;
  13219. }
  13220. fm.exec(cmd.name, getSelected(), opts);
  13221. })
  13222. .on('close', hideMenu);
  13223. fm.bind('disable select', hideMenu).getUI().on('click', hideMenu);
  13224. cmd.change(function() {
  13225. menu.html('');
  13226. $.each(cmd.variants, function(i, variant) {
  13227. menu.append($('<div class="'+item+'">'+variant[1]+'</div>').data('value', variant[0]).addClass(variant[0] == cmd.value ? selected : ''));
  13228. });
  13229. });
  13230. }
  13231. cmd.change(function() {
  13232. var cName;
  13233. tm && cancelAnimationFrame(tm);
  13234. tm = requestAnimationFrame(function() {
  13235. if (cmd.disabled()) {
  13236. button.removeClass(active+' '+hover).addClass(disabled);
  13237. } else {
  13238. button.removeClass(disabled);
  13239. button[cmd.active() ? 'addClass' : 'removeClass'](active);
  13240. }
  13241. if (cmd.syncTitleOnChange) {
  13242. cName = cmd.className? cmd.className : cmd.name;
  13243. if (prvCname !== cName) {
  13244. button.children('.elfinder-button-icon').removeClass('elfinder-button-icon-' + prvCname).addClass('elfinder-button-icon-' + cName);
  13245. if (menu) {
  13246. menu.removeClass('elfinder-button-' + prvCname + '-menu').addClass('elfinder-button-' + cName + '-menu');
  13247. }
  13248. prvCname = cName;
  13249. }
  13250. text.html(cmd.title);
  13251. button.attr('title', cmd.title);
  13252. }
  13253. });
  13254. })
  13255. .change();
  13256. });
  13257. };
  13258. /*
  13259. * File: /js/ui/contextmenu.js
  13260. */
  13261. /**
  13262. * @class elFinder contextmenu
  13263. *
  13264. * @author Dmitry (dio) Levashov
  13265. **/
  13266. $.fn.elfindercontextmenu = function(fm) {
  13267. return this.each(function() {
  13268. var self = $(this),
  13269. cmItem = 'elfinder-contextmenu-item',
  13270. smItem = 'elfinder-contextsubmenu-item',
  13271. exIcon = 'elfinder-contextmenu-extra-icon',
  13272. cHover = fm.res('class', 'hover'),
  13273. dragOpt = {
  13274. distance: 8,
  13275. start: function() {
  13276. menu.data('drag', true).data('touching') && menu.find('.'+cHover).removeClass(cHover);
  13277. },
  13278. stop: function() {
  13279. menu.data('draged', true).removeData('drag');
  13280. }
  13281. },
  13282. menu = $(this).addClass('touch-punch ui-helper-reset ui-front ui-widget ui-state-default ui-corner-all elfinder-contextmenu elfinder-contextmenu-'+fm.direction)
  13283. .hide()
  13284. .on('touchstart', function(e) {
  13285. menu.data('touching', true).children().removeClass(cHover);
  13286. })
  13287. .on('touchend', function(e) {
  13288. menu.removeData('touching');
  13289. })
  13290. .on('mouseenter mouseleave', '.'+cmItem, function(e) {
  13291. $(this).toggleClass(cHover, (e.type === 'mouseenter' || (! menu.data('draged') && menu.data('submenuKeep'))? true : false));
  13292. if (menu.data('draged') && menu.data('submenuKeep')) {
  13293. menu.find('.elfinder-contextmenu-sub:visible').parent().addClass(cHover);
  13294. }
  13295. })
  13296. .on('mouseenter mouseleave', '.'+exIcon, function(e) {
  13297. $(this).parent().toggleClass(cHover, e.type === 'mouseleave');
  13298. })
  13299. .on('mouseenter mouseleave', '.'+cmItem+',.'+smItem, function(e) {
  13300. var setIndex = function(target, sub) {
  13301. $.each(sub? subnodes : nodes, function(i, n) {
  13302. if (target[0] === n) {
  13303. (sub? subnodes : nodes)._cur = i;
  13304. if (sub) {
  13305. subselected = target;
  13306. } else {
  13307. selected = target;
  13308. }
  13309. return false;
  13310. }
  13311. });
  13312. };
  13313. if (e.originalEvent) {
  13314. var target = $(this),
  13315. unHover = function() {
  13316. if (selected && !selected.children('div.elfinder-contextmenu-sub:visible').length) {
  13317. selected.removeClass(cHover);
  13318. }
  13319. };
  13320. if (e.type === 'mouseenter') {
  13321. // mouseenter
  13322. if (target.hasClass(smItem)) {
  13323. // submenu
  13324. if (subselected) {
  13325. subselected.removeClass(cHover);
  13326. }
  13327. if (selected) {
  13328. subnodes = selected.find('div.'+smItem);
  13329. }
  13330. setIndex(target, true);
  13331. } else {
  13332. // menu
  13333. unHover();
  13334. setIndex(target);
  13335. }
  13336. } else {
  13337. // mouseleave
  13338. if (target.hasClass(smItem)) {
  13339. //submenu
  13340. subselected = null;
  13341. subnodes = null;
  13342. } else {
  13343. // menu
  13344. unHover();
  13345. (function(sel) {
  13346. setTimeout(function() {
  13347. if (sel === selected) {
  13348. selected = null;
  13349. }
  13350. }, 250);
  13351. })(selected);
  13352. }
  13353. }
  13354. }
  13355. })
  13356. .on('contextmenu', function(){return false;})
  13357. .on('mouseup', function() {
  13358. setTimeout(function() {
  13359. menu.removeData('draged');
  13360. }, 100);
  13361. })
  13362. .draggable(dragOpt),
  13363. ltr = fm.direction === 'ltr',
  13364. subpos = ltr? 'left' : 'right',
  13365. types = Object.assign({}, fm.options.contextmenu),
  13366. tpl = '<div class="'+cmItem+'{className}"><span class="elfinder-button-icon {icon} elfinder-contextmenu-icon"{style}></span><span>{label}</span></div>',
  13367. item = function(label, icon, callback, opts) {
  13368. var className = '',
  13369. style = '',
  13370. iconClass = '',
  13371. v, pos;
  13372. if (opts) {
  13373. if (opts.className) {
  13374. className = ' ' + opts.className;
  13375. }
  13376. if (opts.iconClass) {
  13377. iconClass = opts.iconClass;
  13378. icon = '';
  13379. }
  13380. if (opts.iconImg) {
  13381. v = opts.iconImg.split(/ +/);
  13382. pos = v[1] && v[2]? fm.escape(v[1] + 'px ' + v[2] + 'px') : '';
  13383. style = ' style="background:url(\''+fm.escape(v[0])+'\') '+(pos? pos : '0 0')+' no-repeat;'+(pos? '' : 'posbackground-size:contain;')+'"';
  13384. }
  13385. }
  13386. return $(tpl.replace('{icon}', icon ? 'elfinder-button-icon-'+icon : (iconClass? iconClass : ''))
  13387. .replace('{label}', label)
  13388. .replace('{style}', style)
  13389. .replace('{className}', className))
  13390. .on('click', function(e) {
  13391. e.stopPropagation();
  13392. e.preventDefault();
  13393. callback();
  13394. });
  13395. },
  13396. urlIcon = function(iconUrl) {
  13397. var v = iconUrl.split(/ +/),
  13398. pos = v[1] && v[2]? (v[1] + 'px ' + v[2] + 'px') : '';
  13399. return {
  13400. backgroundImage: 'url("'+v[0]+'")',
  13401. backgroundRepeat: 'no-repeat',
  13402. backgroundPosition: pos? pos : '',
  13403. backgroundSize: pos? '' : 'contain'
  13404. };
  13405. },
  13406. base, cwd,
  13407. nodes, selected, subnodes, subselected, autoSyncStop, subHoverTm,
  13408. autoToggle = function() {
  13409. var evTouchStart = 'touchstart.contextmenuAutoToggle';
  13410. menu.data('hideTm') && clearTimeout(menu.data('hideTm'));
  13411. if (menu.is(':visible')) {
  13412. menu.on('touchstart', function(e) {
  13413. if (e.originalEvent.touches.length > 1) {
  13414. return;
  13415. }
  13416. menu.stop();
  13417. fm.toFront(menu);
  13418. menu.data('hideTm') && clearTimeout(menu.data('hideTm'));
  13419. })
  13420. .data('hideTm', setTimeout(function() {
  13421. if (menu.is(':visible')) {
  13422. cwd.find('.elfinder-cwd-file').off(evTouchStart);
  13423. cwd.find('.elfinder-cwd-file.ui-selected')
  13424. .one(evTouchStart, function(e) {
  13425. if (e.originalEvent.touches.length > 1) {
  13426. return;
  13427. }
  13428. var tgt = $(e.target);
  13429. if (menu.first().length && !tgt.is('input:checkbox') && !tgt.hasClass('elfinder-cwd-select')) {
  13430. e.stopPropagation();
  13431. //e.preventDefault();
  13432. open(e.originalEvent.touches[0].pageX, e.originalEvent.touches[0].pageY);
  13433. cwd.data('longtap', true)
  13434. tgt.one('touchend', function() {
  13435. setTimeout(function() {
  13436. cwd.removeData('longtap');
  13437. }, 80);
  13438. });
  13439. return;
  13440. }
  13441. cwd.find('.elfinder-cwd-file').off(evTouchStart);
  13442. })
  13443. .one('unselect.'+fm.namespace, function() {
  13444. cwd.find('.elfinder-cwd-file').off(evTouchStart);
  13445. });
  13446. menu.fadeOut({
  13447. duration: 300,
  13448. fail: function() {
  13449. menu.css('opacity', '1').show();
  13450. },
  13451. done: function() {
  13452. fm.toHide(menu);
  13453. }
  13454. });
  13455. }
  13456. }, 4500));
  13457. }
  13458. },
  13459. keyEvts = function(e) {
  13460. var code = e.keyCode,
  13461. ESC = $.ui.keyCode.ESCAPE,
  13462. ENT = $.ui.keyCode.ENTER,
  13463. LEFT = $.ui.keyCode.LEFT,
  13464. RIGHT = $.ui.keyCode.RIGHT,
  13465. UP = $.ui.keyCode.UP,
  13466. DOWN = $.ui.keyCode.DOWN,
  13467. subent = fm.direction === 'ltr'? RIGHT : LEFT,
  13468. sublev = subent === RIGHT? LEFT : RIGHT;
  13469. if ($.inArray(code, [ESC, ENT, LEFT, RIGHT, UP, DOWN]) !== -1) {
  13470. e.preventDefault();
  13471. e.stopPropagation();
  13472. e.stopImmediatePropagation();
  13473. if (code == ESC || code === sublev) {
  13474. if (selected && subnodes && subselected) {
  13475. subselected.trigger('mouseleave').trigger('submenuclose');
  13476. selected.addClass(cHover);
  13477. subnodes = null;
  13478. subselected = null;
  13479. } else {
  13480. code == ESC && close();
  13481. }
  13482. } else if (code == UP || code == DOWN) {
  13483. if (subnodes) {
  13484. if (subselected) {
  13485. subselected.trigger('mouseleave');
  13486. }
  13487. if (code == DOWN && (! subselected || subnodes.length <= ++subnodes._cur)) {
  13488. subnodes._cur = 0;
  13489. } else if (code == UP && (! subselected || --subnodes._cur < 0)) {
  13490. subnodes._cur = subnodes.length - 1;
  13491. }
  13492. subselected = subnodes.eq(subnodes._cur).trigger('mouseenter');
  13493. } else {
  13494. subnodes = null;
  13495. if (selected) {
  13496. selected.trigger('mouseleave');
  13497. }
  13498. if (code == DOWN && (! selected || nodes.length <= ++nodes._cur)) {
  13499. nodes._cur = 0;
  13500. } else if (code == UP && (! selected || --nodes._cur < 0)) {
  13501. nodes._cur = nodes.length - 1;
  13502. }
  13503. selected = nodes.eq(nodes._cur).addClass(cHover);
  13504. }
  13505. } else if (selected && (code == ENT || code === subent)) {
  13506. if (selected.hasClass('elfinder-contextmenu-group')) {
  13507. if (subselected) {
  13508. code == ENT && subselected.click();
  13509. } else {
  13510. selected.trigger('mouseenter');
  13511. subnodes = selected.find('div.'+smItem);
  13512. subnodes._cur = 0;
  13513. subselected = subnodes.first().addClass(cHover);
  13514. }
  13515. } else {
  13516. code == ENT && selected.click();
  13517. }
  13518. }
  13519. }
  13520. },
  13521. open = function(x, y, css) {
  13522. var width = menu.outerWidth(),
  13523. height = menu.outerHeight(),
  13524. bstyle = base.attr('style'),
  13525. bpos = base.offset(),
  13526. bwidth = base.width(),
  13527. bheight = base.height(),
  13528. mw = fm.UA.Mobile? 40 : 2,
  13529. mh = fm.UA.Mobile? 20 : 2,
  13530. x = x - (bpos? bpos.left : 0),
  13531. y = y - (bpos? bpos.top : 0),
  13532. css = Object.assign(css || {}, {
  13533. top : Math.max(0, y + mh + height < bheight ? y + mh : y - (y + height - bheight)),
  13534. left : Math.max(0, (x < width + mw || x + mw + width < bwidth)? x + mw : x - mw - width),
  13535. opacity : '1'
  13536. }),
  13537. evts;
  13538. autoSyncStop = true;
  13539. fm.autoSync('stop');
  13540. base.width(bwidth);
  13541. menu.stop().removeAttr('style').css(css);
  13542. fm.toFront(menu);
  13543. menu.show();
  13544. base.attr('style', bstyle);
  13545. css[subpos] = parseInt(menu.width());
  13546. menu.find('.elfinder-contextmenu-sub').css(css);
  13547. if (fm.UA.iOS) {
  13548. $('div.elfinder div.overflow-scrolling-touch').css('-webkit-overflow-scrolling', 'auto');
  13549. }
  13550. selected = null;
  13551. subnodes = null;
  13552. subselected = null;
  13553. $(document).on('keydown.' + fm.namespace, keyEvts);
  13554. evts = $._data(document).events;
  13555. if (evts && evts.keydown) {
  13556. evts.keydown.unshift(evts.keydown.pop());
  13557. }
  13558. fm.UA.Mobile && autoToggle();
  13559. requestAnimationFrame(function() {
  13560. fm.getUI().one('click.' + fm.namespace, close);
  13561. });
  13562. },
  13563. close = function() {
  13564. fm.getUI().off('click.' + fm.namespace, close);
  13565. $(document).off('keydown.' + fm.namespace, keyEvts);
  13566. currentType = currentTargets = null;
  13567. if (menu.is(':visible') || menu.children().length) {
  13568. fm.toHide(menu.removeAttr('style').empty().removeData('submenuKeep'));
  13569. try {
  13570. if (! menu.draggable('instance')) {
  13571. menu.draggable(dragOpt);
  13572. }
  13573. } catch(e) {
  13574. if (! menu.hasClass('ui-draggable')) {
  13575. menu.draggable(dragOpt);
  13576. }
  13577. }
  13578. if (menu.data('prevNode')) {
  13579. menu.data('prevNode').after(menu);
  13580. menu.removeData('prevNode');
  13581. }
  13582. fm.trigger('closecontextmenu');
  13583. if (fm.UA.iOS) {
  13584. $('div.elfinder div.overflow-scrolling-touch').css('-webkit-overflow-scrolling', 'touch');
  13585. }
  13586. }
  13587. autoSyncStop && fm.searchStatus.state < 1 && ! fm.searchStatus.ininc && fm.autoSync();
  13588. autoSyncStop = false;
  13589. },
  13590. create = function(type, targets) {
  13591. var sep = false,
  13592. insSep = false,
  13593. disabled = [],
  13594. isCwd = type === 'cwd',
  13595. selcnt = 0,
  13596. cmdMap;
  13597. currentType = type;
  13598. currentTargets = targets;
  13599. // get current uiCmdMap option
  13600. if (!(cmdMap = fm.option('uiCmdMap', isCwd? void(0) : targets[0]))) {
  13601. cmdMap = {};
  13602. }
  13603. if (!isCwd) {
  13604. disabled = fm.getDisabledCmds(targets);
  13605. }
  13606. selcnt = fm.selected().length;
  13607. if (selcnt > 1) {
  13608. menu.append('<div class="ui-corner-top ui-widget-header elfinder-contextmenu-header"><span>'
  13609. + fm.i18n('selectedItems', ''+selcnt)
  13610. + '</span></div>');
  13611. }
  13612. nodes = $();
  13613. $.each(types[type]||[], function(i, name) {
  13614. var cmd, cmdName, useMap, node, submenu, hover;
  13615. if (name === '|') {
  13616. if (sep) {
  13617. insSep = true;
  13618. }
  13619. return;
  13620. }
  13621. if (cmdMap[name]) {
  13622. cmdName = cmdMap[name];
  13623. useMap = true;
  13624. } else {
  13625. cmdName = name;
  13626. }
  13627. cmd = fm.getCommand(cmdName);
  13628. if (cmd && !isCwd && (!fm.searchStatus.state || !cmd.disableOnSearch)) {
  13629. cmd.__disabled = cmd._disabled;
  13630. cmd._disabled = !(cmd.alwaysEnabled || (fm._commands[cmdName] ? $.inArray(name, disabled) === -1 && (!useMap || !disabled[cmdName]) : false));
  13631. $.each(cmd.linkedCmds, function(i, n) {
  13632. var c;
  13633. if (c = fm.getCommand(n)) {
  13634. c.__disabled = c._disabled;
  13635. c._disabled = !(c.alwaysEnabled || (fm._commands[n] ? !disabled[n] : false));
  13636. }
  13637. });
  13638. }
  13639. if (cmd && !cmd._disabled && cmd.getstate(targets) != -1) {
  13640. if (cmd.variants) {
  13641. if (!cmd.variants.length) {
  13642. return;
  13643. }
  13644. node = item(cmd.title, cmd.className? cmd.className : cmd.name, function(){}, cmd.contextmenuOpts);
  13645. submenu = $('<div class="ui-front ui-corner-all elfinder-contextmenu-sub"></div>')
  13646. .hide()
  13647. .css('max-height', fm.getUI().height() - 30)
  13648. .appendTo(node.append('<span class="elfinder-contextmenu-arrow"></span>'));
  13649. hover = function(show){
  13650. if (! show) {
  13651. submenu.hide();
  13652. } else {
  13653. var bstyle = base.attr('style');
  13654. base.width(base.width());
  13655. // top: '-1000px' to prevent visible scrollbar of window with the elFinder option `height: '100%'`
  13656. submenu.css({ top: '-1000px', left: 'auto', right: 'auto' });
  13657. var nodeOffset = node.offset(),
  13658. nodeleft = nodeOffset.left,
  13659. nodetop = nodeOffset.top,
  13660. nodewidth = node.outerWidth(),
  13661. width = submenu.outerWidth(true),
  13662. height = submenu.outerHeight(true),
  13663. baseOffset = base.offset(),
  13664. wwidth = baseOffset.left + base.width(),
  13665. wheight = baseOffset.top + base.height(),
  13666. cltr = ltr,
  13667. x = nodewidth,
  13668. y, over;
  13669. if (ltr) {
  13670. over = (nodeleft + nodewidth + width) - wwidth;
  13671. if (over > 10) {
  13672. if (nodeleft > width - 5) {
  13673. x = x - 5;
  13674. cltr = false;
  13675. } else {
  13676. if (!fm.UA.Mobile) {
  13677. x = nodewidth - over;
  13678. }
  13679. }
  13680. }
  13681. } else {
  13682. over = width - nodeleft;
  13683. if (over > 0) {
  13684. if ((nodeleft + nodewidth + width - 15) < wwidth) {
  13685. x = x - 5;
  13686. cltr = true;
  13687. } else {
  13688. if (!fm.UA.Mobile) {
  13689. x = nodewidth - over;
  13690. }
  13691. }
  13692. }
  13693. }
  13694. over = (nodetop + 5 + height) - wheight;
  13695. y = (over > 0 && nodetop < wheight)? 5 - over : (over > 0? 30 - height : 5);
  13696. menu.find('.elfinder-contextmenu-sub:visible').hide();
  13697. submenu.css({
  13698. top : y,
  13699. left : cltr? x : 'auto',
  13700. right: cltr? 'auto' : x,
  13701. overflowY: 'auto'
  13702. }).show();
  13703. base.attr('style', bstyle);
  13704. }
  13705. };
  13706. node.addClass('elfinder-contextmenu-group')
  13707. .on('mouseleave', '.elfinder-contextmenu-sub', function(e) {
  13708. if (! menu.data('draged')) {
  13709. menu.removeData('submenuKeep');
  13710. }
  13711. })
  13712. .on('submenuclose', '.elfinder-contextmenu-sub', function(e) {
  13713. hover(false);
  13714. })
  13715. .on('click', '.'+smItem, function(e){
  13716. var opts, $this;
  13717. e.stopPropagation();
  13718. if (! menu.data('draged')) {
  13719. $this = $(this);
  13720. if (!cmd.keepContextmenu) {
  13721. menu.hide();
  13722. } else {
  13723. $this.removeClass(cHover);
  13724. node.addClass(cHover);
  13725. }
  13726. opts = $this.data('exec');
  13727. if (typeof opts === 'undefined') {
  13728. opts = {};
  13729. }
  13730. if (typeof opts === 'object') {
  13731. opts._userAction = true;
  13732. opts._currentType = type;
  13733. opts._currentNode = $this;
  13734. }
  13735. !cmd.keepContextmenu && close();
  13736. fm.exec(cmd.name, targets, opts);
  13737. }
  13738. })
  13739. .on('touchend', function(e) {
  13740. if (! menu.data('drag')) {
  13741. hover(true);
  13742. menu.data('submenuKeep', true);
  13743. }
  13744. })
  13745. .on('mouseenter mouseleave', function(e){
  13746. if (! menu.data('touching')) {
  13747. if (node.data('timer')) {
  13748. clearTimeout(node.data('timer'));
  13749. node.removeData('timer');
  13750. }
  13751. if (!$(e.target).closest('.elfinder-contextmenu-sub', menu).length) {
  13752. if (e.type === 'mouseleave') {
  13753. if (! menu.data('submenuKeep')) {
  13754. node.data('timer', setTimeout(function() {
  13755. node.removeData('timer');
  13756. hover(false);
  13757. }, 250));
  13758. }
  13759. } else {
  13760. node.data('timer', setTimeout(function() {
  13761. node.removeData('timer');
  13762. hover(true);
  13763. }, nodes.find('div.elfinder-contextmenu-sub:visible').length? 250 : 0));
  13764. }
  13765. }
  13766. }
  13767. });
  13768. $.each(cmd.variants, function(i, variant) {
  13769. var item = variant === '|' ? '<div class="elfinder-contextmenu-separator"></div>' :
  13770. $('<div class="'+cmItem+' '+smItem+'"><span>'+variant[1]+'</span></div>').data('exec', variant[0]),
  13771. iconClass, icon;
  13772. if (typeof variant[2] !== 'undefined') {
  13773. icon = $('<span></span>').addClass('elfinder-button-icon elfinder-contextmenu-icon');
  13774. if (! /\//.test(variant[2])) {
  13775. icon.addClass('elfinder-button-icon-'+variant[2]);
  13776. } else {
  13777. icon.css(urlIcon(variant[2]));
  13778. }
  13779. item.prepend(icon).addClass(smItem+'-icon');
  13780. }
  13781. submenu.append(item);
  13782. });
  13783. } else {
  13784. node = item(cmd.title, cmd.className? cmd.className : cmd.name, function() {
  13785. if (! menu.data('draged')) {
  13786. !cmd.keepContextmenu && close();
  13787. fm.exec(cmd.name, targets, {_userAction: true, _currentType: type, _currentNode: node});
  13788. }
  13789. }, cmd.contextmenuOpts);
  13790. if (cmd.extra && cmd.extra.node) {
  13791. $('<span class="elfinder-button-icon elfinder-button-icon-'+(cmd.extra.icon || '')+' '+exIcon+'"></span>')
  13792. .append(cmd.extra.node).appendTo(node);
  13793. $(cmd.extra.node).trigger('ready', {targets: targets});
  13794. } else {
  13795. node.remove('.'+exIcon);
  13796. }
  13797. }
  13798. if (cmd.extendsCmd) {
  13799. node.children('span.elfinder-button-icon').addClass('elfinder-button-icon-' + cmd.extendsCmd);
  13800. }
  13801. if (insSep) {
  13802. menu.append('<div class="elfinder-contextmenu-separator"></div>');
  13803. }
  13804. menu.append(node);
  13805. sep = true;
  13806. insSep = false;
  13807. }
  13808. if (cmd && typeof cmd.__disabled !== 'undefined') {
  13809. cmd._disabled = cmd.__disabled;
  13810. delete cmd.__disabled;
  13811. $.each(cmd.linkedCmds, function(i, n) {
  13812. var c;
  13813. if (c = fm.getCommand(n)) {
  13814. c._disabled = c.__disabled;
  13815. delete c.__disabled;
  13816. }
  13817. });
  13818. }
  13819. });
  13820. nodes = menu.children('div.'+cmItem);
  13821. },
  13822. createFromRaw = function(raw) {
  13823. currentType = 'raw';
  13824. $.each(raw, function(i, data) {
  13825. var node;
  13826. if (data === '|') {
  13827. menu.append('<div class="elfinder-contextmenu-separator"></div>');
  13828. } else if (data.label && typeof data.callback == 'function') {
  13829. node = item(data.label, data.icon, function() {
  13830. if (! menu.data('draged')) {
  13831. !data.remain && close();
  13832. data.callback();
  13833. }
  13834. }, data.options || null);
  13835. menu.append(node);
  13836. }
  13837. });
  13838. nodes = menu.children('div.'+cmItem);
  13839. },
  13840. currentType = null,
  13841. currentTargets = null;
  13842. fm.one('load', function() {
  13843. base = fm.getUI();
  13844. cwd = fm.getUI('cwd');
  13845. fm.bind('contextmenu', function(e) {
  13846. var data = e.data,
  13847. css = {},
  13848. prevNode;
  13849. if (data.type && data.type !== 'files') {
  13850. cwd.trigger('unselectall');
  13851. }
  13852. close();
  13853. if (data.type && data.targets) {
  13854. fm.trigger('contextmenucreate', data);
  13855. create(data.type, data.targets);
  13856. fm.trigger('contextmenucreatedone', data);
  13857. } else if (data.raw) {
  13858. createFromRaw(data.raw);
  13859. }
  13860. if (menu.children().length) {
  13861. prevNode = data.prevNode || null;
  13862. if (prevNode) {
  13863. menu.data('prevNode', menu.prev());
  13864. prevNode.after(menu);
  13865. }
  13866. if (data.fitHeight) {
  13867. css = {maxHeight: Math.min(fm.getUI().height(), $(window).height()), overflowY: 'auto'};
  13868. menu.draggable('destroy').removeClass('ui-draggable');
  13869. }
  13870. open(data.x, data.y, css);
  13871. // call opened callback function
  13872. if (data.opened && typeof data.opened === 'function') {
  13873. data.opened.call(menu);
  13874. }
  13875. }
  13876. })
  13877. .one('destroy', function() { menu.remove(); })
  13878. .bind('disable', close)
  13879. .bind('select', function(e){
  13880. (currentType === 'files' && (!e.data || e.data.selected.toString() !== currentTargets.toString())) && close();
  13881. });
  13882. })
  13883. .shortcut({
  13884. pattern : fm.OS === 'mac' ? 'ctrl+m' : 'contextmenu shift+f10',
  13885. description : 'contextmenu',
  13886. callback : function(e) {
  13887. e.stopPropagation();
  13888. e.preventDefault();
  13889. $(document).one('contextmenu.' + fm.namespace, function(e) {
  13890. e.preventDefault();
  13891. e.stopPropagation();
  13892. });
  13893. var sel = fm.selected(),
  13894. type, targets, pos, elm;
  13895. if (sel.length) {
  13896. type = 'files';
  13897. targets = sel;
  13898. elm = fm.cwdHash2Elm(sel[0]);
  13899. } else {
  13900. type = 'cwd';
  13901. targets = [ fm.cwd().hash ];
  13902. pos = fm.getUI('workzone').offset();
  13903. }
  13904. if (! elm || ! elm.length) {
  13905. elm = fm.getUI('workzone');
  13906. }
  13907. pos = elm.offset();
  13908. pos.top += (elm.height() / 2);
  13909. pos.left += (elm.width() / 2);
  13910. fm.trigger('contextmenu', {
  13911. 'type' : type,
  13912. 'targets' : targets,
  13913. 'x' : pos.left,
  13914. 'y' : pos.top
  13915. });
  13916. }
  13917. });
  13918. });
  13919. };
  13920. /*
  13921. * File: /js/ui/cwd.js
  13922. */
  13923. /**
  13924. * elFinder current working directory ui.
  13925. *
  13926. * @author Dmitry (dio) Levashov
  13927. **/
  13928. $.fn.elfindercwd = function(fm, options) {
  13929. this.not('.elfinder-cwd').each(function() {
  13930. // fm.time('cwdLoad');
  13931. var mobile = fm.UA.Mobile,
  13932. list = fm.viewType == 'list',
  13933. undef = 'undefined',
  13934. /**
  13935. * Select event full name
  13936. *
  13937. * @type String
  13938. **/
  13939. evtSelect = 'select.'+fm.namespace,
  13940. /**
  13941. * Unselect event full name
  13942. *
  13943. * @type String
  13944. **/
  13945. evtUnselect = 'unselect.'+fm.namespace,
  13946. /**
  13947. * Disable event full name
  13948. *
  13949. * @type String
  13950. **/
  13951. evtDisable = 'disable.'+fm.namespace,
  13952. /**
  13953. * Disable event full name
  13954. *
  13955. * @type String
  13956. **/
  13957. evtEnable = 'enable.'+fm.namespace,
  13958. c = 'class',
  13959. /**
  13960. * File css class
  13961. *
  13962. * @type String
  13963. **/
  13964. clFile = fm.res(c, 'cwdfile'),
  13965. /**
  13966. * Selected css class
  13967. *
  13968. * @type String
  13969. **/
  13970. fileSelector = '.'+clFile,
  13971. /**
  13972. * Selected css class
  13973. *
  13974. * @type String
  13975. **/
  13976. clSelected = 'ui-selected',
  13977. /**
  13978. * Disabled css class
  13979. *
  13980. * @type String
  13981. **/
  13982. clDisabled = fm.res(c, 'disabled'),
  13983. /**
  13984. * Draggable css class
  13985. *
  13986. * @type String
  13987. **/
  13988. clDraggable = fm.res(c, 'draggable'),
  13989. /**
  13990. * Droppable css class
  13991. *
  13992. * @type String
  13993. **/
  13994. clDroppable = fm.res(c, 'droppable'),
  13995. /**
  13996. * Hover css class
  13997. *
  13998. * @type String
  13999. **/
  14000. clHover = fm.res(c, 'hover'),
  14001. /**
  14002. * Active css class
  14003. *
  14004. * @type String
  14005. **/
  14006. clActive = fm.res(c, 'active'),
  14007. /**
  14008. * Hover css class
  14009. *
  14010. * @type String
  14011. **/
  14012. clDropActive = fm.res(c, 'adroppable'),
  14013. /**
  14014. * Css class for temporary nodes (for mkdir/mkfile) commands
  14015. *
  14016. * @type String
  14017. **/
  14018. clTmp = clFile+'-tmp',
  14019. /**
  14020. * Select checkbox css class
  14021. *
  14022. * @type String
  14023. */
  14024. clSelChk = 'elfinder-cwd-selectchk',
  14025. /**
  14026. * Number of thumbnails to load in one request (new api only)
  14027. *
  14028. * @type Number
  14029. **/
  14030. tmbNum = fm.options.loadTmbs > 0 ? fm.options.loadTmbs : 5,
  14031. /**
  14032. * Current search query.
  14033. *
  14034. * @type String
  14035. */
  14036. query = '',
  14037. /**
  14038. * Currect clipboard(cut) hashes as object key
  14039. *
  14040. * @type Object
  14041. */
  14042. clipCuts = {},
  14043. /**
  14044. * Parents hashes of cwd
  14045. *
  14046. * @type Array
  14047. */
  14048. cwdParents = [],
  14049. /**
  14050. * cwd current hashes
  14051. *
  14052. * @type Array
  14053. */
  14054. cwdHashes = [],
  14055. /**
  14056. * incsearch current hashes
  14057. *
  14058. * @type Array
  14059. */
  14060. incHashes = void 0,
  14061. /**
  14062. * Custom columns name and order
  14063. *
  14064. * @type Array
  14065. */
  14066. customCols = [],
  14067. /**
  14068. * Current clicked element id of first time for dblclick
  14069. *
  14070. * @type String
  14071. */
  14072. curClickId = '',
  14073. /**
  14074. * Custom columns builder
  14075. *
  14076. * @type Function
  14077. */
  14078. customColsBuild = function() {
  14079. var cols = '';
  14080. for (var i = 0; i < customCols.length; i++) {
  14081. cols += '<td class="elfinder-col-'+customCols[i]+'">{' + customCols[i] + '}</td>';
  14082. }
  14083. return cols;
  14084. },
  14085. /**
  14086. * Make template.row from customCols
  14087. *
  14088. * @type Function
  14089. */
  14090. makeTemplateRow = function() {
  14091. return '<tr id="{id}" class="'+clFile+' {permsclass} {dirclass}" title="{tooltip}"{css}><td class="elfinder-col-name"><div class="elfinder-cwd-file-wrapper"><span class="elfinder-cwd-icon {mime}"{style}></span>{marker}<span class="elfinder-cwd-filename">{name}</span></div>'+selectCheckbox+'</td>'+customColsBuild()+'</tr>';
  14092. },
  14093. selectCheckbox = ($.map(options.showSelectCheckboxUA, function(t) {return (fm.UA[t] || t.match(/^all$/i))? true : null;}).length)? '<div class="elfinder-cwd-select"><input type="checkbox" class="'+clSelChk+'"></div>' : '',
  14094. colResizing = false,
  14095. colWidth = null,
  14096. /**
  14097. * Table header height
  14098. */
  14099. thHeight,
  14100. /**
  14101. * File templates
  14102. *
  14103. * @type Object
  14104. **/
  14105. templates = {
  14106. icon : '<div id="{id}" class="'+clFile+' {permsclass} {dirclass} ui-corner-all" title="{tooltip}"><div class="elfinder-cwd-file-wrapper ui-corner-all"><div class="elfinder-cwd-icon {mime} ui-corner-all" unselectable="on"{style}></div>{marker}</div><div class="elfinder-cwd-filename" title="{nametitle}">{name}</div>'+selectCheckbox+'</div>',
  14107. row : ''
  14108. },
  14109. permsTpl = fm.res('tpl', 'perms'),
  14110. lockTpl = fm.res('tpl', 'lock'),
  14111. symlinkTpl = fm.res('tpl', 'symlink'),
  14112. /**
  14113. * Template placeholders replacement rules
  14114. *
  14115. * @type Object
  14116. **/
  14117. replacement = {
  14118. id : function(f) {
  14119. return fm.cwdHash2Id(f.hash);
  14120. },
  14121. name : function(f) {
  14122. var name = fm.escape(f.i18 || f.name);
  14123. !list && (name = name.replace(/([_.])/g, '&#8203;$1'));
  14124. return name;
  14125. },
  14126. nametitle : function(f) {
  14127. return fm.escape(f.i18 || f.name);
  14128. },
  14129. permsclass : function(f) {
  14130. return fm.perms2class(f);
  14131. },
  14132. perm : function(f) {
  14133. return fm.formatPermissions(f);
  14134. },
  14135. dirclass : function(f) {
  14136. var cName = f.mime == 'directory' ? 'directory' : '';
  14137. f.isroot && (cName += ' isroot');
  14138. f.csscls && (cName += ' ' + fm.escape(f.csscls));
  14139. options.getClass && (cName += ' ' + options.getClass(f));
  14140. return cName;
  14141. },
  14142. style : function(f) {
  14143. return f.icon? fm.getIconStyle(f) : '';
  14144. },
  14145. mime : function(f) {
  14146. var cName = fm.mime2class(f.mime);
  14147. f.icon && (cName += ' elfinder-cwd-bgurl');
  14148. return cName;
  14149. },
  14150. size : function(f) {
  14151. return (f.mime === 'directory' && !f.size)? '-' : fm.formatSize(f.size);
  14152. },
  14153. date : function(f) {
  14154. return fm.formatDate(f);
  14155. },
  14156. kind : function(f) {
  14157. return fm.mime2kind(f);
  14158. },
  14159. mode : function(f) {
  14160. return f.perm? fm.formatFileMode(f.perm) : '';
  14161. },
  14162. modestr : function(f) {
  14163. return f.perm? fm.formatFileMode(f.perm, 'string') : '';
  14164. },
  14165. modeoct : function(f) {
  14166. return f.perm? fm.formatFileMode(f.perm, 'octal') : '';
  14167. },
  14168. modeboth : function(f) {
  14169. return f.perm? fm.formatFileMode(f.perm, 'both') : '';
  14170. },
  14171. marker : function(f) {
  14172. return (f.alias || f.mime == 'symlink-broken' ? symlinkTpl : '')+(!f.read || !f.write ? permsTpl : '')+(f.locked ? lockTpl : '');
  14173. },
  14174. tooltip : function(f) {
  14175. var title = fm.formatDate(f) + (f.size > 0 ? ' ('+fm.formatSize(f.size)+')' : ''),
  14176. info = '';
  14177. if (query && f.path) {
  14178. info = fm.escape(f.path.replace(/\/[^\/]*$/, ''));
  14179. } else {
  14180. info = f.tooltip? fm.escape(f.tooltip).replace(/\r/g, '&#13;') : '';
  14181. }
  14182. if (list) {
  14183. info += (info? '&#13;' : '') + fm.escape(f.i18 || f.name);
  14184. }
  14185. return info? info + '&#13;' + title : title;
  14186. }
  14187. },
  14188. /**
  14189. * Type badge CSS added flag
  14190. *
  14191. * @type Object
  14192. */
  14193. addedBadges = {},
  14194. /**
  14195. * Type badge style sheet element
  14196. *
  14197. * @type Object
  14198. */
  14199. addBadgeStyleSheet,
  14200. /**
  14201. * Add type badge CSS into 'head'
  14202. *
  14203. * @type Fundtion
  14204. */
  14205. addBadgeStyle = function(mime, name) {
  14206. var sel, ext, type;
  14207. if (mime && ! addedBadges[mime]) {
  14208. if (typeof addBadgeStyleSheet === 'undefined') {
  14209. if ($('#elfinderAddBadgeStyle'+fm.namespace).length) {
  14210. $('#elfinderAddBadgeStyle'+fm.namespace).remove();
  14211. }
  14212. addBadgeStyleSheet = $('<style id="addBadgeStyle'+fm.namespace+'"></style>').insertBefore($('head').children(':first')).get(0).sheet || null;
  14213. }
  14214. if (addBadgeStyleSheet) {
  14215. mime = mime.toLowerCase();
  14216. type = mime.split('/');
  14217. ext = fm.escape(fm.mimeTypes[mime] || (name.replace(/.bac?k$/i, '').match(/\.([^.]+)$/) || ['',''])[1]);
  14218. if (ext) {
  14219. sel = '.elfinder-cwd-icon-' + type[0].replace(/(\.|\+)/g, '-');
  14220. if (typeof type[1] !== 'undefined') {
  14221. sel += '.elfinder-cwd-icon-' + type[1].replace(/(\.|\+)/g, '-');
  14222. }
  14223. try {
  14224. addBadgeStyleSheet.insertRule(sel + ':before{content:"' + ext.toLowerCase() + '"}', 0);
  14225. } catch(e) {}
  14226. }
  14227. addedBadges[mime] = true;
  14228. }
  14229. }
  14230. },
  14231. /**
  14232. * Return file html
  14233. *
  14234. * @param Object file info
  14235. * @return String
  14236. **/
  14237. itemhtml = function(f) {
  14238. f.mime && f.mime !== 'directory' && !addedBadges[f.mime] && addBadgeStyle(f.mime, f.name);
  14239. return templates[list ? 'row' : 'icon']
  14240. .replace(/\{([a-z0-9_]+)\}/g, function(s, e) {
  14241. return replacement[e] ? replacement[e](f, fm) : (f[e] ? f[e] : '');
  14242. });
  14243. },
  14244. /**
  14245. * jQueery node that will be selected next
  14246. *
  14247. * @type Object jQuery node
  14248. */
  14249. selectedNext = $(),
  14250. /**
  14251. * Flag. Required for msie to avoid unselect files on dragstart
  14252. *
  14253. * @type Boolean
  14254. **/
  14255. selectLock = false,
  14256. /**
  14257. * Move selection to prev/next file
  14258. *
  14259. * @param String move direction
  14260. * @param Boolean append to current selection
  14261. * @return void
  14262. * @rise select
  14263. */
  14264. select = function(keyCode, append) {
  14265. var code = $.ui.keyCode,
  14266. prev = keyCode == code.LEFT || keyCode == code.UP,
  14267. sel = cwd.find('[id].'+clSelected),
  14268. selector = prev ? 'first:' : 'last',
  14269. s, n, sib, top, left;
  14270. function sibling(n, direction) {
  14271. return n[direction+'All']('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):first');
  14272. }
  14273. if (sel.length) {
  14274. s = sel.filter(prev ? ':first' : ':last');
  14275. sib = sibling(s, prev ? 'prev' : 'next');
  14276. if (!sib.length) {
  14277. // there is no sibling on required side - do not move selection
  14278. n = s;
  14279. } else if (list || keyCode == code.LEFT || keyCode == code.RIGHT) {
  14280. // find real prevoius file
  14281. n = sib;
  14282. } else {
  14283. // find up/down side file in icons view
  14284. top = s.position().top;
  14285. left = s.position().left;
  14286. n = s;
  14287. if (prev) {
  14288. do {
  14289. n = n.prev('[id]');
  14290. } while (n.length && !(n.position().top < top && n.position().left <= left));
  14291. if (n.hasClass(clDisabled)) {
  14292. n = sibling(n, 'next');
  14293. }
  14294. } else {
  14295. do {
  14296. n = n.next('[id]');
  14297. } while (n.length && !(n.position().top > top && n.position().left >= left));
  14298. if (n.hasClass(clDisabled)) {
  14299. n = sibling(n, 'prev');
  14300. }
  14301. // there is row before last one - select last file
  14302. if (!n.length) {
  14303. sib = cwd.find('[id]:not(.'+clDisabled+'):last');
  14304. if (sib.position().top > top) {
  14305. n = sib;
  14306. }
  14307. }
  14308. }
  14309. }
  14310. // !append && unselectAll();
  14311. } else {
  14312. if (selectedNext.length) {
  14313. n = prev? selectedNext.prev() : selectedNext;
  14314. } else {
  14315. // there are no selected file - select first/last one
  14316. n = cwd.find('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):'+(prev ? 'last' : 'first'));
  14317. }
  14318. }
  14319. if (n && n.length && !n.hasClass('elfinder-cwd-parent')) {
  14320. if (s && append) {
  14321. // append new files to selected
  14322. n = s.add(s[prev ? 'prevUntil' : 'nextUntil']('#'+n.attr('id'))).add(n);
  14323. } else {
  14324. // unselect selected files
  14325. sel.trigger(evtUnselect);
  14326. }
  14327. // select file(s)
  14328. n.trigger(evtSelect);
  14329. // set its visible
  14330. scrollToView(n.filter(prev ? ':first' : ':last'));
  14331. // update cache/view
  14332. trigger();
  14333. }
  14334. },
  14335. selectedFiles = {},
  14336. selectFile = function(hash) {
  14337. fm.cwdHash2Elm(hash).trigger(evtSelect);
  14338. },
  14339. allSelected = false,
  14340. selectAll = function() {
  14341. var phash = fm.cwd().hash;
  14342. selectCheckbox && selectAllCheckbox.find('input').prop('checked', true);
  14343. fm.lazy(function() {
  14344. var files;
  14345. if (fm.maxTargets && (incHashes || cwdHashes).length > fm.maxTargets) {
  14346. unselectAll({ notrigger: true });
  14347. files = $.map(incHashes || cwdHashes, function(hash) { return fm.file(hash) || null; });
  14348. files = files.slice(0, fm.maxTargets);
  14349. selectedFiles = {};
  14350. $.each(files, function(i, v) {
  14351. selectedFiles[v.hash] = true;
  14352. fm.cwdHash2Elm(v.hash).trigger(evtSelect);
  14353. });
  14354. fm.toast({mode: 'warning', msg: fm.i18n(['errMaxTargets', fm.maxTargets])});
  14355. } else {
  14356. cwd.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').trigger(evtSelect);
  14357. selectedFiles = fm.arrayFlip(incHashes || cwdHashes, true);
  14358. }
  14359. trigger();
  14360. selectCheckbox && selectAllCheckbox.data('pending', false);
  14361. }, 0, {repaint: true});
  14362. },
  14363. /**
  14364. * Unselect all files
  14365. *
  14366. * @param Object options
  14367. * @return void
  14368. */
  14369. unselectAll = function(opts) {
  14370. var o = opts || {};
  14371. selectCheckbox && selectAllCheckbox.find('input').prop('checked', false);
  14372. if (Object.keys(selectedFiles).length) {
  14373. selectLock = false;
  14374. selectedFiles = {};
  14375. cwd.find('[id].'+clSelected).trigger(evtUnselect);
  14376. selectCheckbox && cwd.find('input:checkbox.'+clSelChk).prop('checked', false);
  14377. }
  14378. !o.notrigger && trigger();
  14379. selectCheckbox && selectAllCheckbox.data('pending', false);
  14380. cwd.removeClass('elfinder-cwd-allselected');
  14381. },
  14382. selectInvert = function() {
  14383. var invHashes = {};
  14384. if (allSelected) {
  14385. unselectAll();
  14386. } else if (! Object.keys(selectedFiles).length) {
  14387. selectAll();
  14388. } else {
  14389. $.each((incHashes || cwdHashes), function(i, h) {
  14390. var itemNode = fm.cwdHash2Elm(h);
  14391. if (! selectedFiles[h]) {
  14392. invHashes[h] = true;
  14393. itemNode.length && itemNode.trigger(evtSelect);
  14394. } else {
  14395. itemNode.length && itemNode.trigger(evtUnselect);
  14396. }
  14397. });
  14398. selectedFiles = invHashes;
  14399. trigger();
  14400. }
  14401. },
  14402. /**
  14403. * Return selected files hashes list
  14404. *
  14405. * @return Array
  14406. */
  14407. selected = function() {
  14408. return Object.keys(selectedFiles);
  14409. },
  14410. /**
  14411. * Last selected node id
  14412. *
  14413. * @type String|Void
  14414. */
  14415. lastSelect = void 0,
  14416. /**
  14417. * Fire elfinder "select" event and pass selected files to it
  14418. *
  14419. * @return void
  14420. */
  14421. trigger = function() {
  14422. var selected = Object.keys(selectedFiles),
  14423. opts = {
  14424. selected : selected,
  14425. origin : 'cwd'
  14426. };
  14427. if (oldSchoolItem && (selected.length > 1 || selected[0] !== fm.cwdId2Hash(
  14428. oldSchoolItem.attr('id'))) && oldSchoolItem.hasClass(clSelected)) {
  14429. oldSchoolItem.trigger(evtUnselect);
  14430. }
  14431. allSelected = selected.length && (selected.length === (incHashes || cwdHashes).length) && (!fm.maxTargets || selected.length <= fm.maxTargets);
  14432. if (selectCheckbox) {
  14433. selectAllCheckbox.find('input').prop('checked', allSelected);
  14434. cwd[allSelected? 'addClass' : 'removeClass']('elfinder-cwd-allselected');
  14435. }
  14436. if (allSelected) {
  14437. opts.selectall = true;
  14438. } else if (! selected.length) {
  14439. opts.unselectall = true;
  14440. }
  14441. fm.trigger('select', opts);
  14442. },
  14443. /**
  14444. * Scroll file to set it visible
  14445. *
  14446. * @param DOMElement file/dir node
  14447. * @return void
  14448. */
  14449. scrollToView = function(o, blink) {
  14450. if (! o.length) {
  14451. return;
  14452. }
  14453. var ftop = o.position().top,
  14454. fheight = o.outerHeight(true),
  14455. wtop = wrapper.scrollTop(),
  14456. wheight = wrapper.get(0).clientHeight,
  14457. thheight = tableHeader? tableHeader.outerHeight(true) : 0;
  14458. if (ftop + thheight + fheight > wtop + wheight) {
  14459. wrapper.scrollTop(parseInt(ftop + thheight + fheight - wheight));
  14460. } else if (ftop < wtop) {
  14461. wrapper.scrollTop(ftop);
  14462. }
  14463. list && wrapper.scrollLeft(0);
  14464. !!blink && fm.resources.blink(o, 'lookme');
  14465. },
  14466. /**
  14467. * Files we get from server but not show yet
  14468. *
  14469. * @type Array
  14470. **/
  14471. buffer = [],
  14472. /**
  14473. * Extra data of buffer
  14474. *
  14475. * @type Object
  14476. **/
  14477. bufferExt = {},
  14478. /**
  14479. * Return index of elements with required hash in buffer
  14480. *
  14481. * @param String file hash
  14482. * @return Number
  14483. */
  14484. index = function(hash) {
  14485. var l = buffer.length;
  14486. while (l--) {
  14487. if (buffer[l].hash == hash) {
  14488. return l;
  14489. }
  14490. }
  14491. return -1;
  14492. },
  14493. /**
  14494. * Scroll start event name
  14495. *
  14496. * @type String
  14497. **/
  14498. scrollStartEvent = 'elfscrstart',
  14499. /**
  14500. * Scroll stop event name
  14501. *
  14502. * @type String
  14503. **/
  14504. scrollEvent = 'elfscrstop',
  14505. scrolling = false,
  14506. /**
  14507. * jQuery UI selectable option
  14508. *
  14509. * @type Object
  14510. */
  14511. selectableOption = {
  14512. disabled : true,
  14513. filter : '[id]:first',
  14514. stop : trigger,
  14515. delay : 250,
  14516. appendTo : 'body',
  14517. autoRefresh: false,
  14518. selected : function(e, ui) { $(ui.selected).trigger(evtSelect); },
  14519. unselected : function(e, ui) { $(ui.unselected).trigger(evtUnselect); }
  14520. },
  14521. /**
  14522. * hashes of items displayed in current view
  14523. *
  14524. * @type Object ItemHash => DomId
  14525. */
  14526. inViewHashes = {},
  14527. /**
  14528. * Processing when the current view is changed (On open, search, scroll, resize etc.)
  14529. *
  14530. * @return void
  14531. */
  14532. wrapperRepaint = function(init, recnt) {
  14533. if (!bufferExt.renderd) {
  14534. return;
  14535. }
  14536. var firstNode = (list? cwd.find('tbody:first') : cwd).children('[id]'+(options.oldSchool? ':not(.elfinder-cwd-parent)' : '')+':first');
  14537. if (!firstNode.length) {
  14538. return;
  14539. }
  14540. var selectable = cwd.data('selectable'),
  14541. rec = (function() {
  14542. var wos = wrapper.offset(),
  14543. ww = wrapper.width(),
  14544. w = $(window),
  14545. x = firstNode.width() / 2,
  14546. l = Math.min(wos.left - w.scrollLeft() + (fm.direction === 'ltr'? x : ww - x), wos.left + ww - 10),
  14547. t = wos.top - w.scrollTop() + 10 + (list? thHeight : 0);
  14548. return {left: Math.max(0, Math.round(l)), top: Math.max(0, Math.round(t))};
  14549. })(),
  14550. tgt = init? firstNode : $(document.elementFromPoint(rec.left , rec.top)),
  14551. ids = {},
  14552. tmbs = {},
  14553. multi = 5,
  14554. cnt = Math.ceil((bufferExt.hpi? Math.ceil((wz.data('rectangle').height / bufferExt.hpi) * 1.5) : showFiles) / multi),
  14555. chk = function() {
  14556. var id, hash, file, i;
  14557. for (i = 0; i < multi; i++) {
  14558. id = tgt.attr('id');
  14559. if (id) {
  14560. bufferExt.getTmbs = [];
  14561. hash = fm.cwdId2Hash(id);
  14562. inViewHashes[hash] = id;
  14563. // for tmbs
  14564. if (bufferExt.attachTmbs[hash]) {
  14565. tmbs[hash] = bufferExt.attachTmbs[hash];
  14566. }
  14567. // for selectable
  14568. selectable && (ids[id] = true);
  14569. }
  14570. // next node
  14571. tgt = tgt.next();
  14572. if (!tgt.length) {
  14573. break;
  14574. }
  14575. }
  14576. },
  14577. done = function() {
  14578. var idsArr;
  14579. if (cwd.data('selectable')) {
  14580. Object.assign(ids, selectedFiles);
  14581. idsArr = Object.keys(ids);
  14582. if (idsArr.length) {
  14583. selectableOption.filter = '#'+idsArr.join(', #');
  14584. cwd.selectable('enable').selectable('option', {filter : selectableOption.filter}).selectable('refresh');
  14585. }
  14586. }
  14587. if (Object.keys(tmbs).length) {
  14588. bufferExt.getTmbs = [];
  14589. attachThumbnails(tmbs);
  14590. }
  14591. },
  14592. setTarget = function() {
  14593. if (!tgt.hasClass(clFile)) {
  14594. tgt = tgt.closest(fileSelector);
  14595. }
  14596. },
  14597. arr, widget;
  14598. inViewHashes = {};
  14599. selectable && cwd.selectable('option', 'disabled');
  14600. if (tgt.length) {
  14601. if (!tgt.hasClass(clFile) && !tgt.closest(fileSelector).length) {
  14602. // dialog, serach button etc.
  14603. widget = fm.getUI().find('.ui-dialog:visible,.ui-widget:visible');
  14604. if (widget.length) {
  14605. widget.hide();
  14606. tgt = $(document.elementFromPoint(rec.left , rec.top));
  14607. widget.show();
  14608. } else {
  14609. widget = null;
  14610. }
  14611. }
  14612. setTarget();
  14613. if (!tgt.length) {
  14614. // try search 5px down
  14615. widget && widget.hide();
  14616. tgt = $(document.elementFromPoint(rec.left , rec.top + 5));
  14617. widget && widget.show();
  14618. setTarget();
  14619. }
  14620. }
  14621. if (tgt.length) {
  14622. if (tgt.attr('id')) {
  14623. if (init) {
  14624. for (var i = 0; i < cnt; i++) {
  14625. chk();
  14626. if (! tgt.length) {
  14627. break;
  14628. }
  14629. }
  14630. done();
  14631. } else {
  14632. bufferExt.repaintJob && bufferExt.repaintJob.state() === 'pending' && bufferExt.repaintJob.reject();
  14633. arr = new Array(cnt);
  14634. bufferExt.repaintJob = fm.asyncJob(function() {
  14635. chk();
  14636. if (! tgt.length) {
  14637. done();
  14638. bufferExt.repaintJob && bufferExt.repaintJob.state() === 'pending' && bufferExt.repaintJob.reject();
  14639. }
  14640. }, arr).done(done);
  14641. }
  14642. }
  14643. } else if (init && bufferExt.renderd) {
  14644. // In initial request, cwd DOM not renderd so doing lazy check
  14645. recnt = recnt || 0;
  14646. if (recnt < 10) { // Prevent infinite loop
  14647. requestAnimationFrame(function() {
  14648. wrapperRepaint(init, ++recnt);
  14649. });
  14650. }
  14651. }
  14652. },
  14653. /**
  14654. * Item node of oldScholl ".."
  14655. */
  14656. oldSchoolItem = null,
  14657. /**
  14658. * display parent folder with ".." name
  14659. *
  14660. * @param String phash
  14661. * @return void
  14662. */
  14663. oldSchool = function(p) {
  14664. var phash = fm.cwd().phash,
  14665. pdir = fm.file(phash) || null,
  14666. set = function(pdir) {
  14667. if (pdir) {
  14668. oldSchoolItem = $(itemhtml($.extend(true, {}, pdir, {name : '..', i18 : '..', mime : 'directory'})))
  14669. .addClass('elfinder-cwd-parent')
  14670. .on('dblclick', function() {
  14671. fm.trigger('select', {selected : [phash]}).exec('open', phash);
  14672. });
  14673. (list ? oldSchoolItem.children('td:first') : oldSchoolItem).children('.elfinder-cwd-select').remove();
  14674. if (fm.cwdHash2Elm(phash).length) {
  14675. fm.cwdHash2Elm(phash).replaceWith(oldSchoolItem);
  14676. } else {
  14677. (list ? cwd.find('tbody') : cwd).prepend(oldSchoolItem);
  14678. }
  14679. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  14680. }
  14681. };
  14682. if (pdir) {
  14683. set(pdir);
  14684. } else {
  14685. set({hash: phash, read: true, write: true});
  14686. if (fm.getUI('tree').length) {
  14687. fm.one('parents', function() {
  14688. set(fm.file(phash) || null);
  14689. wrapper.trigger(scrollEvent);
  14690. });
  14691. } else {
  14692. fm.request({
  14693. data : {cmd : 'parents', target : fm.cwd().hash},
  14694. preventFail : true
  14695. })
  14696. .done(function(data) {
  14697. set(fm.file(phash) || null);
  14698. wrapper.trigger(scrollEvent);
  14699. });
  14700. }
  14701. }
  14702. },
  14703. showFiles = fm.options.showFiles,
  14704. /**
  14705. * Cwd scroll event handler.
  14706. * Lazy load - append to cwd not shown files
  14707. *
  14708. * @return void
  14709. */
  14710. render = function() {
  14711. if (bufferExt.rendering || (bufferExt.renderd && ! buffer.length)) {
  14712. return;
  14713. }
  14714. var place = (list ? cwd.children('table').children('tbody') : cwd),
  14715. phash,
  14716. chk,
  14717. // created document fragment for jQuery >= 1.12, 2.2, 3.0
  14718. // see Studio-42/elFinder#1544 @ github
  14719. docFlag = $.htmlPrefilter? true : false,
  14720. tempDom = docFlag? $(document.createDocumentFragment()) : $('<div></div>'),
  14721. go = function(o){
  14722. var over = o || null,
  14723. html = [],
  14724. dirs = false,
  14725. atmb = {},
  14726. stmb = (fm.option('tmbUrl') === 'self'),
  14727. init = bufferExt.renderd? false : true,
  14728. files, locks, selected;
  14729. files = buffer.splice(0, showFiles + (over || 0) / (bufferExt.hpi || 1));
  14730. bufferExt.renderd += files.length;
  14731. if (! buffer.length) {
  14732. bottomMarker.hide();
  14733. wrapper.off(scrollEvent, render);
  14734. }
  14735. locks = [];
  14736. html = $.map(files, function(f) {
  14737. if (f.hash && f.name) {
  14738. if (f.mime == 'directory') {
  14739. dirs = true;
  14740. }
  14741. if ((f.tmb && (f.tmb != 1 || f.size > 0)) || (stmb && f.mime.indexOf('image/') === 0)) {
  14742. atmb[f.hash] = f.tmb || 'self';
  14743. }
  14744. clipCuts[f.hash] && locks.push(f.hash);
  14745. return itemhtml(f);
  14746. }
  14747. return null;
  14748. });
  14749. // html into temp node
  14750. tempDom.empty().append(html.join(''));
  14751. // make directory droppable
  14752. dirs && !mobile && makeDroppable(tempDom);
  14753. // check selected items
  14754. selected = [];
  14755. if (Object.keys(selectedFiles).length) {
  14756. tempDom.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').each(function() {
  14757. selectedFiles[fm.cwdId2Hash(this.id)] && selected.push($(this));
  14758. });
  14759. }
  14760. // append to cwd
  14761. place.append(docFlag? tempDom : tempDom.children());
  14762. // trigger select
  14763. if (selected.length) {
  14764. $.each(selected, function(i, n) { n.trigger(evtSelect); });
  14765. trigger();
  14766. }
  14767. locks.length && fm.trigger('lockfiles', {files: locks});
  14768. !bufferExt.hpi && bottomMarkerShow(place, files.length);
  14769. if (list) {
  14770. // show thead
  14771. cwd.find('thead').show();
  14772. // fixed table header
  14773. fixTableHeader({fitWidth: ! colWidth});
  14774. }
  14775. if (Object.keys(atmb).length) {
  14776. Object.assign(bufferExt.attachTmbs, atmb);
  14777. }
  14778. if (init) {
  14779. if (! mobile && ! cwd.data('selectable')) {
  14780. // make files selectable
  14781. cwd.selectable(selectableOption).data('selectable', true);
  14782. }
  14783. }
  14784. ! scrolling && wrapper.trigger(scrollEvent);
  14785. };
  14786. if (! bufferExt.renderd) {
  14787. // first time to go()
  14788. bufferExt.rendering = true;
  14789. // scroll top on dir load to avoid scroll after page reload
  14790. wrapper.scrollTop(0);
  14791. phash = fm.cwd().phash;
  14792. go();
  14793. if (options.oldSchool) {
  14794. if (phash && !query) {
  14795. oldSchool(phash);
  14796. } else {
  14797. oldSchoolItem = $();
  14798. }
  14799. }
  14800. if (list) {
  14801. colWidth && setColwidth();
  14802. fixTableHeader({fitWidth: true});
  14803. }
  14804. bufferExt.itemH = (list? place.find('tr:first') : place.find('[id]:first')).outerHeight(true);
  14805. fm.trigger('cwdrender');
  14806. bufferExt.rendering = false;
  14807. wrapperRepaint(true);
  14808. }
  14809. if (! bufferExt.rendering && buffer.length) {
  14810. // next go()
  14811. if ((chk = (wrapper.height() + wrapper.scrollTop() + fm.options.showThreshold + bufferExt.row) - (bufferExt.renderd * bufferExt.hpi)) > 0) {
  14812. bufferExt.rendering = true;
  14813. fm.lazy(function() {
  14814. go(chk);
  14815. bufferExt.rendering = false;
  14816. });
  14817. } else {
  14818. !fm.enabled() && resize();
  14819. }
  14820. } else {
  14821. resize();
  14822. }
  14823. },
  14824. // fixed table header jQuery object
  14825. tableHeader = null,
  14826. // Is UA support CSS sticky
  14827. cssSticky = fm.UA.CSS.positionSticky && fm.UA.CSS.widthMaxContent,
  14828. // To fixed table header colmun
  14829. fixTableHeader = function(optsArg) {
  14830. thHeight = 0;
  14831. if (! options.listView.fixedHeader) {
  14832. return;
  14833. }
  14834. var setPos = function() {
  14835. var val, pos;
  14836. pos = (fm.direction === 'ltr')? 'left' : 'right';
  14837. val = ((fm.direction === 'ltr')? wrapper.scrollLeft() : table.outerWidth(true) - wrapper.width() - wrapper.scrollLeft()) * -1;
  14838. if (base.css(pos) !== val) {
  14839. base.css(pos, val);
  14840. }
  14841. },
  14842. opts = optsArg || {},
  14843. cnt, base, table, htable, thead, tbody, hheight, htr, btr, htd, btd, htw, btw, init;
  14844. tbody = cwd.find('tbody');
  14845. btr = tbody.children('tr:first');
  14846. if (btr.length && btr.is(':visible')) {
  14847. table = tbody.parent();
  14848. if (! tableHeader) {
  14849. init = true;
  14850. tbody.addClass('elfinder-cwd-fixheader');
  14851. thead = cwd.find('thead').attr('id', fm.namespace+'-cwd-thead');
  14852. htr = thead.children('tr:first');
  14853. hheight = htr.outerHeight(true);
  14854. cwd.css('margin-top', hheight - parseInt(table.css('padding-top')));
  14855. if (cssSticky) {
  14856. tableHeader = $('<div class="elfinder-table-header-sticky"></div>').addClass(cwd.attr('class')).append($('<table></table>').append(thead));
  14857. cwd.after(tableHeader);
  14858. wrapper.on('resize.fixheader', function(e) {
  14859. e.stopPropagation();
  14860. fixTableHeader({fitWidth: true});
  14861. });
  14862. } else {
  14863. base = $('<div></div>').addClass(cwd.attr('class')).append($('<table></table>').append(thead));
  14864. tableHeader = $('<div></div>').addClass(wrapper.attr('class') + ' elfinder-cwd-fixheader')
  14865. .removeClass('ui-droppable native-droppable')
  14866. .css(wrapper.position())
  14867. .css({ height: hheight, width: cwd.outerWidth() })
  14868. .append(base);
  14869. if (fm.direction === 'rtl') {
  14870. tableHeader.css('left', (wrapper.data('width') - wrapper.width()) + 'px');
  14871. }
  14872. setPos();
  14873. wrapper.after(tableHeader)
  14874. .on('scroll.fixheader resize.fixheader', function(e) {
  14875. setPos();
  14876. if (e.type === 'resize') {
  14877. e.stopPropagation();
  14878. tableHeader.css(wrapper.position());
  14879. wrapper.data('width', wrapper.css('overflow', 'hidden').width());
  14880. wrapper.css('overflow', 'auto');
  14881. fixTableHeader();
  14882. }
  14883. });
  14884. }
  14885. } else {
  14886. thead = $('#'+fm.namespace+'-cwd-thead');
  14887. htr = thead.children('tr:first');
  14888. }
  14889. if (init || opts.fitWidth || Math.abs(btr.outerWidth() - htr.outerWidth()) > 2) {
  14890. cnt = customCols.length + 1;
  14891. for (var i = 0; i < cnt; i++) {
  14892. htd = htr.children('td:eq('+i+')');
  14893. btd = btr.children('td:eq('+i+')');
  14894. htw = htd.width();
  14895. btw = btd.width();
  14896. if (typeof htd.data('delta') === 'undefined') {
  14897. htd.data('delta', (htd.outerWidth() - htw) - (btd.outerWidth() - btw));
  14898. }
  14899. btw -= htd.data('delta');
  14900. if (! init && ! opts.fitWidth && htw === btw) {
  14901. break;
  14902. }
  14903. htd.css('width', btw + 'px');
  14904. }
  14905. }
  14906. if (!cssSticky) {
  14907. tableHeader.data('widthTimer') && cancelAnimationFrame(tableHeader.data('widthTimer'));
  14908. tableHeader.data('widthTimer', requestAnimationFrame(function() {
  14909. if (tableHeader) {
  14910. tableHeader.css('width', mBoard.width() + 'px');
  14911. if (fm.direction === 'rtl') {
  14912. tableHeader.css('left', (wrapper.data('width') - wrapper.width()) + 'px');
  14913. }
  14914. }
  14915. }));
  14916. }
  14917. thHeight = thead.height();
  14918. }
  14919. },
  14920. // Set colmun width
  14921. setColwidth = function() {
  14922. if (list && colWidth) {
  14923. var cl = 'elfinder-cwd-colwidth',
  14924. first = cwd.find('tr[id]:first'),
  14925. former;
  14926. if (! first.hasClass(cl)) {
  14927. former = cwd.find('tr.'+cl);
  14928. former.removeClass(cl).find('td').css('width', '');
  14929. first.addClass(cl);
  14930. cwd.find('table:first').css('table-layout', 'fixed');
  14931. $.each($.merge(['name'], customCols), function(i, k) {
  14932. var w = colWidth[k] || first.find('td.elfinder-col-'+k).width();
  14933. first.find('td.elfinder-col-'+k).width(w);
  14934. });
  14935. }
  14936. }
  14937. },
  14938. /**
  14939. * Droppable options for cwd.
  14940. * Drop target is `wrapper`
  14941. * Do not add class on childs file over
  14942. *
  14943. * @type Object
  14944. */
  14945. droppable = Object.assign({}, fm.droppable, {
  14946. over : function(e, ui) {
  14947. var dst = $(this),
  14948. helper = ui.helper,
  14949. ctr = (e.shiftKey || e.ctrlKey || e.metaKey),
  14950. hash, status, inParent;
  14951. e.stopPropagation();
  14952. helper.data('dropover', helper.data('dropover') + 1);
  14953. dst.data('dropover', true);
  14954. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  14955. if (helper.data('namespace') !== fm.namespace || ! fm.insideWorkzone(e.pageX, e.pageY)) {
  14956. dst.removeClass(clDropActive);
  14957. //helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  14958. return;
  14959. }
  14960. if (dst.hasClass(fm.res(c, 'cwdfile'))) {
  14961. hash = fm.cwdId2Hash(dst.attr('id'));
  14962. dst.data('dropover', hash);
  14963. } else {
  14964. hash = fm.cwd().hash;
  14965. fm.cwd().write && dst.data('dropover', hash);
  14966. }
  14967. inParent = (fm.file(helper.data('files')[0]).phash === hash);
  14968. if (dst.data('dropover') === hash) {
  14969. $.each(helper.data('files'), function(i, h) {
  14970. if (h === hash || (inParent && !ctr && !helper.hasClass('elfinder-drag-helper-plus'))) {
  14971. dst.removeClass(clDropActive);
  14972. return false; // break $.each
  14973. }
  14974. });
  14975. } else {
  14976. dst.removeClass(clDropActive);
  14977. }
  14978. if (helper.data('locked') || inParent) {
  14979. status = 'elfinder-drag-helper-plus';
  14980. } else {
  14981. status = 'elfinder-drag-helper-move';
  14982. if (ctr) {
  14983. status += ' elfinder-drag-helper-plus';
  14984. }
  14985. }
  14986. dst.hasClass(clDropActive) && helper.addClass(status);
  14987. requestAnimationFrame(function(){ dst.hasClass(clDropActive) && helper.addClass(status); });
  14988. },
  14989. out : function(e, ui) {
  14990. var helper = ui.helper;
  14991. e.stopPropagation();
  14992. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0));
  14993. $(this).removeData('dropover')
  14994. .removeClass(clDropActive);
  14995. },
  14996. deactivate : function() {
  14997. $(this).removeData('dropover')
  14998. .removeClass(clDropActive);
  14999. },
  15000. drop : function(e, ui) {
  15001. unselectAll({ notrigger: true });
  15002. fm.droppable.drop.call(this, e, ui);
  15003. }
  15004. }),
  15005. /**
  15006. * Make directory droppable
  15007. *
  15008. * @return void
  15009. */
  15010. makeDroppable = function(place) {
  15011. place = place? place : (list ? cwd.find('tbody') : cwd);
  15012. var targets = place.children('.directory:not(.'+clDroppable+',.elfinder-na,.elfinder-ro)');
  15013. if (fm.isCommandEnabled('paste')) {
  15014. targets.droppable(droppable);
  15015. }
  15016. if (fm.isCommandEnabled('upload')) {
  15017. targets.addClass('native-droppable');
  15018. }
  15019. place.children('.isroot').each(function(i, n) {
  15020. var $n = $(n),
  15021. hash = fm.cwdId2Hash(n.id);
  15022. if (fm.isCommandEnabled('paste', hash)) {
  15023. if (! $n.hasClass(clDroppable+',elfinder-na,elfinder-ro')) {
  15024. $n.droppable(droppable);
  15025. }
  15026. } else {
  15027. if ($n.hasClass(clDroppable)) {
  15028. $n.droppable('destroy');
  15029. }
  15030. }
  15031. if (fm.isCommandEnabled('upload', hash)) {
  15032. if (! $n.hasClass('native-droppable,elfinder-na,elfinder-ro')) {
  15033. $n.addClass('native-droppable');
  15034. }
  15035. } else {
  15036. if ($n.hasClass('native-droppable')) {
  15037. $n.removeClass('native-droppable');
  15038. }
  15039. }
  15040. });
  15041. },
  15042. /**
  15043. * Preload required thumbnails and on load add css to files.
  15044. * Return false if required file is not visible yet (in buffer) -
  15045. * required for old api to stop loading thumbnails.
  15046. *
  15047. * @param Object file hash -> thumbnail map
  15048. * @param Bool reload
  15049. * @return void
  15050. */
  15051. attachThumbnails = function(tmbs, reload) {
  15052. var attach = function(node, tmb) {
  15053. $('<img/>')
  15054. .on('load', function() {
  15055. node.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')");
  15056. })
  15057. .attr('src', tmb.url);
  15058. },
  15059. chk = function(hash, tmb) {
  15060. var node = fm.cwdHash2Elm(hash),
  15061. file, tmbObj, reloads = [];
  15062. if (node.length) {
  15063. if (tmb != '1') {
  15064. file = fm.file(hash);
  15065. if (file.tmb !== tmb) {
  15066. file.tmb = tmb;
  15067. }
  15068. tmbObj = fm.tmb(file);
  15069. if (reload) {
  15070. node.find('.elfinder-cwd-icon').addClass(tmbObj.className).css('background-image', "url('"+tmbObj.url+"')");
  15071. } else {
  15072. attach(node, tmbObj);
  15073. }
  15074. delete bufferExt.attachTmbs[hash];
  15075. } else {
  15076. if (reload) {
  15077. loadThumbnails([hash]);
  15078. } else if (! bufferExt.tmbLoading[hash]) {
  15079. bufferExt.getTmbs.push(hash);
  15080. }
  15081. }
  15082. }
  15083. };
  15084. if ($.isPlainObject(tmbs) && Object.keys(tmbs).length) {
  15085. Object.assign(bufferExt.attachTmbs, tmbs);
  15086. $.each(tmbs, chk);
  15087. if (! reload && bufferExt.getTmbs.length && ! Object.keys(bufferExt.tmbLoading).length) {
  15088. loadThumbnails();
  15089. }
  15090. }
  15091. },
  15092. /**
  15093. * Load thumbnails from backend.
  15094. *
  15095. * @param Array|void reloads hashes list for reload thumbnail items
  15096. * @return void
  15097. */
  15098. loadThumbnails = function(reloads) {
  15099. var tmbs = [],
  15100. reload = false;
  15101. if (fm.oldAPI) {
  15102. fm.request({
  15103. data : {cmd : 'tmb', current : fm.cwd().hash},
  15104. preventFail : true
  15105. })
  15106. .done(function(data) {
  15107. if (data.images && Object.keys(data.images).length) {
  15108. attachThumbnails(data.images);
  15109. }
  15110. if (data.tmb) {
  15111. loadThumbnails();
  15112. }
  15113. });
  15114. return;
  15115. }
  15116. if (reloads) {
  15117. reload = true;
  15118. tmbs = reloads.splice(0, tmbNum);
  15119. } else {
  15120. tmbs = bufferExt.getTmbs.splice(0, tmbNum);
  15121. }
  15122. if (tmbs.length) {
  15123. if (reload || inViewHashes[tmbs[0]] || inViewHashes[tmbs[tmbs.length-1]]) {
  15124. $.each(tmbs, function(i, h) {
  15125. bufferExt.tmbLoading[h] = true;
  15126. });
  15127. fm.request({
  15128. data : {cmd : 'tmb', targets : tmbs},
  15129. preventFail : true
  15130. })
  15131. .done(function(data) {
  15132. var errs = [],
  15133. resLen;
  15134. if (data.images) {
  15135. if (resLen = Object.keys(data.images).length) {
  15136. if (resLen < tmbs.length) {
  15137. $.each(tmbs, function(i, h) {
  15138. if (! data.images[h]) {
  15139. errs.push(h);
  15140. }
  15141. });
  15142. }
  15143. attachThumbnails(data.images, reload);
  15144. } else {
  15145. errs = tmbs;
  15146. }
  15147. // unset error items from bufferExt.attachTmbs
  15148. if (errs.length) {
  15149. $.each(errs, function(i, h) {
  15150. delete bufferExt.attachTmbs[h];
  15151. });
  15152. }
  15153. }
  15154. if (reload) {
  15155. if (reloads.length) {
  15156. loadThumbnails(reloads);
  15157. }
  15158. }
  15159. })
  15160. .always(function() {
  15161. bufferExt.tmbLoading = {};
  15162. if (! reload && bufferExt.getTmbs.length) {
  15163. loadThumbnails();
  15164. }
  15165. });
  15166. }
  15167. }
  15168. },
  15169. /**
  15170. * Add new files to cwd/buffer
  15171. *
  15172. * @param Array new files
  15173. * @return void
  15174. */
  15175. add = function(files, mode) {
  15176. var place = list ? cwd.find('tbody') : cwd,
  15177. l = files.length,
  15178. atmb = {},
  15179. findNode = function(file) {
  15180. var pointer = cwd.find('[id]:first'), file2;
  15181. while (pointer.length) {
  15182. file2 = fm.file(fm.cwdId2Hash(pointer.attr('id')));
  15183. if (!pointer.hasClass('elfinder-cwd-parent') && file2 && fm.compare(file, file2) < 0) {
  15184. return pointer;
  15185. }
  15186. pointer = pointer.next('[id]');
  15187. }
  15188. },
  15189. findIndex = function(file) {
  15190. var l = buffer.length, i;
  15191. for (i =0; i < l; i++) {
  15192. if (fm.compare(file, buffer[i]) < 0) {
  15193. return i;
  15194. }
  15195. }
  15196. return l || -1;
  15197. },
  15198. // created document fragment for jQuery >= 1.12, 2.2, 3.0
  15199. // see Studio-42/elFinder#1544 @ github
  15200. docFlag = $.htmlPrefilter? true : false,
  15201. tempDom = docFlag? $(document.createDocumentFragment()) : $('<div></div>'),
  15202. file, hash, node, nodes, ndx, stmb;
  15203. if (l > showFiles) {
  15204. // re-render for performance tune
  15205. content();
  15206. selectedFiles = fm.arrayFlip($.map(files, function(f) { return f.hash; }), true);
  15207. trigger();
  15208. } else {
  15209. // add the item immediately
  15210. l && wz.removeClass('elfinder-cwd-wrapper-empty');
  15211. // Self thumbnail
  15212. stmb = (fm.option('tmbUrl') === 'self');
  15213. while (l--) {
  15214. file = files[l];
  15215. hash = file.hash;
  15216. if (fm.cwdHash2Elm(hash).length) {
  15217. continue;
  15218. }
  15219. if ((node = findNode(file)) && ! node.length) {
  15220. node = null;
  15221. }
  15222. if (! node && (ndx = findIndex(file)) >= 0) {
  15223. buffer.splice(ndx, 0, file);
  15224. } else {
  15225. tempDom.empty().append(itemhtml(file));
  15226. (file.mime === 'directory') && !mobile && makeDroppable(tempDom);
  15227. nodes = docFlag? tempDom : tempDom.children();
  15228. if (node) {
  15229. node.before(nodes);
  15230. } else {
  15231. place.append(nodes);
  15232. }
  15233. ++bufferExt.renderd;
  15234. }
  15235. if (fm.cwdHash2Elm(hash).length) {
  15236. if ((file.tmb && (file.tmb != 1 || file.size > 0)) || (stmb && file.mime.indexOf('image/') === 0)) {
  15237. atmb[hash] = file.tmb || 'self';
  15238. }
  15239. }
  15240. }
  15241. if (list) {
  15242. setColwidth();
  15243. fixTableHeader({fitWidth: ! colWidth});
  15244. }
  15245. bottomMarkerShow(place);
  15246. if (Object.keys(atmb).length) {
  15247. Object.assign(bufferExt.attachTmbs, atmb);
  15248. if (buffer.length < 1) {
  15249. loadThumbnails();
  15250. }
  15251. }
  15252. }
  15253. },
  15254. /**
  15255. * Remove files from cwd/buffer
  15256. *
  15257. * @param Array files hashes
  15258. * @return void
  15259. */
  15260. remove = function(files) {
  15261. var l = files.length,
  15262. inSearch = fm.searchStatus.state > 1,
  15263. curCmd = fm.getCommand(fm.currentReqCmd) || {},
  15264. hash, n, ndx, found;
  15265. // removed cwd
  15266. if (!fm.cwd().hash && !curCmd.noChangeDirOnRemovedCwd) {
  15267. $.each(cwdParents.reverse(), function(i, h) {
  15268. if (fm.file(h)) {
  15269. found = true;
  15270. fm.one(fm.currentReqCmd + 'done', function() {
  15271. !fm.cwd().hash && fm.exec('open', h);
  15272. });
  15273. return false;
  15274. }
  15275. });
  15276. // fallback to fm.roots[0]
  15277. !found && !fm.cwd().hash && fm.exec('open', fm.roots[Object.keys(fm.roots)[0]]);
  15278. return;
  15279. }
  15280. while (l--) {
  15281. hash = files[l];
  15282. if ((n = fm.cwdHash2Elm(hash)).length) {
  15283. try {
  15284. n.remove();
  15285. --bufferExt.renderd;
  15286. } catch(e) {
  15287. fm.debug('error', e);
  15288. }
  15289. } else if ((ndx = index(hash)) !== -1) {
  15290. buffer.splice(ndx, 1);
  15291. }
  15292. selectedFiles[hash] && delete selectedFiles[hash];
  15293. if (inSearch) {
  15294. if ((ndx = $.inArray(hash, cwdHashes)) !== -1) {
  15295. cwdHashes.splice(ndx, 1);
  15296. }
  15297. }
  15298. }
  15299. inSearch && fm.trigger('cwdhasheschange', cwdHashes);
  15300. if (list) {
  15301. setColwidth();
  15302. fixTableHeader({fitWidth: ! colWidth});
  15303. }
  15304. },
  15305. customColsNameBuild = function() {
  15306. var name = '',
  15307. customColsName = '';
  15308. for (var i = 0; i < customCols.length; i++) {
  15309. name = fm.getColumnName(customCols[i]);
  15310. customColsName +='<td class="elfinder-cwd-view-th-'+customCols[i]+' sortable-item">'+name+'</td>';
  15311. }
  15312. return customColsName;
  15313. },
  15314. setItemBoxSize = function(boxSize) {
  15315. var place, elm;
  15316. if (!boxSize.height) {
  15317. place = (list ? cwd.find('tbody') : cwd);
  15318. elm = place.find(list? 'tr:first' : '[id]:first');
  15319. boxSize.height = elm.outerHeight(true);
  15320. if (!list) {
  15321. boxSize.width = elm.outerWidth(true);
  15322. }
  15323. }
  15324. },
  15325. bottomMarkerShow = function(cur, cnt) {
  15326. var place = cur || (list ? cwd.find('tbody') : cwd),
  15327. boxSize = itemBoxSize[fm.viewType],
  15328. col = 1,
  15329. row;
  15330. if (buffer.length > 0) {
  15331. if (!bufferExt.hpi) {
  15332. setItemBoxSize(boxSize);
  15333. if (! list) {
  15334. col = Math.floor(place.width() / boxSize.width);
  15335. bufferExt.row = boxSize.height;
  15336. bufferExt.hpi = bufferExt.row / col;
  15337. } else {
  15338. bufferExt.row = bufferExt.hpi = boxSize.height;
  15339. }
  15340. } else if (!list) {
  15341. col = Math.floor(place.width() / boxSize.width);
  15342. }
  15343. row = Math.ceil((buffer.length + (cnt || 0)) / col);
  15344. if (list && tableHeader) {
  15345. ++row;
  15346. }
  15347. bottomMarker.css({top: (bufferExt.row * row) + 'px'}).show();
  15348. }
  15349. },
  15350. wrapperContextMenu = {
  15351. contextmenu : function(e) {
  15352. e.preventDefault();
  15353. if (cwd.data('longtap') !== void(0)) {
  15354. e.stopPropagation();
  15355. return;
  15356. }
  15357. fm.trigger('contextmenu', {
  15358. 'type' : 'cwd',
  15359. 'targets' : [fm.cwd().hash],
  15360. 'x' : e.pageX,
  15361. 'y' : e.pageY
  15362. });
  15363. },
  15364. touchstart : function(e) {
  15365. if (e.originalEvent.touches.length > 1) {
  15366. return;
  15367. }
  15368. if (cwd.data('longtap') !== false) {
  15369. wrapper.data('touching', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY});
  15370. cwd.data('tmlongtap', setTimeout(function(){
  15371. // long tap
  15372. cwd.data('longtap', true);
  15373. fm.trigger('contextmenu', {
  15374. 'type' : 'cwd',
  15375. 'targets' : [fm.cwd().hash],
  15376. 'x' : wrapper.data('touching').x,
  15377. 'y' : wrapper.data('touching').y
  15378. });
  15379. }, 500));
  15380. }
  15381. cwd.data('longtap', null);
  15382. },
  15383. touchend : function(e) {
  15384. if (e.type === 'touchmove') {
  15385. if (! wrapper.data('touching') ||
  15386. ( Math.abs(wrapper.data('touching').x - e.originalEvent.touches[0].pageX)
  15387. + Math.abs(wrapper.data('touching').y - e.originalEvent.touches[0].pageY)) > 4) {
  15388. wrapper.data('touching', null);
  15389. }
  15390. } else {
  15391. setTimeout(function() {
  15392. cwd.removeData('longtap');
  15393. }, 80);
  15394. }
  15395. clearTimeout(cwd.data('tmlongtap'));
  15396. },
  15397. click : function(e) {
  15398. if (cwd.data('longtap')) {
  15399. e.preventDefault();
  15400. e.stopPropagation();
  15401. }
  15402. }
  15403. },
  15404. /**
  15405. * Update directory content
  15406. *
  15407. * @return void
  15408. */
  15409. content = function() {
  15410. fm.lazy(function() {
  15411. var phash, emptyMethod, thtr;
  15412. wz.append(selectAllCheckbox).removeClass('elfinder-cwd-wrapper-empty elfinder-search-result elfinder-incsearch-result elfinder-letsearch-result');
  15413. if (fm.searchStatus.state > 1 || fm.searchStatus.ininc) {
  15414. wz.addClass('elfinder-search-result' + (fm.searchStatus.ininc? ' elfinder-'+(query.substr(0,1) === '/' ? 'let':'inc')+'search-result' : ''));
  15415. }
  15416. // abort attachThumbJob
  15417. bufferExt.attachThumbJob && bufferExt.attachThumbJob._abort();
  15418. // destroy selectable for GC
  15419. cwd.data('selectable') && cwd.selectable('disable').selectable('destroy').removeData('selectable');
  15420. // notify cwd init
  15421. fm.trigger('cwdinit');
  15422. selectedNext = $();
  15423. try {
  15424. // to avoid problem with draggable
  15425. cwd.empty();
  15426. } catch (e) {
  15427. cwd.html('');
  15428. }
  15429. if (tableHeader) {
  15430. wrapper.off('scroll.fixheader resize.fixheader');
  15431. tableHeader.remove();
  15432. tableHeader = null;
  15433. }
  15434. cwd.removeClass('elfinder-cwd-view-icons elfinder-cwd-view-list')
  15435. .addClass('elfinder-cwd-view-'+(list ? 'list' :'icons'))
  15436. .attr('style', '')
  15437. .css('height', 'auto');
  15438. bottomMarker.hide();
  15439. wrapper[list ? 'addClass' : 'removeClass']('elfinder-cwd-wrapper-list')._padding = parseInt(wrapper.css('padding-top')) + parseInt(wrapper.css('padding-bottom'));
  15440. if (fm.UA.iOS) {
  15441. wrapper.removeClass('overflow-scrolling-touch').addClass('overflow-scrolling-touch');
  15442. }
  15443. if (list) {
  15444. cwd.html('<table><thead></thead><tbody></tbody></table>');
  15445. thtr = $('<tr class="ui-state-default"><td class="elfinder-cwd-view-th-name">'+fm.getColumnName('name')+'</td>'+customColsNameBuild()+'</tr>');
  15446. cwd.find('thead').hide().append(thtr).find('td:first').append(selectAllCheckbox);
  15447. if ($.fn.sortable) {
  15448. thtr.addClass('touch-punch touch-punch-keep-default')
  15449. .sortable({
  15450. axis: 'x',
  15451. distance: 8,
  15452. items: '> .sortable-item',
  15453. start: function(e, ui) {
  15454. $(ui.item[0]).data('dragging', true);
  15455. ui.placeholder
  15456. .width(ui.helper.removeClass('ui-state-hover').width())
  15457. .removeClass('ui-state-active')
  15458. .addClass('ui-state-hover')
  15459. .css('visibility', 'visible');
  15460. },
  15461. update: function(e, ui){
  15462. var target = $(ui.item[0]).attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', ''),
  15463. prev, done;
  15464. customCols = $.map($(this).children(), function(n) {
  15465. var name = $(n).attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', '');
  15466. if (! done) {
  15467. if (target === name) {
  15468. done = true;
  15469. } else {
  15470. prev = name;
  15471. }
  15472. }
  15473. return (name === 'name')? null : name;
  15474. });
  15475. templates.row = makeTemplateRow();
  15476. fm.storage('cwdCols', customCols);
  15477. prev = '.elfinder-col-'+prev+':first';
  15478. target = '.elfinder-col-'+target+':first';
  15479. fm.lazy(function() {
  15480. cwd.find('tbody tr').each(function() {
  15481. var $this = $(this);
  15482. $this.children(prev).after($this.children(target));
  15483. });
  15484. });
  15485. },
  15486. stop: function(e, ui) {
  15487. setTimeout(function() {
  15488. $(ui.item[0]).removeData('dragging');
  15489. }, 100);
  15490. }
  15491. });
  15492. }
  15493. thtr.find('td').addClass('touch-punch').resizable({
  15494. handles: fm.direction === 'ltr'? 'e' : 'w',
  15495. start: function(e, ui) {
  15496. var target = cwd.find('td.elfinder-col-'
  15497. + ui.element.attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', '')
  15498. + ':first');
  15499. ui.element
  15500. .data('dragging', true)
  15501. .data('resizeTarget', target)
  15502. .data('targetWidth', target.width());
  15503. colResizing = true;
  15504. if (cwd.find('table').css('table-layout') !== 'fixed') {
  15505. cwd.find('tbody tr:first td').each(function() {
  15506. $(this).width($(this).width());
  15507. });
  15508. cwd.find('table').css('table-layout', 'fixed');
  15509. }
  15510. },
  15511. resize: function(e, ui) {
  15512. ui.element.data('resizeTarget').width(ui.element.data('targetWidth') - (ui.originalSize.width - ui.size.width));
  15513. },
  15514. stop : function(e, ui) {
  15515. colResizing = false;
  15516. fixTableHeader({fitWidth: true});
  15517. colWidth = {};
  15518. cwd.find('tbody tr:first td').each(function() {
  15519. var name = $(this).attr('class').split(' ')[0].replace('elfinder-col-', '');
  15520. colWidth[name] = $(this).width();
  15521. });
  15522. fm.storage('cwdColWidth', colWidth);
  15523. setTimeout(function() {
  15524. ui.element.removeData('dragging');
  15525. }, 100);
  15526. }
  15527. })
  15528. .find('.ui-resizable-handle').addClass('ui-icon ui-icon-grip-dotted-vertical');
  15529. }
  15530. buffer = $.map(incHashes || cwdHashes, function(hash) { return fm.file(hash) || null; });
  15531. buffer = fm.sortFiles(buffer);
  15532. if (incHashes) {
  15533. incHashes = $.map(buffer, function(f) { return f.hash; });
  15534. } else {
  15535. cwdHashes = $.map(buffer, function(f) { return f.hash; });
  15536. }
  15537. bufferExt = {
  15538. renderd: 0,
  15539. attachTmbs: {},
  15540. getTmbs: [],
  15541. tmbLoading: {},
  15542. lazyOpts: { tm : 0 }
  15543. };
  15544. wz[(buffer.length < 1) ? 'addClass' : 'removeClass']('elfinder-cwd-wrapper-empty');
  15545. wrapper.off(scrollEvent, render).on(scrollEvent, render).trigger(scrollEvent);
  15546. // set droppable
  15547. if (!fm.cwd().write) {
  15548. wrapper.removeClass('native-droppable')
  15549. .droppable('disable')
  15550. .removeClass('ui-state-disabled'); // for old jQueryUI see https://bugs.jqueryui.com/ticket/5974
  15551. } else {
  15552. wrapper[fm.isCommandEnabled('upload')? 'addClass' : 'removeClass']('native-droppable');
  15553. wrapper.droppable(fm.isCommandEnabled('paste')? 'enable' : 'disable');
  15554. }
  15555. });
  15556. },
  15557. /**
  15558. * CWD node itself
  15559. *
  15560. * @type JQuery
  15561. **/
  15562. cwd = $(this)
  15563. .addClass('ui-helper-clearfix elfinder-cwd')
  15564. .attr('unselectable', 'on')
  15565. // fix ui.selectable bugs and add shift+click support
  15566. .on('click.'+fm.namespace, fileSelector, function(e) {
  15567. var p = this.id ? $(this) : $(this).parents('[id]:first'),
  15568. tgt = $(e.target),
  15569. prev,
  15570. next,
  15571. pl,
  15572. nl,
  15573. sib;
  15574. if (selectCheckbox && (tgt.is('input:checkbox.'+clSelChk) || tgt.hasClass('elfinder-cwd-select'))) {
  15575. e.stopPropagation();
  15576. e.preventDefault();
  15577. p.trigger(p.hasClass(clSelected) ? evtUnselect : evtSelect);
  15578. trigger();
  15579. requestAnimationFrame(function() {
  15580. tgt.prop('checked', p.hasClass(clSelected));
  15581. });
  15582. return;
  15583. }
  15584. if (cwd.data('longtap') || tgt.hasClass('elfinder-cwd-nonselect')) {
  15585. e.stopPropagation();
  15586. return;
  15587. }
  15588. if (!curClickId) {
  15589. curClickId = p.attr('id');
  15590. setTimeout(function() {
  15591. curClickId = '';
  15592. }, 500);
  15593. }
  15594. if (e.shiftKey) {
  15595. prev = p.prevAll(lastSelect || '.'+clSelected+':first');
  15596. next = p.nextAll(lastSelect || '.'+clSelected+':first');
  15597. pl = prev.length;
  15598. nl = next.length;
  15599. }
  15600. if (e.shiftKey && (pl || nl)) {
  15601. sib = pl ? p.prevUntil('#'+prev.attr('id')) : p.nextUntil('#'+next.attr('id'));
  15602. sib = sib.add(p);
  15603. if (!pl) {
  15604. sib = $(sib.get().reverse());
  15605. }
  15606. sib.trigger(evtSelect);
  15607. } else if (e.ctrlKey || e.metaKey) {
  15608. p.trigger(p.hasClass(clSelected) ? evtUnselect : evtSelect);
  15609. } else {
  15610. if (wrapper.data('touching') && p.hasClass(clSelected)) {
  15611. wrapper.data('touching', null);
  15612. fm.dblclick({file : fm.cwdId2Hash(this.id)});
  15613. return;
  15614. } else {
  15615. unselectAll({ notrigger: true });
  15616. p.trigger(evtSelect);
  15617. }
  15618. }
  15619. trigger();
  15620. })
  15621. // call fm.open()
  15622. .on('dblclick.'+fm.namespace, fileSelector, function(e) {
  15623. if (curClickId) {
  15624. var hash = fm.cwdId2Hash(curClickId);
  15625. e.stopPropagation();
  15626. if (this.id !== curClickId) {
  15627. $(this).trigger(evtUnselect);
  15628. $('#'+curClickId).trigger(evtSelect);
  15629. trigger();
  15630. }
  15631. fm.dblclick({file : hash});
  15632. }
  15633. })
  15634. // for touch device
  15635. .on('touchstart.'+fm.namespace, fileSelector, function(e) {
  15636. if (e.originalEvent.touches.length > 1) {
  15637. return;
  15638. }
  15639. var p = this.id ? $(this) : $(this).parents('[id]:first'),
  15640. tgt = $(e.target),
  15641. nodeName = e.target.nodeName,
  15642. sel;
  15643. if ((nodeName === 'INPUT' && e.target.type === 'text') || nodeName === 'TEXTAREA' || tgt.hasClass('elfinder-cwd-nonselect')) {
  15644. e.stopPropagation();
  15645. return;
  15646. }
  15647. // now name editing
  15648. if (p.find('input:text,textarea').length) {
  15649. e.stopPropagation();
  15650. e.preventDefault();
  15651. return;
  15652. }
  15653. wrapper.data('touching', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY});
  15654. if (selectCheckbox && (tgt.is('input:checkbox.'+clSelChk) || tgt.hasClass('elfinder-cwd-select'))) {
  15655. return;
  15656. }
  15657. sel = p.prevAll('.'+clSelected+':first').length +
  15658. p.nextAll('.'+clSelected+':first').length;
  15659. cwd.data('longtap', null);
  15660. if (Object.keys(selectedFiles).length
  15661. ||
  15662. (list && e.target.nodeName !== 'TD')
  15663. ||
  15664. (!list && this !== e.target)
  15665. ) {
  15666. cwd.data('longtap', false);
  15667. p.addClass(clHover);
  15668. p.data('tmlongtap', setTimeout(function(){
  15669. // long tap
  15670. cwd.data('longtap', true);
  15671. p.trigger(evtSelect);
  15672. trigger();
  15673. fm.trigger('contextmenu', {
  15674. 'type' : 'files',
  15675. 'targets' : fm.selected(),
  15676. 'x' : e.originalEvent.touches[0].pageX,
  15677. 'y' : e.originalEvent.touches[0].pageY
  15678. });
  15679. }, 500));
  15680. }
  15681. })
  15682. .on('touchmove.'+fm.namespace+' touchend.'+fm.namespace, fileSelector, function(e) {
  15683. var tgt = $(e.target),
  15684. p;
  15685. if (selectCheckbox && (tgt.is('input:checkbox.'+clSelChk) || tgt.hasClass('elfinder-cwd-select'))) {
  15686. return;
  15687. }
  15688. if (e.target.nodeName == 'INPUT' || e.target.nodeName == 'TEXTAREA') {
  15689. e.stopPropagation();
  15690. return;
  15691. }
  15692. p = this.id ? $(this) : $(this).parents('[id]:first');
  15693. clearTimeout(p.data('tmlongtap'));
  15694. if (e.type === 'touchmove') {
  15695. wrapper.data('touching', null);
  15696. p.removeClass(clHover);
  15697. } else {
  15698. if (wrapper.data('touching') && !cwd.data('longtap') && p.hasClass(clSelected)) {
  15699. e.preventDefault();
  15700. wrapper.data('touching', null);
  15701. fm.dblclick({file : fm.cwdId2Hash(this.id)});
  15702. }
  15703. setTimeout(function() {
  15704. cwd.removeData('longtap');
  15705. }, 80);
  15706. }
  15707. })
  15708. // attach draggable
  15709. .on('mouseenter.'+fm.namespace, fileSelector, function(e) {
  15710. if (scrolling) { return; }
  15711. var $this = $(this), helper = null;
  15712. if (!mobile && !$this.data('dragRegisted') && !$this.hasClass(clTmp) && !$this.hasClass(clDraggable) && !$this.hasClass(clDisabled)) {
  15713. $this.data('dragRegisted', true);
  15714. if (!fm.isCommandEnabled('copy', fm.searchStatus.state > 1 || $this.hasClass('isroot')? fm.cwdId2Hash($this.attr('id')) : void 0)) {
  15715. return;
  15716. }
  15717. $this.on('mousedown', function(e) {
  15718. // shiftKey or altKey + drag start for HTML5 native drag function
  15719. // Note: can no use shiftKey with the Google Chrome
  15720. var metaKey = options.metakeyDragout && !fm.UA.IE && (e.shiftKey || e.altKey),
  15721. disable = false;
  15722. if (metaKey && cwd.data('selectable')) {
  15723. // destroy jQuery-ui selectable while trigger native drag
  15724. cwd.selectable('disable').selectable('destroy').removeData('selectable');
  15725. requestAnimationFrame(function(){
  15726. cwd.selectable(selectableOption).selectable('option', {disabled: false}).selectable('refresh').data('selectable', true);
  15727. });
  15728. }
  15729. $this.removeClass('ui-state-disabled');
  15730. if (metaKey) {
  15731. $this.draggable('option', 'disabled', true).attr('draggable', 'true');
  15732. } else {
  15733. if (!$this.hasClass(clSelected)) {
  15734. if (list) {
  15735. disable = $(e.target).closest('span,tr').is('tr');
  15736. } else {
  15737. disable = $(e.target).hasClass('elfinder-cwd-file');
  15738. }
  15739. }
  15740. if (disable) {
  15741. // removeClass('ui-state-disabled') for old version of jQueryUI
  15742. $this.draggable('option', 'disabled', true).removeClass('ui-state-disabled');
  15743. } else {
  15744. $this.draggable('option', 'disabled', false)
  15745. .removeAttr('draggable')
  15746. .draggable('option', 'cursorAt', {left: 50 - parseInt($(e.currentTarget).css('margin-left')), top: 47});
  15747. }
  15748. }
  15749. })
  15750. .on('dragstart', function(e) {
  15751. var dt = e.dataTransfer || e.originalEvent.dataTransfer || null;
  15752. helper = null;
  15753. if (dt && !fm.UA.IE) {
  15754. var p = this.id ? $(this) : $(this).parents('[id]:first'),
  15755. elm = $('<span>'),
  15756. url = '',
  15757. durl = null,
  15758. murl = null,
  15759. files = [],
  15760. icon = function(f) {
  15761. var mime = f.mime, i, tmb = fm.tmb(f);
  15762. i = '<div class="elfinder-cwd-icon elfinder-cwd-icon-drag '+fm.mime2class(mime)+' ui-corner-all"></div>';
  15763. if (tmb) {
  15764. i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML;
  15765. }
  15766. return i;
  15767. }, l, geturl = [];
  15768. p.trigger(evtSelect);
  15769. trigger();
  15770. $.each(selectedFiles, function(v){
  15771. var file = fm.file(v),
  15772. furl = file.url;
  15773. if (file && file.mime !== 'directory') {
  15774. if (!furl) {
  15775. furl = fm.url(file.hash);
  15776. } else if (furl == '1') {
  15777. geturl.push(v);
  15778. return true;
  15779. }
  15780. if (furl) {
  15781. furl = fm.convAbsUrl(furl);
  15782. files.push(v);
  15783. $('<a>').attr('href', furl).text(furl).appendTo(elm);
  15784. url += furl + "\n";
  15785. if (!durl) {
  15786. durl = file.mime + ':' + file.name + ':' + furl;
  15787. }
  15788. if (!murl) {
  15789. murl = furl + "\n" + file.name;
  15790. }
  15791. }
  15792. }
  15793. });
  15794. if (geturl.length) {
  15795. $.each(geturl, function(i, v){
  15796. var rfile = fm.file(v);
  15797. rfile.url = '';
  15798. fm.request({
  15799. data : {cmd : 'url', target : v},
  15800. notify : {type : 'url', cnt : 1},
  15801. preventDefault : true
  15802. })
  15803. .always(function(data) {
  15804. rfile.url = data.url? data.url : '1';
  15805. });
  15806. });
  15807. return false;
  15808. } else if (url) {
  15809. if (dt.setDragImage) {
  15810. helper = $('<div class="elfinder-drag-helper html5-native"></div>').append(icon(fm.file(files[0]))).appendTo($(document.body));
  15811. if ((l = files.length) > 1) {
  15812. helper.append(icon(fm.file(files[l-1])) + '<span class="elfinder-drag-num">'+l+'</span>');
  15813. }
  15814. dt.setDragImage(helper.get(0), 50, 47);
  15815. }
  15816. dt.effectAllowed = 'copyLink';
  15817. dt.setData('DownloadURL', durl);
  15818. dt.setData('text/x-moz-url', murl);
  15819. dt.setData('text/uri-list', url);
  15820. dt.setData('text/plain', url);
  15821. dt.setData('text/html', elm.html());
  15822. dt.setData('elfinderfrom', window.location.href + fm.cwd().hash);
  15823. dt.setData('elfinderfrom:' + dt.getData('elfinderfrom'), '');
  15824. } else {
  15825. return false;
  15826. }
  15827. }
  15828. })
  15829. .on('dragend', function(e){
  15830. unselectAll({ notrigger: true });
  15831. helper && helper.remove();
  15832. })
  15833. .draggable(fm.draggable);
  15834. }
  15835. })
  15836. // add hover class to selected file
  15837. .on(evtSelect, fileSelector, function(e) {
  15838. var $this = $(this),
  15839. id = fm.cwdId2Hash($this.attr('id'));
  15840. if (!selectLock && !$this.hasClass(clDisabled)) {
  15841. lastSelect = '#'+ this.id;
  15842. $this.addClass(clSelected).children().addClass(clHover).find('input:checkbox.'+clSelChk).prop('checked', true);
  15843. if (! selectedFiles[id]) {
  15844. selectedFiles[id] = true;
  15845. }
  15846. // will be selected next
  15847. selectedNext = cwd.find('[id].'+clSelected+':last').next();
  15848. }
  15849. })
  15850. // remove hover class from unselected file
  15851. .on(evtUnselect, fileSelector, function(e) {
  15852. var $this = $(this),
  15853. id = fm.cwdId2Hash($this.attr('id'));
  15854. if (!selectLock) {
  15855. $this.removeClass(clSelected).children().removeClass(clHover).find('input:checkbox.'+clSelChk).prop('checked', false);
  15856. if (cwd.hasClass('elfinder-cwd-allselected')) {
  15857. selectCheckbox && selectAllCheckbox.children('input').prop('checked', false);
  15858. cwd.removeClass('elfinder-cwd-allselected');
  15859. }
  15860. selectedFiles[id] && delete selectedFiles[id];
  15861. }
  15862. })
  15863. // disable files wich removing or moving
  15864. .on(evtDisable, fileSelector, function() {
  15865. var $this = $(this).removeClass(clHover+' '+clSelected).addClass(clDisabled),
  15866. child = $this.children(),
  15867. target = (list ? $this : child.find('div.elfinder-cwd-file-wrapper,div.elfinder-cwd-filename'));
  15868. child.removeClass(clHover+' '+clSelected);
  15869. $this.hasClass(clDroppable) && $this.droppable('disable');
  15870. target.hasClass(clDraggable) && target.draggable('disable');
  15871. })
  15872. // if any files was not removed/moved - unlock its
  15873. .on(evtEnable, fileSelector, function() {
  15874. var $this = $(this).removeClass(clDisabled),
  15875. target = list ? $this : $this.children('div.elfinder-cwd-file-wrapper,div.elfinder-cwd-filename');
  15876. $this.hasClass(clDroppable) && $this.droppable('enable');
  15877. target.hasClass(clDraggable) && target.draggable('enable');
  15878. })
  15879. .on('scrolltoview', fileSelector, function(e, data) {
  15880. scrollToView($(this), (data && typeof data.blink !== 'undefined')? data.blink : true);
  15881. })
  15882. .on('mouseenter.'+fm.namespace+' mouseleave.'+fm.namespace, fileSelector, function(e) {
  15883. var enter = (e.type === 'mouseenter');
  15884. if (enter && (scrolling || fm.UA.Mobile)) { return; }
  15885. fm.trigger('hover', {hash : fm.cwdId2Hash($(this).attr('id')), type : e.type});
  15886. $(this).toggleClass(clHover, (e.type == 'mouseenter'));
  15887. })
  15888. // for file contextmenu
  15889. .on('mouseenter.'+fm.namespace+' mouseleave.'+fm.namespace, '.elfinder-cwd-file-wrapper,.elfinder-cwd-filename', function(e) {
  15890. var enter = (e.type === 'mouseenter');
  15891. if (enter && scrolling) { return; }
  15892. $(this).closest(fileSelector).children('.elfinder-cwd-file-wrapper,.elfinder-cwd-filename').toggleClass(clActive, (e.type == 'mouseenter'));
  15893. })
  15894. .on('contextmenu.'+fm.namespace, function(e) {
  15895. var file = $(e.target).closest(fileSelector);
  15896. if (file.get(0) === e.target && !selectedFiles[fm.cwdId2Hash(file.get(0).id)]) {
  15897. return;
  15898. }
  15899. // now filename editing
  15900. if (file.find('input:text,textarea').length) {
  15901. e.stopPropagation();
  15902. return;
  15903. }
  15904. if (file.length && (e.target.nodeName != 'TD' || selectedFiles[fm.cwdId2Hash(file.get(0).id)])) {
  15905. e.stopPropagation();
  15906. e.preventDefault();
  15907. if (!file.hasClass(clDisabled) && !wrapper.data('touching')) {
  15908. if (!file.hasClass(clSelected)) {
  15909. unselectAll({ notrigger: true });
  15910. file.trigger(evtSelect);
  15911. trigger();
  15912. }
  15913. fm.trigger('contextmenu', {
  15914. 'type' : 'files',
  15915. 'targets' : fm.selected(),
  15916. 'x' : e.pageX,
  15917. 'y' : e.pageY
  15918. });
  15919. }
  15920. }
  15921. })
  15922. // unselect all on cwd click
  15923. .on('click.'+fm.namespace, function(e) {
  15924. if (e.target === this && ! cwd.data('longtap')) {
  15925. !e.shiftKey && !e.ctrlKey && !e.metaKey && unselectAll();
  15926. }
  15927. })
  15928. // prepend fake file/dir
  15929. .on('create.'+fm.namespace, function(e, f) {
  15930. var parent = list ? cwd.find('tbody') : cwd,
  15931. p = parent.find('.elfinder-cwd-parent'),
  15932. lock = f.move || false,
  15933. file = $(itemhtml(f)).addClass(clTmp),
  15934. selected = fm.selected();
  15935. if (selected.length) {
  15936. lock && fm.trigger('lockfiles', {files: selected});
  15937. } else {
  15938. unselectAll();
  15939. }
  15940. if (p.length) {
  15941. p.after(file);
  15942. } else {
  15943. parent.prepend(file);
  15944. }
  15945. setColwidth();
  15946. wrapper.scrollTop(0).scrollLeft(0);
  15947. })
  15948. // unselect all selected files
  15949. .on('unselectall', unselectAll)
  15950. .on('selectfile', function(e, id) {
  15951. fm.cwdHash2Elm(id).trigger(evtSelect);
  15952. trigger();
  15953. })
  15954. .on('colwidth', function() {
  15955. if (list) {
  15956. cwd.find('table').css('table-layout', '')
  15957. .find('td').css('width', '');
  15958. fixTableHeader({fitWidth: true});
  15959. fm.storage('cwdColWidth', colWidth = null);
  15960. }
  15961. })
  15962. .on('iconpref', function(e, data) {
  15963. cwd.removeClass(function(i, cName) {
  15964. return (cName.match(/\belfinder-cwd-size\S+/g) || []).join(' ');
  15965. });
  15966. iconSize = data? (parseInt(data.size) || 0) : 0;
  15967. if (!list) {
  15968. if (iconSize > 0) {
  15969. cwd.addClass('elfinder-cwd-size' + iconSize);
  15970. }
  15971. if (bufferExt.renderd) {
  15972. requestAnimationFrame(function() {
  15973. itemBoxSize.icons = {};
  15974. bufferExt.hpi = null;
  15975. bottomMarkerShow(cwd, bufferExt.renderd);
  15976. wrapperRepaint();
  15977. });
  15978. }
  15979. }
  15980. })
  15981. // Change icon size with mouse wheel event
  15982. .on('onwheel' in document ? 'wheel' : 'mousewheel', function(e) {
  15983. var tm, size, delta;
  15984. if (!list && ((e.ctrlKey && !e.metaKey) || (!e.ctrlKey && e.metaKey))) {
  15985. e.stopPropagation();
  15986. e.preventDefault();
  15987. tm = cwd.data('wheelTm');
  15988. if (typeof tm !== 'undefined') {
  15989. clearTimeout(tm);
  15990. cwd.data('wheelTm', setTimeout(function() {
  15991. cwd.removeData('wheelTm');
  15992. }, 200));
  15993. } else {
  15994. cwd.data('wheelTm', false);
  15995. size = iconSize || 0;
  15996. delta = e.originalEvent.deltaY ? e.originalEvent.deltaY : -(e.originalEvent.wheelDelta);
  15997. if (delta > 0) {
  15998. if (iconSize > 0) {
  15999. size = iconSize - 1;
  16000. }
  16001. } else {
  16002. if (iconSize < options.iconsView.sizeMax) {
  16003. size = iconSize + 1;
  16004. }
  16005. }
  16006. if (size !== iconSize) {
  16007. fm.storage('iconsize', size);
  16008. cwd.trigger('iconpref', {size: size});
  16009. }
  16010. }
  16011. }
  16012. }),
  16013. wrapper = $('<div class="elfinder-cwd-wrapper"></div>')
  16014. // make cwd itself droppable for folders from nav panel
  16015. .droppable(Object.assign({}, droppable, {autoDisable: false}))
  16016. .on('contextmenu.'+fm.namespace, wrapperContextMenu.contextmenu)
  16017. .on('touchstart.'+fm.namespace, wrapperContextMenu.touchstart)
  16018. .on('touchmove.'+fm.namespace+' touchend.'+fm.namespace, wrapperContextMenu.touchend)
  16019. .on('click.'+fm.namespace, wrapperContextMenu.click)
  16020. .on('scroll.'+fm.namespace, function() {
  16021. if (! scrolling) {
  16022. cwd.data('selectable') && cwd.selectable('disable');
  16023. wrapper.trigger(scrollStartEvent);
  16024. }
  16025. scrolling = true;
  16026. bufferExt.scrtm && cancelAnimationFrame(bufferExt.scrtm);
  16027. if (bufferExt.scrtm && Math.abs((bufferExt.scrolltop || 0) - (bufferExt.scrolltop = (this.scrollTop || $(this).scrollTop()))) < 5) {
  16028. bufferExt.scrtm = 0;
  16029. wrapper.trigger(scrollEvent);
  16030. }
  16031. bufferExt.scrtm = requestAnimationFrame(function() {
  16032. bufferExt.scrtm = 0;
  16033. wrapper.trigger(scrollEvent);
  16034. });
  16035. })
  16036. .on(scrollEvent, function() {
  16037. scrolling = false;
  16038. wrapperRepaint();
  16039. }),
  16040. bottomMarker = $('<div>&nbsp;</div>')
  16041. .css({position: 'absolute', width: '1px', height: '1px'})
  16042. .hide(),
  16043. selectAllCheckbox = selectCheckbox? $('<div class="elfinder-cwd-selectall"><input type="checkbox"/></div>')
  16044. .attr('title', fm.i18n('selectall'))
  16045. .on('click', function(e) {
  16046. e.stopPropagation();
  16047. e.preventDefault();
  16048. if ($(this).data('pending')) {
  16049. return false;
  16050. }
  16051. selectAllCheckbox.data('pending', true);
  16052. if (cwd.hasClass('elfinder-cwd-allselected')) {
  16053. selectAllCheckbox.find('input').prop('checked', false);
  16054. requestAnimationFrame(function() {
  16055. unselectAll();
  16056. });
  16057. } else {
  16058. selectAll();
  16059. }
  16060. }) : $(),
  16061. restm = null,
  16062. resize = function(init) {
  16063. var initHeight = function() {
  16064. if (typeof bufferExt.renderd !== 'undefined') {
  16065. var h = 0;
  16066. wrapper.siblings('div.elfinder-panel:visible').each(function() {
  16067. h += $(this).outerHeight(true);
  16068. });
  16069. wrapper.height(wz.height() - h - wrapper._padding);
  16070. }
  16071. };
  16072. init && initHeight();
  16073. restm && cancelAnimationFrame(restm);
  16074. restm = requestAnimationFrame(function(){
  16075. !init && initHeight();
  16076. var wph, cwdoh;
  16077. // fix cwd height if it less then wrapper
  16078. cwd.css('height', 'auto');
  16079. wph = wrapper[0].clientHeight - parseInt(wrapper.css('padding-top')) - parseInt(wrapper.css('padding-bottom')) - parseInt(cwd.css('margin-top')),
  16080. cwdoh = cwd.outerHeight(true);
  16081. if (cwdoh < wph) {
  16082. cwd.height(wph);
  16083. }
  16084. });
  16085. list && ! colResizing && (init? wrapper.trigger('resize.fixheader') : fixTableHeader());
  16086. wrapperRepaint();
  16087. },
  16088. // elfinder node
  16089. parent = $(this).parent().on('resize', resize),
  16090. // workzone node
  16091. wz = parent.children('.elfinder-workzone').append(wrapper.append(this).append(bottomMarker)),
  16092. // message board
  16093. mBoard = $('<div class="elfinder-cwd-message-board"></div>').insertAfter(cwd),
  16094. // Volume expires
  16095. vExpires = $('<div class="elfinder-cwd-expires" ></div>'),
  16096. vExpiresTm,
  16097. showVolumeExpires = function() {
  16098. var remain, sec, int;
  16099. vExpiresTm && clearTimeout(vExpiresTm);
  16100. if (curVolId && fm.volumeExpires[curVolId]) {
  16101. sec = fm.volumeExpires[curVolId] - ((+new Date()) / 1000);
  16102. int = (sec % 60) + 0.1;
  16103. remain = Math.floor(sec / 60);
  16104. vExpires.html(fm.i18n(['minsLeft', remain])).show();
  16105. if (remain) {
  16106. vExpiresTm = setTimeout(showVolumeExpires, int * 1000);
  16107. }
  16108. }
  16109. },
  16110. // each item box size
  16111. itemBoxSize = {
  16112. icons : {},
  16113. list : {}
  16114. },
  16115. // has UI tree
  16116. hasUiTree,
  16117. // Icon size of icons view
  16118. iconSize,
  16119. // Current volume id
  16120. curVolId,
  16121. winScrTm;
  16122. // IE < 11 not support CSS `pointer-events: none`
  16123. if (!fm.UA.ltIE10) {
  16124. mBoard.append($('<div class="elfinder-cwd-trash" ></div>').html(fm.i18n('volume_Trash')))
  16125. .append(vExpires);
  16126. }
  16127. // setup by options
  16128. replacement = Object.assign(replacement, options.replacement || {});
  16129. try {
  16130. colWidth = fm.storage('cwdColWidth')? fm.storage('cwdColWidth') : null;
  16131. } catch(e) {
  16132. colWidth = null;
  16133. }
  16134. // setup costomCols
  16135. fm.bind('columnpref', function(e) {
  16136. var opts = e.data || {};
  16137. if (customCols = fm.storage('cwdCols')) {
  16138. customCols = $.grep(customCols, function(n) {
  16139. return (options.listView.columns.indexOf(n) !== -1)? true : false;
  16140. });
  16141. if (options.listView.columns.length > customCols.length) {
  16142. $.each(options.listView.columns, function(i, n) {
  16143. if (customCols.indexOf(n) === -1) {
  16144. customCols.push(n);
  16145. }
  16146. });
  16147. }
  16148. } else {
  16149. customCols = options.listView.columns;
  16150. }
  16151. // column names array that hidden
  16152. var columnhides = fm.storage('columnhides') || null;
  16153. if (columnhides && Object.keys(columnhides).length)
  16154. customCols = $.grep(customCols, function(n) {
  16155. return columnhides[n]? false : true;
  16156. });
  16157. // make template with customCols
  16158. templates.row = makeTemplateRow();
  16159. // repaint if need it
  16160. list && opts.repaint && content();
  16161. }).trigger('columnpref');
  16162. if (mobile) {
  16163. // for iOS5 bug
  16164. $('body').on('touchstart touchmove touchend', function(e){});
  16165. }
  16166. selectCheckbox && cwd.addClass('elfinder-has-checkbox');
  16167. $(window).on('scroll.'+fm.namespace, function() {
  16168. winScrTm && cancelAnimationFrame(winScrTm);
  16169. winScrTm = requestAnimationFrame(function() {
  16170. wrapper.trigger(scrollEvent);
  16171. });
  16172. });
  16173. $(document).on('keydown.'+fm.namespace, function(e) {
  16174. if (e.keyCode == $.ui.keyCode.ESCAPE) {
  16175. if (! fm.getUI().find('.ui-widget:visible').length) {
  16176. unselectAll();
  16177. }
  16178. }
  16179. });
  16180. fm
  16181. .one('init', function(){
  16182. var style = document.createElement('style'),
  16183. sheet, node, base, resizeTm, iconSize, i = 0;
  16184. if (document.head) {
  16185. document.head.appendChild(style);
  16186. sheet = style.sheet;
  16187. sheet.insertRule('.elfinder-cwd-wrapper-empty .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyFolder')+'" }', i++);
  16188. sheet.insertRule('.elfinder-cwd-wrapper-empty .native-droppable .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyFolder'+(mobile? 'LTap' : 'Drop'))+'" }', i++);
  16189. sheet.insertRule('.elfinder-cwd-wrapper-empty .ui-droppable-disabled .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyFolder')+'" }', i++);
  16190. sheet.insertRule('.elfinder-cwd-wrapper-empty.elfinder-search-result .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptySearch')+'" }', i++);
  16191. sheet.insertRule('.elfinder-cwd-wrapper-empty.elfinder-search-result.elfinder-incsearch-result .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyIncSearch')+'" }', i++);
  16192. sheet.insertRule('.elfinder-cwd-wrapper-empty.elfinder-search-result.elfinder-letsearch-result .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyLetSearch')+'" }', i++);
  16193. }
  16194. if (iconSize = (fm.storage('iconsize') || options.iconsView.size || 0)) {
  16195. iconSize = Math.min(iconSize, options.iconsView.sizeMax);
  16196. cwd.trigger('iconpref', {size: iconSize});
  16197. }
  16198. if (! mobile) {
  16199. fm.one('open', function() {
  16200. sheet && fm.zIndex && sheet.insertRule('.ui-selectable-helper{z-index:'+fm.zIndex+';}', i++);
  16201. });
  16202. base = $('<div style="position:absolute"></div>');
  16203. node = fm.getUI();
  16204. node.on('resize', function(e, data) {
  16205. var offset;
  16206. e.preventDefault();
  16207. e.stopPropagation();
  16208. if (data && data.fullscreen) {
  16209. offset = node.offset();
  16210. if (data.fullscreen === 'on') {
  16211. base.css({top:offset.top * -1 , left:offset.left * -1 }).appendTo(node);
  16212. selectableOption.appendTo = base;
  16213. } else {
  16214. base.detach();
  16215. selectableOption.appendTo = 'body';
  16216. }
  16217. cwd.data('selectable') && cwd.selectable('option', {appendTo : selectableOption.appendTo});
  16218. }
  16219. });
  16220. }
  16221. hasUiTree = fm.getUI('tree').length;
  16222. })
  16223. .bind('enable', function() {
  16224. resize();
  16225. })
  16226. .bind('request.open', function() {
  16227. bufferExt.getTmbs = [];
  16228. })
  16229. .one('open', function() {
  16230. if (fm.maxTargets) {
  16231. tmbNum = Math.min(fm.maxTargets, tmbNum);
  16232. }
  16233. })
  16234. .bind('open add remove searchend', function() {
  16235. var phash = fm.cwd().hash,
  16236. type = this.type;
  16237. if (type === 'open' || type === 'searchend' || fm.searchStatus.state < 2) {
  16238. cwdHashes = $.map(fm.files(phash), function(f) { return f.hash; });
  16239. fm.trigger('cwdhasheschange', cwdHashes);
  16240. }
  16241. if (type === 'open') {
  16242. var inTrash = function() {
  16243. var isIn = false;
  16244. $.each(cwdParents, function(i, h) {
  16245. if (fm.trashes[h]) {
  16246. isIn = true;
  16247. return false;
  16248. }
  16249. });
  16250. return isIn;
  16251. },
  16252. req = phash?
  16253. (! fm.file(phash) || hasUiTree?
  16254. (! hasUiTree?
  16255. fm.request({
  16256. data: {
  16257. cmd : 'parents',
  16258. target : fm.cwd().hash
  16259. },
  16260. preventFail : true
  16261. }) : (function() {
  16262. var dfd = $.Deferred();
  16263. fm.one('treesync', function(e) {
  16264. e.data.always(function() {
  16265. dfd.resolve();
  16266. });
  16267. });
  16268. return dfd;
  16269. })()
  16270. ) : null
  16271. ) : null,
  16272. cwdObj = fm.cwd();
  16273. // add/remove volume id class
  16274. if (cwdObj.volumeid !== curVolId) {
  16275. vExpires.empty().hide();
  16276. if (curVolId) {
  16277. wrapper.removeClass('elfinder-cwd-wrapper-' + curVolId);
  16278. }
  16279. curVolId = cwdObj.volumeid;
  16280. showVolumeExpires();
  16281. wrapper.addClass('elfinder-cwd-wrapper-' + curVolId);
  16282. }
  16283. // add/remove trash class
  16284. $.when(req).done(function() {
  16285. cwdParents = fm.parents(cwdObj.hash);
  16286. wrapper[inTrash()? 'addClass':'removeClass']('elfinder-cwd-wrapper-trash');
  16287. });
  16288. incHashes = void 0;
  16289. unselectAll({ notrigger: true });
  16290. content();
  16291. }
  16292. })
  16293. .bind('search', function(e) {
  16294. cwdHashes = $.map(e.data.files, function(f) { return f.hash; });
  16295. fm.trigger('cwdhasheschange', cwdHashes);
  16296. incHashes = void 0;
  16297. fm.searchStatus.ininc = false;
  16298. content();
  16299. fm.autoSync('stop');
  16300. })
  16301. .bind('searchend', function(e) {
  16302. if (query || incHashes) {
  16303. query = '';
  16304. if (incHashes) {
  16305. fm.trigger('incsearchend', e.data);
  16306. } else {
  16307. if (!e.data || !e.data.noupdate) {
  16308. content();
  16309. }
  16310. }
  16311. }
  16312. fm.autoSync();
  16313. })
  16314. .bind('searchstart', function(e) {
  16315. unselectAll();
  16316. query = e.data.query;
  16317. })
  16318. .bind('incsearchstart', function(e) {
  16319. var q = e.data.query || '',
  16320. type = e.data.type || 'SearchName',
  16321. searchTypes = fm.options.commandsOptions.search.searchTypes || {};
  16322. if ((searchTypes[type] && searchTypes[type].incsearch) || type === 'SearchName') {
  16323. selectedFiles = {};
  16324. fm.lazy(function() {
  16325. // incremental search
  16326. var regex, incSearch, fst = '';
  16327. query = q;
  16328. if (q) {
  16329. if (q.substr(0,1) === '/') {
  16330. q = q.substr(1);
  16331. fst = '^';
  16332. }
  16333. regex = new RegExp(fst + q.replace(/([\\*\;\.\?\[\]\{\}\(\)\^\$\-\|])/g, '\\$1'), 'i');
  16334. if (type === 'SearchName') {
  16335. incHashes = $.grep(cwdHashes, function(hash) {
  16336. var file = fm.file(hash);
  16337. return (file && (file.name.match(regex) || (file.i18 && file.i18.match(regex))))? true : false;
  16338. });
  16339. } else {
  16340. incSearch = searchTypes[type].incsearch;
  16341. if (typeof incSearch === 'string') {
  16342. incHashes = $.grep(cwdHashes, function(hash) {
  16343. var file = fm.file(hash);
  16344. return (file && file[incSearch] && (file[incSearch] + '').match(regex))? true : false;
  16345. });
  16346. } else if (typeof incSearch === 'function') {
  16347. try {
  16348. incHashes = $.grep(incSearch({val: q, regex: regex}, cwdHashes, fm), function(hash) {
  16349. return fm.file(hash)? true : false;
  16350. });
  16351. } catch(e) {
  16352. incHashes = [];
  16353. }
  16354. }
  16355. }
  16356. fm.trigger('incsearch', { hashes: incHashes, query: q })
  16357. .searchStatus.ininc = true;
  16358. content();
  16359. fm.autoSync('stop');
  16360. } else {
  16361. fm.trigger('incsearchend');
  16362. }
  16363. });
  16364. }
  16365. })
  16366. .bind('incsearchend', function(e) {
  16367. query = '';
  16368. fm.searchStatus.ininc = false;
  16369. incHashes = void 0;
  16370. if (!e.data || !e.data.noupdate) {
  16371. content();
  16372. }
  16373. fm.autoSync();
  16374. })
  16375. .bind('sortchange', function() {
  16376. var lastScrollLeft = wrapper.scrollLeft(),
  16377. allsel = cwd.hasClass('elfinder-cwd-allselected');
  16378. content();
  16379. fm.one('cwdrender', function() {
  16380. wrapper.scrollLeft(lastScrollLeft);
  16381. if (allsel) {
  16382. selectedFiles = fm.arrayFlip(incHashes || cwdHashes, true);
  16383. }
  16384. (allsel || Object.keys(selectedFiles).length) && trigger();
  16385. });
  16386. })
  16387. .bind('viewchange', function() {
  16388. var l = fm.viewType != 'list',
  16389. allsel = cwd.hasClass('elfinder-cwd-allselected');
  16390. if (l != list) {
  16391. list = l;
  16392. fm.viewType = list? 'list' : 'icons';
  16393. if (iconSize) {
  16394. fm.one('cwdinit', function() {
  16395. cwd.trigger('iconpref', {size: iconSize});
  16396. });
  16397. }
  16398. content();
  16399. resize();
  16400. if (allsel) {
  16401. cwd.addClass('elfinder-cwd-allselected');
  16402. selectAllCheckbox.find('input').prop('checked', true);
  16403. }
  16404. Object.keys(selectedFiles).length && trigger();
  16405. }
  16406. })
  16407. .bind('wzresize', function() {
  16408. var place = list ? cwd.find('tbody') : cwd,
  16409. cwdOffset;
  16410. resize(true);
  16411. if (bufferExt.hpi) {
  16412. bottomMarkerShow(place, place.find('[id]').length);
  16413. }
  16414. cwdOffset = cwd.offset();
  16415. wz.data('rectangle', Object.assign(
  16416. {
  16417. width: wz.width(),
  16418. height: wz.height(),
  16419. cwdEdge: (fm.direction === 'ltr')? cwdOffset.left : cwdOffset.left + cwd.width()
  16420. },
  16421. wz.offset())
  16422. );
  16423. bufferExt.itemH = (list? place.find('tr:first') : place.find('[id]:first')).outerHeight(true);
  16424. })
  16425. .bind('changeclipboard', function(e) {
  16426. clipCuts = {};
  16427. if (e.data && e.data.clipboard && e.data.clipboard.length) {
  16428. $.each(e.data.clipboard, function(i, f) {
  16429. if (f.cut) {
  16430. clipCuts[f.hash] = true;
  16431. }
  16432. });
  16433. }
  16434. })
  16435. .bind('resMixinMake', function() {
  16436. setColwidth();
  16437. })
  16438. .bind('tmbreload', function(e) {
  16439. var imgs = {},
  16440. files = (e.data && e.data.files)? e.data.files : null;
  16441. $.each(files, function(i, f) {
  16442. if (f.tmb && f.tmb != '1') {
  16443. imgs[f.hash] = f.tmb;
  16444. }
  16445. });
  16446. if (Object.keys(imgs).length) {
  16447. attachThumbnails(imgs, true);
  16448. }
  16449. })
  16450. .add(function(e) {
  16451. var regex = query? new RegExp(query.replace(/([\\*\;\.\?\[\]\{\}\(\)\^\$\-\|])/g, '\\$1'), 'i') : null,
  16452. mime = fm.searchStatus.mime,
  16453. inSearch = fm.searchStatus.state > 1,
  16454. phash = inSearch && fm.searchStatus.target? fm.searchStatus.target : fm.cwd().hash,
  16455. curPath = fm.path(phash),
  16456. inTarget = function(f) {
  16457. var res, parents, path;
  16458. res = (f.phash === phash);
  16459. if (!res && inSearch) {
  16460. path = f.path || fm.path(f.hash);
  16461. res = (curPath && path.indexOf(curPath) === 0);
  16462. if (! res && fm.searchStatus.mixed) {
  16463. res = $.grep(fm.searchStatus.mixed, function(vid) { return f.hash.indexOf(vid) === 0? true : false; }).length? true : false;
  16464. }
  16465. }
  16466. if (res && inSearch) {
  16467. if (mime) {
  16468. res = (f.mime.indexOf(mime) === 0);
  16469. } else {
  16470. res = (f.name.match(regex) || (f.i18 && f.i18.match(regex)))? true : false;
  16471. }
  16472. }
  16473. return res;
  16474. },
  16475. files = $.grep(e.data.added || [], function(f) { return inTarget(f)? true : false ;});
  16476. add(files);
  16477. if (fm.searchStatus.state === 2) {
  16478. $.each(files, function(i, f) {
  16479. if ($.inArray(f.hash, cwdHashes) === -1) {
  16480. cwdHashes.push(f.hash);
  16481. }
  16482. });
  16483. fm.trigger('cwdhasheschange', cwdHashes);
  16484. }
  16485. list && resize();
  16486. wrapper.trigger(scrollEvent);
  16487. })
  16488. .change(function(e) {
  16489. var phash = fm.cwd().hash,
  16490. sel = fm.selected(),
  16491. files, added;
  16492. if (query) {
  16493. $.each(e.data.changed || [], function(i, file) {
  16494. if (fm.cwdHash2Elm(file.hash).length) {
  16495. remove([file.hash]);
  16496. add([file], 'change');
  16497. $.inArray(file.hash, sel) !== -1 && selectFile(file.hash);
  16498. added = true;
  16499. }
  16500. });
  16501. } else {
  16502. $.each($.grep(e.data.changed || [], function(f) { return f.phash == phash ? true : false; }), function(i, file) {
  16503. if (fm.cwdHash2Elm(file.hash).length) {
  16504. remove([file.hash]);
  16505. add([file], 'change');
  16506. $.inArray(file.hash, sel) !== -1 && selectFile(file.hash);
  16507. added = true;
  16508. }
  16509. });
  16510. }
  16511. if (added) {
  16512. fm.trigger('cwdhasheschange', cwdHashes);
  16513. list && resize();
  16514. wrapper.trigger(scrollEvent);
  16515. }
  16516. trigger();
  16517. })
  16518. .remove(function(e) {
  16519. var place = list ? cwd.find('tbody') : cwd;
  16520. remove(e.data.removed || []);
  16521. trigger();
  16522. if (buffer.length < 1 && place.children(fileSelector + (options.oldSchool? ':not(.elfinder-cwd-parent)' : '')).length < 1) {
  16523. wz.addClass('elfinder-cwd-wrapper-empty');
  16524. selectCheckbox && selectAllCheckbox.find('input').prop('checked', false);
  16525. bottomMarker.hide();
  16526. wrapper.off(scrollEvent, render);
  16527. resize();
  16528. } else {
  16529. bottomMarkerShow(place);
  16530. wrapper.trigger(scrollEvent);
  16531. }
  16532. })
  16533. // select dragged file if no selected, disable selectable
  16534. .dragstart(function(e) {
  16535. var target = $(e.data.target),
  16536. oe = e.data.originalEvent;
  16537. if (target.hasClass(clFile)) {
  16538. if (!target.hasClass(clSelected)) {
  16539. !(oe.ctrlKey || oe.metaKey || oe.shiftKey) && unselectAll({ notrigger: true });
  16540. target.trigger(evtSelect);
  16541. trigger();
  16542. }
  16543. }
  16544. cwd.removeClass(clDisabled).data('selectable') && cwd.selectable('disable');
  16545. selectLock = true;
  16546. })
  16547. // enable selectable
  16548. .dragstop(function() {
  16549. cwd.data('selectable') && cwd.selectable('enable');
  16550. selectLock = false;
  16551. })
  16552. .bind('lockfiles unlockfiles selectfiles unselectfiles', function(e) {
  16553. var events = {
  16554. lockfiles : evtDisable ,
  16555. unlockfiles : evtEnable ,
  16556. selectfiles : evtSelect,
  16557. unselectfiles : evtUnselect },
  16558. event = events[e.type],
  16559. files = e.data.files || [],
  16560. l = files.length,
  16561. helper = e.data.helper || $(),
  16562. parents, ctr, add;
  16563. if (l > 0) {
  16564. parents = fm.parents(files[0]);
  16565. }
  16566. if (event === evtSelect || event === evtUnselect) {
  16567. add = (event === evtSelect),
  16568. $.each(files, function(i, hash) {
  16569. var all = cwd.hasClass('elfinder-cwd-allselected');
  16570. if (! selectedFiles[hash]) {
  16571. add && (selectedFiles[hash] = true);
  16572. } else {
  16573. if (all) {
  16574. selectCheckbox && selectAllCheckbox.children('input').prop('checked', false);
  16575. cwd.removeClass('elfinder-cwd-allselected');
  16576. all = false;
  16577. }
  16578. ! add && delete selectedFiles[hash];
  16579. }
  16580. });
  16581. }
  16582. if (!helper.data('locked')) {
  16583. while (l--) {
  16584. try {
  16585. fm.cwdHash2Elm(files[l]).trigger(event);
  16586. } catch(e) {}
  16587. }
  16588. ! e.data.inselect && trigger();
  16589. }
  16590. if (wrapper.data('dropover') && parents.indexOf(wrapper.data('dropover')) !== -1) {
  16591. ctr = e.type !== 'lockfiles';
  16592. helper.toggleClass('elfinder-drag-helper-plus', ctr);
  16593. wrapper.toggleClass(clDropActive, ctr);
  16594. }
  16595. })
  16596. // select new files after some actions
  16597. .bind('mkdir mkfile duplicate upload rename archive extract paste multiupload', function(e) {
  16598. if (e.type == 'upload' && e.data._multiupload) return;
  16599. var phash = fm.cwd().hash, files;
  16600. unselectAll({ notrigger: true });
  16601. $.each((e.data.added || []).concat(e.data.changed || []), function(i, file) {
  16602. file && file.phash == phash && selectFile(file.hash);
  16603. });
  16604. trigger();
  16605. })
  16606. .shortcut({
  16607. pattern :'ctrl+a',
  16608. description : 'selectall',
  16609. callback : selectAll
  16610. })
  16611. .shortcut({
  16612. pattern :'ctrl+shift+i',
  16613. description : 'selectinvert',
  16614. callback : selectInvert
  16615. })
  16616. .shortcut({
  16617. pattern : 'left right up down shift+left shift+right shift+up shift+down',
  16618. description : 'selectfiles',
  16619. type : 'keydown' , //fm.UA.Firefox || fm.UA.Opera ? 'keypress' : 'keydown',
  16620. callback : function(e) { select(e.keyCode, e.shiftKey); }
  16621. })
  16622. .shortcut({
  16623. pattern : 'home',
  16624. description : 'selectffile',
  16625. callback : function(e) {
  16626. unselectAll({ notrigger: true });
  16627. scrollToView(cwd.find('[id]:first').trigger(evtSelect));
  16628. trigger();
  16629. }
  16630. })
  16631. .shortcut({
  16632. pattern : 'end',
  16633. description : 'selectlfile',
  16634. callback : function(e) {
  16635. unselectAll({ notrigger: true });
  16636. scrollToView(cwd.find('[id]:last').trigger(evtSelect)) ;
  16637. trigger();
  16638. }
  16639. })
  16640. .shortcut({
  16641. pattern : 'page_up',
  16642. description : 'pageTurning',
  16643. callback : function(e) {
  16644. if (bufferExt.itemH) {
  16645. wrapper.scrollTop(
  16646. Math.round(
  16647. wrapper.scrollTop()
  16648. - (Math.floor((wrapper.height() + (list? bufferExt.itemH * -1 : 16)) / bufferExt.itemH)) * bufferExt.itemH
  16649. )
  16650. );
  16651. }
  16652. }
  16653. }).shortcut({
  16654. pattern : 'page_down',
  16655. description : 'pageTurning',
  16656. callback : function(e) {
  16657. if (bufferExt.itemH) {
  16658. wrapper.scrollTop(
  16659. Math.round(
  16660. wrapper.scrollTop()
  16661. + (Math.floor((wrapper.height() + (list? bufferExt.itemH * -1 : 16)) / bufferExt.itemH)) * bufferExt.itemH
  16662. )
  16663. );
  16664. }
  16665. }
  16666. });
  16667. });
  16668. // fm.timeEnd('cwdLoad')
  16669. return this;
  16670. };
  16671. /*
  16672. * File: /js/ui/dialog.js
  16673. */
  16674. /**
  16675. * @class elFinder dialog
  16676. *
  16677. * @author Dmitry (dio) Levashov
  16678. **/
  16679. $.fn.elfinderdialog = function(opts, fm) {
  16680. var platformWin = (window.navigator.platform.indexOf('Win') != -1),
  16681. delta = {},
  16682. syncSize = { enabled: false, width: false, height: false, defaultSize: null },
  16683. fitSize = function(dialog) {
  16684. var opts, node;
  16685. if (syncSize.enabled) {
  16686. node = fm.options.dialogContained? elfNode : $(window);
  16687. opts = {
  16688. maxWidth : syncSize.width? node.width() - delta.width : null,
  16689. maxHeight: syncSize.height? node.height() - delta.height : null
  16690. };
  16691. Object.assign(restoreStyle, opts);
  16692. dialog.css(opts).trigger('resize');
  16693. if (dialog.data('hasResizable') && (dialog.resizable('option', 'maxWidth') < opts.maxWidth || dialog.resizable('option', 'maxHeight') < opts.maxHeight)) {
  16694. dialog.resizable('option', opts);
  16695. }
  16696. }
  16697. },
  16698. syncFunc = function(e) {
  16699. var dialog = e.data;
  16700. syncTm && cancelAnimationFrame(syncTm);
  16701. syncTm = requestAnimationFrame(function() {
  16702. var opts, offset;
  16703. if (syncSize.enabled) {
  16704. fitSize(dialog);
  16705. }
  16706. });
  16707. },
  16708. checkEditing = function() {
  16709. var cldialog = 'elfinder-dialog',
  16710. dialogs = elfNode.children('.' + cldialog + '.' + fm.res('class', 'editing') + ':visible');
  16711. fm[dialogs.length? 'disable' : 'enable']();
  16712. },
  16713. propagationEvents = {},
  16714. syncTm, dialog, elfNode, restoreStyle;
  16715. if (fm && fm.ui) {
  16716. elfNode = fm.getUI();
  16717. } else {
  16718. elfNode = this.closest('.elfinder');
  16719. if (! fm) {
  16720. fm = elfNode.elfinder('instance');
  16721. }
  16722. }
  16723. if (typeof opts === 'string') {
  16724. if ((dialog = this.closest('.ui-dialog')).length) {
  16725. if (opts === 'open') {
  16726. if (dialog.css('display') === 'none') {
  16727. // Need dialog.show() and hide() to detect elements size in open() callbacks
  16728. dialog.trigger('posinit').show().trigger('open').hide();
  16729. dialog.fadeIn(120, function() {
  16730. fm.trigger('dialogopened', {dialog: dialog});
  16731. });
  16732. }
  16733. } else if (opts === 'close' || opts === 'destroy') {
  16734. dialog.stop(true);
  16735. if (dialog.is(':visible') || elfNode.is(':hidden')) {
  16736. dialog.trigger('close');
  16737. fm.trigger('dialogclosed', {dialog: dialog});
  16738. }
  16739. if (opts === 'destroy') {
  16740. dialog.remove();
  16741. fm.trigger('dialogremoved', {dialog: dialog});
  16742. } else if (dialog.data('minimized')) {
  16743. dialog.data('minimized').close();
  16744. }
  16745. } else if (opts === 'toTop') {
  16746. dialog.trigger('totop');
  16747. fm.trigger('dialogtotoped', {dialog: dialog});
  16748. } else if (opts === 'posInit') {
  16749. dialog.trigger('posinit');
  16750. fm.trigger('dialogposinited', {dialog: dialog});
  16751. } else if (opts === 'tabstopsInit') {
  16752. dialog.trigger('tabstopsInit');
  16753. fm.trigger('dialogtabstopsinited', {dialog: dialog});
  16754. } else if (opts === 'checkEditing') {
  16755. checkEditing();
  16756. }
  16757. }
  16758. return this;
  16759. }
  16760. opts = Object.assign({}, $.fn.elfinderdialog.defaults, opts);
  16761. if (opts.allowMinimize && opts.allowMinimize === 'auto') {
  16762. opts.allowMinimize = this.find('textarea,input').length? true : false;
  16763. }
  16764. opts.openMaximized = opts.allowMinimize && opts.openMaximized;
  16765. if (opts.headerBtnPos && opts.headerBtnPos === 'auto') {
  16766. opts.headerBtnPos = platformWin? 'right' : 'left';
  16767. }
  16768. if (opts.headerBtnOrder && opts.headerBtnOrder === 'auto') {
  16769. opts.headerBtnOrder = platformWin? 'close:maximize:minimize' : 'close:minimize:maximize';
  16770. }
  16771. if (opts.modal && opts.allowMinimize) {
  16772. opts.allowMinimize = false;
  16773. }
  16774. if (fm.options.dialogContained) {
  16775. syncSize.width = syncSize.height = syncSize.enabled = true;
  16776. } else {
  16777. syncSize.width = (opts.maxWidth === 'window');
  16778. syncSize.height = (opts.maxHeight === 'window');
  16779. if (syncSize.width || syncSize.height) {
  16780. syncSize.enabled = true;
  16781. }
  16782. }
  16783. propagationEvents = fm.arrayFlip(opts.propagationEvents, true);
  16784. this.filter(':not(.ui-dialog-content)').each(function() {
  16785. var self = $(this).addClass('ui-dialog-content ui-widget-content'),
  16786. clactive = 'elfinder-dialog-active',
  16787. cldialog = 'elfinder-dialog',
  16788. clnotify = 'elfinder-dialog-notify',
  16789. clhover = 'ui-state-hover',
  16790. cltabstop = 'elfinder-tabstop',
  16791. cl1stfocus = 'elfinder-focus',
  16792. clmodal = 'elfinder-dialog-modal',
  16793. id = parseInt(Math.random()*1000000),
  16794. titlebar = $('<div class="ui-dialog-titlebar ui-widget-header ui-corner-top ui-helper-clearfix"><span class="elfinder-dialog-title">'+opts.title+'</span></div>'),
  16795. buttonset = $('<div class="ui-dialog-buttonset"></div>'),
  16796. buttonpane = $('<div class=" ui-helper-clearfix ui-dialog-buttonpane ui-widget-content"></div>')
  16797. .append(buttonset),
  16798. btnWidth = 0,
  16799. btnCnt = 0,
  16800. tabstops = $(),
  16801. evCover = $('<div style="width:100%;height:100%;position:absolute;top:0px;left:0px;"></div>').hide(),
  16802. numberToTel = function() {
  16803. if (opts.optimizeNumber) {
  16804. dialog.find('input[type=number]').each(function() {
  16805. $(this).attr('inputmode', 'numeric');
  16806. $(this).attr('pattern', '[0-9]*');
  16807. });
  16808. }
  16809. },
  16810. tabstopsInit = function() {
  16811. tabstops = dialog.find('.'+cltabstop);
  16812. if (tabstops.length) {
  16813. tabstops.attr('tabindex', '-1');
  16814. if (! tabstops.filter('.'+cl1stfocus).length) {
  16815. buttonset.children('.'+cltabstop+':'+(platformWin? 'first' : 'last')).addClass(cl1stfocus);
  16816. }
  16817. }
  16818. },
  16819. tabstopNext = function(cur) {
  16820. var elms = tabstops.filter(':visible:enabled'),
  16821. node = cur? null : elms.filter('.'+cl1stfocus+':first');
  16822. if (! node || ! node.length) {
  16823. node = elms.first();
  16824. }
  16825. if (cur) {
  16826. $.each(elms, function(i, elm) {
  16827. if (elm === cur && elms[i+1]) {
  16828. node = elms.eq(i+1);
  16829. return false;
  16830. }
  16831. });
  16832. }
  16833. return node;
  16834. },
  16835. tabstopPrev = function(cur) {
  16836. var elms = tabstops.filter(':visible:enabled'),
  16837. node = elms.last();
  16838. $.each(elms, function(i, elm) {
  16839. if (elm === cur && elms[i-1]) {
  16840. node = elms.eq(i-1);
  16841. return false;
  16842. }
  16843. });
  16844. return node;
  16845. },
  16846. makeHeaderBtn = function() {
  16847. $.each(opts.headerBtnOrder.split(':').reverse(), function(i, v) {
  16848. headerBtns[v] && headerBtns[v]();
  16849. });
  16850. if (platformWin) {
  16851. titlebar.children('.elfinder-titlebar-button').addClass('elfinder-titlebar-button-right');
  16852. }
  16853. },
  16854. headerBtns = {
  16855. close: function() {
  16856. titlebar.prepend($('<span class="ui-widget-header ui-dialog-titlebar-close ui-corner-all elfinder-titlebar-button"><span class="ui-icon ui-icon-closethick"></span></span>')
  16857. .on('mousedown touchstart', function(e) {
  16858. e.preventDefault();
  16859. e.stopPropagation();
  16860. self.elfinderdialog('close');
  16861. })
  16862. );
  16863. },
  16864. maximize: function() {
  16865. if (opts.allowMaximize) {
  16866. dialog.on('resize', function(e, data) {
  16867. var full, elm;
  16868. e.preventDefault();
  16869. e.stopPropagation();
  16870. if (data && data.maximize) {
  16871. elm = titlebar.find('.elfinder-titlebar-full');
  16872. full = (data.maximize === 'on');
  16873. elm.children('span.ui-icon')
  16874. .toggleClass('ui-icon-plusthick', ! full)
  16875. .toggleClass('ui-icon-arrowreturnthick-1-s', full);
  16876. if (full) {
  16877. try {
  16878. dialog.hasClass('ui-draggable') && dialog.draggable('disable');
  16879. dialog.hasClass('ui-resizable') && dialog.resizable('disable');
  16880. } catch(e) {}
  16881. self.css('width', '100%').css('height', dialog.height() - dialog.children('.ui-dialog-titlebar').outerHeight(true) - buttonpane.outerHeight(true));
  16882. } else {
  16883. self.attr('style', elm.data('style'));
  16884. elm.removeData('style');
  16885. posCheck();
  16886. try {
  16887. dialog.hasClass('ui-draggable') && dialog.draggable('enable');
  16888. dialog.hasClass('ui-resizable') && dialog.resizable('enable');
  16889. } catch(e) {}
  16890. }
  16891. dialog.trigger('resize', {init: true});
  16892. }
  16893. });
  16894. titlebar.prepend($('<span class="ui-widget-header ui-corner-all elfinder-titlebar-button elfinder-titlebar-full"><span class="ui-icon ui-icon-plusthick"></span></span>')
  16895. .on('mousedown touchstart', function(e) {
  16896. var elm = $(this);
  16897. e.preventDefault();
  16898. e.stopPropagation();
  16899. if (!dialog.hasClass('elfinder-maximized') && typeof elm.data('style') === 'undefined') {
  16900. self.height(self.height());
  16901. elm.data('style', self.attr('style') || '');
  16902. }
  16903. fm.toggleMaximize(dialog);
  16904. typeof(opts.maximize) === 'function' && opts.maximize.call(self[0]);
  16905. })
  16906. );
  16907. }
  16908. },
  16909. minimize: function() {
  16910. var btn, mnode, doffset;
  16911. if (opts.allowMinimize) {
  16912. btn = $('<span class="ui-widget-header ui-corner-all elfinder-titlebar-button elfinder-titlebar-minimize"><span class="ui-icon ui-icon-minusthick"></span></span>')
  16913. .on('mousedown touchstart', function(e) {
  16914. var $this = $(this),
  16915. tray = fm.getUI('bottomtray'),
  16916. dumStyle = { width: 70, height: 24 },
  16917. dum = $('<div></div>').css(dumStyle).addClass(dialog.get(0).className + ' elfinder-dialog-minimized'),
  16918. close = function() {
  16919. mnode.remove();
  16920. dialog.removeData('minimized').show();
  16921. self.elfinderdialog('close');
  16922. },
  16923. pos = {};
  16924. e.preventDefault();
  16925. e.stopPropagation();
  16926. if (!dialog.data('minimized')) {
  16927. // minimize
  16928. doffset = dialog.data('minimized', {
  16929. dialog : function() { return mnode; },
  16930. show : function() { mnode.show(); },
  16931. hide : function() { mnode.hide(); },
  16932. close : close,
  16933. title : function(v) { mnode.children('.ui-dialog-titlebar').children('.elfinder-dialog-title').text(v); }
  16934. }).position();
  16935. mnode = dialog.clone().on('mousedown', function() {
  16936. $this.trigger('mousedown');
  16937. }).removeClass('ui-draggable ui-resizable elfinder-frontmost');
  16938. tray.append(dum);
  16939. Object.assign(pos, dum.offset(), dumStyle);
  16940. dum.remove();
  16941. mnode.height(dialog.height()).children('.ui-dialog-content:first').empty();
  16942. fm.toHide(dialog.before(mnode));
  16943. mnode.children('.ui-dialog-content:first,.ui-dialog-buttonpane,.ui-resizable-handle').remove();
  16944. mnode.find('.elfinder-titlebar-minimize,.elfinder-titlebar-full').remove();
  16945. mnode.find('.ui-dialog-titlebar-close').on('mousedown', function(e) {
  16946. e.stopPropagation();
  16947. e.preventDefault();
  16948. close();
  16949. });
  16950. mnode.animate(pos, function() {
  16951. mnode.attr('style', '')
  16952. .css({ maxWidth: dialog.width() })
  16953. .addClass('elfinder-dialog-minimized')
  16954. .appendTo(tray);
  16955. checkEditing();
  16956. typeof(opts.minimize) === 'function' && opts.minimize.call(self[0]);
  16957. });
  16958. } else {
  16959. //restore
  16960. dialog.removeData('minimized').before(mnode.css(Object.assign({'position': 'absolute'}, mnode.offset())));
  16961. fm.toFront(mnode);
  16962. mnode.animate(Object.assign({ width: dialog.width(), height: dialog.height() }, doffset), function() {
  16963. dialog.show();
  16964. fm.toFront(dialog);
  16965. mnode.remove();
  16966. posCheck();
  16967. checkEditing();
  16968. dialog.trigger('resize', {init: true});
  16969. typeof(opts.minimize) === 'function' && opts.minimize.call(self[0]);
  16970. });
  16971. }
  16972. });
  16973. titlebar.on('dblclick', function(e) {
  16974. $(this).children('.elfinder-titlebar-minimize').trigger('mousedown');
  16975. }).prepend(btn);
  16976. dialog.on('togleminimize', function() {
  16977. btn.trigger('mousedown');
  16978. });
  16979. }
  16980. }
  16981. },
  16982. dialog = $('<div class="ui-front ui-dialog ui-widget ui-widget-content ui-corner-all ui-draggable std42-dialog touch-punch '+cldialog+' '+opts.cssClass+'"></div>')
  16983. .hide()
  16984. .append(self)
  16985. .appendTo(elfNode)
  16986. .draggable({
  16987. containment : fm.options.dialogContained? elfNode : null,
  16988. handle : '.ui-dialog-titlebar',
  16989. start : function() {
  16990. evCover.show();
  16991. },
  16992. drag : function(e, ui) {
  16993. var top = ui.offset.top,
  16994. left = ui.offset.left;
  16995. if (top < 0) {
  16996. ui.position.top = ui.position.top - top;
  16997. }
  16998. if (left < 0) {
  16999. ui.position.left = ui.position.left - left;
  17000. }
  17001. if (fm.options.dialogContained) {
  17002. ui.position.top < 0 && (ui.position.top = 0);
  17003. ui.position.left < 0 && (ui.position.left = 0);
  17004. }
  17005. },
  17006. stop : function(e, ui) {
  17007. evCover.hide();
  17008. dialog.css({height : opts.height});
  17009. self.data('draged', true);
  17010. }
  17011. })
  17012. .css({
  17013. width : opts.width,
  17014. height : opts.height,
  17015. minWidth : opts.minWidth,
  17016. minHeight : opts.minHeight,
  17017. maxWidth : opts.maxWidth,
  17018. maxHeight : opts.maxHeight
  17019. })
  17020. .on('touchstart touchmove touchend click dblclick mouseup mouseenter mouseleave mouseout mouseover mousemove', function(e) {
  17021. // stopPropagation of user action events
  17022. !propagationEvents[e.type] && e.stopPropagation();
  17023. })
  17024. .on('mousedown', function(e) {
  17025. !propagationEvents[e.type] && e.stopPropagation();
  17026. requestAnimationFrame(function() {
  17027. if (dialog.is(':visible') && !dialog.hasClass('elfinder-frontmost')) {
  17028. toFocusNode = $(':focus');
  17029. if (!toFocusNode.length) {
  17030. toFocusNode = void(0);
  17031. }
  17032. dialog.trigger('totop');
  17033. }
  17034. });
  17035. })
  17036. .on('open', function() {
  17037. dialog.data('margin-y', self.outerHeight(true) - self.height());
  17038. if (syncSize.enabled) {
  17039. if (opts.height && opts.height !== 'auto') {
  17040. dialog.trigger('resize', {init: true});
  17041. }
  17042. if (!syncSize.defaultSize) {
  17043. syncSize.defaultSize = { width: self.width(), height: self.height() };
  17044. }
  17045. fitSize(dialog);
  17046. dialog.trigger('resize').trigger('posinit');
  17047. elfNode.on('resize.'+fm.namespace, dialog, syncFunc);
  17048. }
  17049. if (!dialog.hasClass(clnotify)) {
  17050. elfNode.children('.'+cldialog+':visible:not(.'+clnotify+')').each(function() {
  17051. var d = $(this),
  17052. top = parseInt(d.css('top')),
  17053. left = parseInt(d.css('left')),
  17054. _top = parseInt(dialog.css('top')),
  17055. _left = parseInt(dialog.css('left')),
  17056. ct = Math.abs(top - _top) < 10,
  17057. cl = Math.abs(left - _left) < 10;
  17058. if (d[0] != dialog[0] && (ct || cl)) {
  17059. dialog.css({
  17060. top : ct ? (top + 10) : _top,
  17061. left : cl ? (left + 10) : _left
  17062. });
  17063. }
  17064. });
  17065. }
  17066. if (dialog.data('modal')) {
  17067. dialog.addClass(clmodal);
  17068. fm.getUI('overlay').elfinderoverlay('show');
  17069. }
  17070. dialog.trigger('totop');
  17071. opts.openMaximized && fm.toggleMaximize(dialog);
  17072. fm.trigger('dialogopen', {dialog: dialog});
  17073. typeof(opts.open) == 'function' && $.proxy(opts.open, self[0])();
  17074. if (opts.closeOnEscape) {
  17075. $(document).on('keydown.'+id, function(e) {
  17076. if (e.keyCode == $.ui.keyCode.ESCAPE && dialog.hasClass('elfinder-frontmost')) {
  17077. self.elfinderdialog('close');
  17078. }
  17079. });
  17080. }
  17081. dialog.hasClass(fm.res('class', 'editing')) && checkEditing();
  17082. })
  17083. .on('close', function(e) {
  17084. var dialogs, dfd;
  17085. if (opts.beforeclose && typeof opts.beforeclose === 'function') {
  17086. dfd = opts.beforeclose();
  17087. if (!dfd || !dfd.promise) {
  17088. dfd = !dfd? $.Deferred().reject() : $.Deferred().resolve();
  17089. }
  17090. } else {
  17091. dfd = $.Deferred().resolve();
  17092. }
  17093. dfd.done(function() {
  17094. syncSize.enabled && elfNode.off('resize.'+fm.namespace, syncFunc);
  17095. if (opts.closeOnEscape) {
  17096. $(document).off('keyup.'+id);
  17097. }
  17098. if (opts.allowMaximize) {
  17099. fm.toggleMaximize(dialog, false);
  17100. }
  17101. fm.toHide(dialog);
  17102. dialog.data('modal') && fm.getUI('overlay').elfinderoverlay('hide');
  17103. if (typeof(opts.close) == 'function') {
  17104. $.proxy(opts.close, self[0])();
  17105. }
  17106. if (opts.destroyOnClose && dialog.parent().length) {
  17107. dialog.hide().remove();
  17108. }
  17109. // get focus to next dialog
  17110. dialogs = elfNode.children('.'+cldialog+':visible');
  17111. dialog.hasClass(fm.res('class', 'editing')) && checkEditing();
  17112. });
  17113. })
  17114. .on('totop frontmost', function() {
  17115. var s = fm.storage('autoFocusDialog');
  17116. dialog.data('focusOnMouseOver', s? (s > 0) : fm.options.uiOptions.dialog.focusOnMouseOver);
  17117. if (dialog.data('minimized')) {
  17118. titlebar.children('.elfinder-titlebar-minimize').trigger('mousedown');
  17119. }
  17120. if (!dialog.data('modal') && fm.getUI('overlay').is(':visible')) {
  17121. fm.getUI('overlay').before(dialog);
  17122. } else {
  17123. fm.toFront(dialog);
  17124. }
  17125. elfNode.children('.'+cldialog+':not(.'+clmodal+')').removeClass(clactive);
  17126. dialog.addClass(clactive);
  17127. ! fm.UA.Mobile && (toFocusNode || tabstopNext()).trigger('focus');
  17128. toFocusNode = void(0);
  17129. })
  17130. .on('posinit', function() {
  17131. var css = opts.position,
  17132. nodeOffset, minTop, minLeft, outerSize, win, winSize, nodeFull;
  17133. if (dialog.hasClass('elfinder-maximized')) {
  17134. return;
  17135. }
  17136. if (! css && ! dialog.data('resizing')) {
  17137. nodeFull = elfNode.hasClass('elfinder-fullscreen') || fm.options.enableAlways;
  17138. dialog.css(nodeFull? {
  17139. maxWidth : '100%',
  17140. maxHeight : '100%',
  17141. overflow : 'auto'
  17142. } : restoreStyle);
  17143. if (fm.UA.Mobile && !nodeFull && dialog.data('rotated') === fm.UA.Rotated) {
  17144. return;
  17145. }
  17146. dialog.data('rotated', fm.UA.Rotated);
  17147. win = $(window);
  17148. nodeOffset = elfNode.offset();
  17149. outerSize = {
  17150. width : dialog.outerWidth(true),
  17151. height: dialog.outerHeight(true)
  17152. };
  17153. outerSize.right = nodeOffset.left + outerSize.width;
  17154. outerSize.bottom = nodeOffset.top + outerSize.height;
  17155. winSize = {
  17156. scrLeft: win.scrollLeft(),
  17157. scrTop : win.scrollTop(),
  17158. width : win.width(),
  17159. height : win.height()
  17160. };
  17161. winSize.right = winSize.scrLeft + winSize.width;
  17162. winSize.bottom = winSize.scrTop + winSize.height;
  17163. if (fm.options.dialogContained || nodeFull) {
  17164. minTop = 0;
  17165. minLeft = 0;
  17166. } else {
  17167. minTop = nodeOffset.top * -1 + winSize.scrTop;
  17168. minLeft = nodeOffset.left * -1 + winSize.scrLeft;
  17169. }
  17170. css = {
  17171. top : outerSize.height >= winSize.height? minTop : Math.max(minTop, parseInt((elfNode.height() - outerSize.height)/2 - 42)),
  17172. left : outerSize.width >= winSize.width ? minLeft : Math.max(minLeft, parseInt((elfNode.width() - outerSize.width)/2))
  17173. };
  17174. if (outerSize.right + css.left > winSize.right) {
  17175. css.left = Math.max(minLeft, winSize.right - outerSize.right);
  17176. }
  17177. if (outerSize.bottom + css.top > winSize.bottom) {
  17178. css.top = Math.max(minTop, winSize.bottom - outerSize.bottom);
  17179. }
  17180. }
  17181. if (opts.absolute) {
  17182. css.position = 'absolute';
  17183. }
  17184. css && dialog.css(css);
  17185. })
  17186. .on('resize', function(e, data) {
  17187. var oh = 0, init = data && data.init, h, minH, maxH, autoH;
  17188. if ((data && (data.minimize || data.maxmize)) || dialog.data('minimized')) {
  17189. return;
  17190. }
  17191. e.stopPropagation();
  17192. e.preventDefault();
  17193. dialog.children('.ui-widget-header,.ui-dialog-buttonpane').each(function() {
  17194. oh += $(this).outerHeight(true);
  17195. });
  17196. autoH = (opts.height === 'auto')? true : false;
  17197. if (autoH) {
  17198. self.css({'max-height': '', 'height': 'auto'});
  17199. }
  17200. if (!init && syncSize.enabled && !e.originalEvent && !dialog.hasClass('elfinder-maximized')) {
  17201. h = dialog.height();
  17202. minH = dialog.css('min-height') || h;
  17203. maxH = dialog.css('max-height') || h;
  17204. if (minH.match(/%/)) {
  17205. minH = Math.floor((parseInt(minH) / 100) * dialog.parent().height());
  17206. } else {
  17207. minH = parseInt(minH);
  17208. }
  17209. if (maxH.match(/%/)) {
  17210. maxH = Math.floor((parseInt(maxH) / 100) * dialog.parent().height());
  17211. } else {
  17212. maxH = parseInt(maxH);
  17213. }
  17214. h = Math.min((autoH? dialog.height() : syncSize.defaultSize.height), Math.max(maxH, minH) - oh - dialog.data('margin-y'));
  17215. } else {
  17216. h = dialog.height() - oh - dialog.data('margin-y');
  17217. }
  17218. self.css(autoH? 'max-height' : 'height', h);
  17219. if (init) {
  17220. return;
  17221. }
  17222. posCheck();
  17223. minH = self.height();
  17224. minH = (h < minH)? (minH + oh + dialog.data('margin-y')) : opts.minHeight;
  17225. dialog.css('min-height', minH);
  17226. dialog.data('hasResizable') && dialog.resizable('option', { minHeight: minH });
  17227. if (typeof(opts.resize) === 'function') {
  17228. $.proxy(opts.resize, self[0])(e, data);
  17229. }
  17230. })
  17231. .on('tabstopsInit', tabstopsInit)
  17232. .on('focus', '.'+cltabstop, function() {
  17233. $(this).addClass(clhover).parent('label').addClass(clhover);
  17234. this.id && $(this).parent().find('label[for='+this.id+']').addClass(clhover);
  17235. })
  17236. .on('click', 'select.'+cltabstop, function() {
  17237. var node = $(this);
  17238. node.data('keepFocus')? node.removeData('keepFocus') : node.data('keepFocus', true);
  17239. })
  17240. .on('blur', '.'+cltabstop, function() {
  17241. $(this).removeClass(clhover).removeData('keepFocus').parent('label').removeClass(clhover);
  17242. this.id && $(this).parent().find('label[for='+this.id+']').removeClass(clhover);
  17243. })
  17244. .on('mouseenter mouseleave', '.'+cltabstop+',label', function(e) {
  17245. var $this = $(this), labelfor;
  17246. if (this.nodeName === 'LABEL') {
  17247. if (!$this.children('.'+cltabstop).length && (!(labelfor = $this.attr('for')) || !$('#'+labelfor).hasClass(cltabstop))) {
  17248. return;
  17249. }
  17250. }
  17251. if (opts.btnHoverFocus && dialog.data('focusOnMouseOver')) {
  17252. if (e.type === 'mouseenter' && ! $(':focus').data('keepFocus')) {
  17253. $this.trigger('focus');
  17254. }
  17255. } else {
  17256. $this.toggleClass(clhover, e.type == 'mouseenter');
  17257. }
  17258. })
  17259. .on('keydown', '.'+cltabstop, function(e) {
  17260. var $this = $(this),
  17261. esc, move, moveTo;
  17262. if ($this.is(':focus')) {
  17263. esc = e.keyCode === $.ui.keyCode.ESCAPE;
  17264. if (e.keyCode === $.ui.keyCode.ENTER) {
  17265. e.preventDefault();
  17266. $this.trigger('click');
  17267. } else if (((e.keyCode === $.ui.keyCode.TAB) && e.shiftKey) || e.keyCode === $.ui.keyCode.LEFT || e.keyCode == $.ui.keyCode.UP) {
  17268. move = 'prev';
  17269. } else if (e.keyCode === $.ui.keyCode.TAB || e.keyCode == $.ui.keyCode.RIGHT || e.keyCode == $.ui.keyCode.DOWN) {
  17270. move = 'next';
  17271. }
  17272. if (move
  17273. &&
  17274. (
  17275. ($this.is('textarea') && !(e.ctrlKey || e.metaKey))
  17276. ||
  17277. ($this.is('select,span.ui-slider-handle') && e.keyCode !== $.ui.keyCode.TAB)
  17278. ||
  17279. ($this.is('input:not(:checkbox,:radio)') && (!(e.ctrlKey || e.metaKey) && e.keyCode === $.ui.keyCode[move === 'prev'? 'LEFT':'RIGHT']))
  17280. )
  17281. ) {
  17282. e.stopPropagation();
  17283. return;
  17284. }
  17285. if (!esc) {
  17286. e.stopPropagation();
  17287. } else if ($this.is('input:not(:checkbox,:radio),textarea')) {
  17288. if ($this.val() !== '') {
  17289. $this.val('');
  17290. e.stopPropagation();
  17291. }
  17292. }
  17293. if (move) {
  17294. e.preventDefault();
  17295. (move === 'prev'? tabstopPrev : tabstopNext)(this).trigger('focus');
  17296. }
  17297. }
  17298. })
  17299. .data({modal: opts.modal}),
  17300. posCheck = function() {
  17301. var node = fm.getUI(),
  17302. pos;
  17303. if (node.hasClass('elfinder-fullscreen')) {
  17304. pos = dialog.position();
  17305. dialog.css('top', Math.max(Math.min(Math.max(pos.top, 0), node.height() - 100), 0));
  17306. dialog.css('left', Math.max(Math.min(Math.max(pos.left, 0), node.width() - 200), 0));
  17307. }
  17308. },
  17309. maxSize, toFocusNode;
  17310. dialog.prepend(titlebar);
  17311. makeHeaderBtn();
  17312. $.each(opts.buttons, function(name, cb) {
  17313. var button = $('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only '
  17314. +'elfinder-btncnt-'+(btnCnt++)+' '
  17315. +cltabstop
  17316. +'"><span class="ui-button-text">'+name+'</span></button>')
  17317. .on('click', $.proxy(cb, self[0]));
  17318. if (cb._cssClass) {
  17319. button.addClass(cb._cssClass);
  17320. }
  17321. if (platformWin) {
  17322. buttonset.append(button);
  17323. } else {
  17324. buttonset.prepend(button);
  17325. }
  17326. });
  17327. if (buttonset.children().length) {
  17328. dialog.append(buttonpane);
  17329. dialog.show();
  17330. buttonpane.find('button').each(function(i, btn) {
  17331. btnWidth += $(btn).outerWidth(true);
  17332. });
  17333. dialog.hide();
  17334. btnWidth += 20;
  17335. if (dialog.width() < btnWidth) {
  17336. dialog.width(btnWidth);
  17337. }
  17338. }
  17339. dialog.append(evCover);
  17340. if (syncSize.enabled) {
  17341. delta.width = dialog.outerWidth(true) - dialog.width() + ((dialog.outerWidth() - dialog.width()) / 2);
  17342. delta.height = dialog.outerHeight(true) - dialog.height() + ((dialog.outerHeight() - dialog.height()) / 2);
  17343. }
  17344. if (fm.options.dialogContained) {
  17345. maxSize = {
  17346. maxWidth: elfNode.width() - delta.width,
  17347. maxHeight: elfNode.height() - delta.height
  17348. };
  17349. opts.maxWidth = opts.maxWidth? Math.min(maxSize.maxWidth, opts.maxWidth) : maxSize.maxWidth;
  17350. opts.maxHeight = opts.maxHeight? Math.min(maxSize.maxHeight, opts.maxHeight) : maxSize.maxHeight;
  17351. dialog.css(maxSize);
  17352. }
  17353. restoreStyle = {
  17354. maxWidth : dialog.css('max-width'),
  17355. maxHeight : dialog.css('max-height'),
  17356. overflow : dialog.css('overflow')
  17357. };
  17358. if (opts.resizable) {
  17359. dialog.resizable({
  17360. minWidth : opts.minWidth,
  17361. minHeight : opts.minHeight,
  17362. maxWidth : opts.maxWidth,
  17363. maxHeight : opts.maxHeight,
  17364. start : function() {
  17365. evCover.show();
  17366. if (dialog.data('resizing') !== true && dialog.data('resizing')) {
  17367. clearTimeout(dialog.data('resizing'));
  17368. }
  17369. dialog.data('resizing', true);
  17370. },
  17371. stop : function(e, ui) {
  17372. evCover.hide();
  17373. dialog.data('resizing', setTimeout(function() {
  17374. dialog.data('resizing', false);
  17375. }, 200));
  17376. if (syncSize.enabled) {
  17377. syncSize.defaultSize = { width: self.width(), height: self.height() };
  17378. }
  17379. }
  17380. }).data('hasResizable', true);
  17381. }
  17382. numberToTel();
  17383. tabstopsInit();
  17384. typeof(opts.create) == 'function' && $.proxy(opts.create, this)();
  17385. if (opts.autoOpen) {
  17386. if (opts.open) {
  17387. requestAnimationFrame(function() {
  17388. self.elfinderdialog('open');
  17389. });
  17390. } else {
  17391. self.elfinderdialog('open');
  17392. }
  17393. }
  17394. if (opts.resize) {
  17395. fm.bind('themechange', function() {
  17396. setTimeout(function() {
  17397. dialog.data('margin-y', self.outerHeight(true) - self.height());
  17398. dialog.trigger('resize', {init: true});
  17399. }, 300);
  17400. });
  17401. }
  17402. });
  17403. return this;
  17404. };
  17405. $.fn.elfinderdialog.defaults = {
  17406. cssClass : '',
  17407. title : '',
  17408. modal : false,
  17409. resizable : true,
  17410. autoOpen : true,
  17411. closeOnEscape : true,
  17412. destroyOnClose : false,
  17413. buttons : {},
  17414. btnHoverFocus : true,
  17415. position : null,
  17416. absolute : false,
  17417. width : 320,
  17418. height : 'auto',
  17419. minWidth : 200,
  17420. minHeight : 70,
  17421. maxWidth : null,
  17422. maxHeight : null,
  17423. allowMinimize : 'auto',
  17424. allowMaximize : false,
  17425. openMaximized : false,
  17426. headerBtnPos : 'auto',
  17427. headerBtnOrder : 'auto',
  17428. optimizeNumber : true,
  17429. propagationEvents : ['mousemove', 'mouseup']
  17430. };
  17431. /*
  17432. * File: /js/ui/fullscreenbutton.js
  17433. */
  17434. /**
  17435. * @class elFinder toolbar button to switch full scrren mode.
  17436. *
  17437. * @author Naoki Sawada
  17438. **/
  17439. $.fn.elfinderfullscreenbutton = function(cmd) {
  17440. return this.each(function() {
  17441. var button = $(this).elfinderbutton(cmd),
  17442. icon = button.children('.elfinder-button-icon'),
  17443. tm;
  17444. cmd.change(function() {
  17445. tm && cancelAnimationFrame(tm);
  17446. tm = requestAnimationFrame(function() {
  17447. var fullscreen = cmd.value;
  17448. icon.addClass('elfinder-button-icon-fullscreen').toggleClass('elfinder-button-icon-unfullscreen', fullscreen);
  17449. cmd.className = fullscreen? 'unfullscreen' : '';
  17450. });
  17451. });
  17452. });
  17453. };
  17454. /*
  17455. * File: /js/ui/navbar.js
  17456. */
  17457. /**
  17458. * @class elfindernav - elFinder container for diretories tree and places
  17459. *
  17460. * @author Dmitry (dio) Levashov
  17461. **/
  17462. $.fn.elfindernavbar = function(fm, opts) {
  17463. this.not('.elfinder-navbar').each(function() {
  17464. var nav = $(this).hide().addClass('ui-state-default elfinder-navbar'),
  17465. parent = nav.css('overflow', 'hidden').parent(),
  17466. wz = parent.children('.elfinder-workzone').append(nav),
  17467. ltr = fm.direction == 'ltr',
  17468. delta, deltaW, handle, swipeHandle, autoHide, setWidth, navdock,
  17469. setWzRect = function() {
  17470. var cwd = fm.getUI('cwd'),
  17471. wz = fm.getUI('workzone'),
  17472. wzRect = wz.data('rectangle'),
  17473. cwdOffset = cwd.offset();
  17474. wz.data('rectangle', Object.assign(wzRect, { cwdEdge: (fm.direction === 'ltr')? cwdOffset.left : cwdOffset.left + cwd.width() }));
  17475. },
  17476. setDelta = function() {
  17477. nav.css('overflow', 'hidden');
  17478. delta = Math.round(nav.outerHeight() - nav.height());
  17479. deltaW = Math.round(navdock.outerWidth() - navdock.innerWidth());
  17480. nav.css('overflow', 'auto');
  17481. };
  17482. fm.one('init', function() {
  17483. navdock = fm.getUI('navdock');
  17484. var set = function() {
  17485. setDelta();
  17486. fm.bind('wzresize', function() {
  17487. var navdockH = 0;
  17488. navdock.width(nav.outerWidth() - deltaW);
  17489. if (navdock.children().length > 1) {
  17490. navdockH = navdock.outerHeight(true);
  17491. }
  17492. nav.height(wz.height() - navdockH - delta);
  17493. }).trigger('wzresize');
  17494. };
  17495. if (fm.cssloaded) {
  17496. set();
  17497. } else {
  17498. fm.one('cssloaded', set);
  17499. }
  17500. })
  17501. .one('opendone',function() {
  17502. handle && handle.trigger('resize');
  17503. nav.css('overflow', 'auto');
  17504. }).bind('themechange', setDelta);
  17505. if (fm.UA.Touch) {
  17506. autoHide = fm.storage('autoHide') || {};
  17507. if (typeof autoHide.navbar === 'undefined') {
  17508. autoHide.navbar = (opts.autoHideUA && opts.autoHideUA.length > 0 && $.grep(opts.autoHideUA, function(v){ return fm.UA[v]? true : false; }).length);
  17509. fm.storage('autoHide', autoHide);
  17510. }
  17511. if (autoHide.navbar) {
  17512. fm.one('init', function() {
  17513. if (nav.children().length) {
  17514. fm.uiAutoHide.push(function(){ nav.stop(true, true).trigger('navhide', { duration: 'slow', init: true }); });
  17515. }
  17516. });
  17517. }
  17518. fm.bind('load', function() {
  17519. if (nav.children().length) {
  17520. swipeHandle = $('<div class="elfinder-navbar-swipe-handle"></div>').hide().appendTo(wz);
  17521. if (swipeHandle.css('pointer-events') !== 'none') {
  17522. swipeHandle.remove();
  17523. swipeHandle = null;
  17524. }
  17525. }
  17526. });
  17527. nav.on('navshow navhide', function(e, data) {
  17528. var mode = (e.type === 'navshow')? 'show' : 'hide',
  17529. duration = (data && data.duration)? data.duration : 'fast',
  17530. handleW = (data && data.handleW)? data.handleW : Math.max(50, fm.getUI().width() / 10);
  17531. nav.stop(true, true)[mode]({
  17532. duration: duration,
  17533. step : function() {
  17534. fm.trigger('wzresize');
  17535. },
  17536. complete: function() {
  17537. if (swipeHandle) {
  17538. if (mode === 'show') {
  17539. swipeHandle.stop(true, true).hide();
  17540. } else {
  17541. swipeHandle.width(handleW? handleW : '');
  17542. fm.resources.blink(swipeHandle, 'slowonce');
  17543. }
  17544. }
  17545. fm.trigger('navbar'+ mode);
  17546. data.init && fm.trigger('uiautohide');
  17547. setWzRect();
  17548. }
  17549. });
  17550. autoHide.navbar = (mode !== 'show');
  17551. fm.storage('autoHide', Object.assign(fm.storage('autoHide'), {navbar: autoHide.navbar}));
  17552. }).on('touchstart', function(e) {
  17553. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  17554. e.originalEvent._preventSwipeX = true;
  17555. }
  17556. });
  17557. }
  17558. if (! fm.UA.Mobile) {
  17559. handle = nav.resizable({
  17560. handles : ltr ? 'e' : 'w',
  17561. minWidth : opts.minWidth || 150,
  17562. maxWidth : opts.maxWidth || 500,
  17563. resize : function() {
  17564. fm.trigger('wzresize');
  17565. },
  17566. stop : function(e, ui) {
  17567. fm.storage('navbarWidth', ui.size.width);
  17568. setWzRect();
  17569. }
  17570. })
  17571. .on('resize scroll', function(e) {
  17572. var $this = $(this),
  17573. tm = $this.data('posinit');
  17574. e.preventDefault();
  17575. e.stopPropagation();
  17576. if (! ltr && e.type === 'resize') {
  17577. nav.css('left', 0);
  17578. }
  17579. tm && cancelAnimationFrame(tm);
  17580. $this.data('posinit', requestAnimationFrame(function() {
  17581. var offset = (fm.UA.Opera && nav.scrollLeft())? 20 : 2;
  17582. handle.css('top', 0).css({
  17583. top : parseInt(nav.scrollTop())+'px',
  17584. left : ltr ? 'auto' : parseInt(nav.scrollRight() - offset) * -1,
  17585. right: ltr ? parseInt(nav.scrollLeft() - offset) * -1 : 'auto'
  17586. });
  17587. if (e.type === 'resize') {
  17588. fm.getUI('cwd').trigger('resize');
  17589. }
  17590. }));
  17591. })
  17592. .children('.ui-resizable-handle').addClass('ui-front');
  17593. }
  17594. if (setWidth = fm.storage('navbarWidth')) {
  17595. nav.width(setWidth);
  17596. } else {
  17597. if (fm.UA.Mobile) {
  17598. fm.one(fm.cssloaded? 'init' : 'cssloaded', function() {
  17599. var set = function() {
  17600. setWidth = nav.parent().width() / 2;
  17601. if (nav.data('defWidth') > setWidth) {
  17602. nav.width(setWidth);
  17603. } else {
  17604. nav.width(nav.data('defWidth'));
  17605. }
  17606. nav.data('width', nav.width());
  17607. fm.trigger('wzresize');
  17608. };
  17609. nav.data('defWidth', nav.width());
  17610. $(window).on('resize.' + fm.namespace, set);
  17611. set();
  17612. });
  17613. }
  17614. }
  17615. });
  17616. return this;
  17617. };
  17618. /*
  17619. * File: /js/ui/navdock.js
  17620. */
  17621. /**
  17622. * @class elfindernavdock - elFinder container for preview etc at below the navbar
  17623. *
  17624. * @author Naoki Sawada
  17625. **/
  17626. $.fn.elfindernavdock = function(fm, opts) {
  17627. this.not('.elfinder-navdock').each(function() {
  17628. var self = $(this).hide().addClass('ui-state-default elfinder-navdock touch-punch'),
  17629. node = self.parent(),
  17630. wz = node.children('.elfinder-workzone').append(self),
  17631. resize = function(to, h) {
  17632. var curH = h || self.height(),
  17633. diff = to - curH,
  17634. len = Object.keys(sizeSyncs).length,
  17635. calc = len? diff / len : 0,
  17636. ovf;
  17637. if (diff) {
  17638. ovf = self.css('overflow');
  17639. self.css('overflow', 'hidden');
  17640. self.height(to);
  17641. $.each(sizeSyncs, function(id, n) {
  17642. n.height(n.height() + calc).trigger('resize.' + fm.namespace);
  17643. });
  17644. fm.trigger('wzresize');
  17645. self.css('overflow', ovf);
  17646. }
  17647. },
  17648. handle = $('<div class="ui-front ui-resizable-handle ui-resizable-n"></div>').appendTo(self),
  17649. sizeSyncs = {},
  17650. resizeFn = [],
  17651. initMaxHeight = (parseInt(opts.initMaxHeight) || 50) / 100,
  17652. maxHeight = (parseInt(opts.maxHeight) || 90) / 100,
  17653. basicHeight, hasNode;
  17654. self.data('addNode', function(cNode, opts) {
  17655. var wzH = fm.getUI('workzone').height(),
  17656. imaxH = wzH * initMaxHeight,
  17657. curH, tH, mH;
  17658. opts = Object.assign({
  17659. first: false,
  17660. sizeSync: true,
  17661. init: false
  17662. }, opts);
  17663. if (!cNode.attr('id')) {
  17664. cNode.attr('id', fm.namespace+'-navdock-' + (+new Date()));
  17665. }
  17666. opts.sizeSync && (sizeSyncs[cNode.attr('id')] = cNode);
  17667. curH = self.height();
  17668. tH = curH + cNode.outerHeight(true);
  17669. if (opts.first) {
  17670. handle.after(cNode);
  17671. } else {
  17672. self.append(cNode);
  17673. }
  17674. hasNode = true;
  17675. self.resizable('enable').height(tH).show();
  17676. fm.trigger('wzresize');
  17677. if (opts.init) {
  17678. mH = fm.storage('navdockHeight');
  17679. if (mH) {
  17680. tH = mH;
  17681. } else {
  17682. tH = tH > imaxH? imaxH : tH;
  17683. }
  17684. basicHeight = tH;
  17685. }
  17686. resize(Math.min(tH, wzH * maxHeight));
  17687. return self;
  17688. }).data('removeNode', function(nodeId, appendTo) {
  17689. var cNode = $('#'+nodeId);
  17690. delete sizeSyncs[nodeId];
  17691. self.height(self.height() - $('#'+nodeId).outerHeight(true));
  17692. if (appendTo) {
  17693. if (appendTo === 'detach') {
  17694. cNode = cNode.detach();
  17695. } else {
  17696. appendTo.append(cNode);
  17697. }
  17698. } else {
  17699. cNode.remove();
  17700. }
  17701. if (self.children().length <= 1) {
  17702. hasNode = false;
  17703. self.resizable('disable').height(0).hide();
  17704. }
  17705. fm.trigger('wzresize');
  17706. return cNode;
  17707. });
  17708. if (! opts.disabled) {
  17709. fm.one('init', function() {
  17710. var ovf;
  17711. if (fm.getUI('navbar').children().not('.ui-resizable-handle').length) {
  17712. self.data('dockEnabled', true);
  17713. self.resizable({
  17714. maxHeight: fm.getUI('workzone').height() * maxHeight,
  17715. handles: { n: handle },
  17716. start: function(e, ui) {
  17717. ovf = self.css('overflow');
  17718. self.css('overflow', 'hidden');
  17719. fm.trigger('navdockresizestart', {event: e, ui: ui}, true);
  17720. },
  17721. resize: function(e, ui) {
  17722. self.css('top', '');
  17723. fm.trigger('wzresize', { inNavdockResize : true });
  17724. },
  17725. stop: function(e, ui) {
  17726. fm.trigger('navdockresizestop', {event: e, ui: ui}, true);
  17727. self.css('top', '');
  17728. basicHeight = ui.size.height;
  17729. fm.storage('navdockHeight', basicHeight);
  17730. resize(basicHeight, ui.originalSize.height);
  17731. self.css('overflow', ovf);
  17732. }
  17733. });
  17734. fm.bind('wzresize', function(e) {
  17735. var minH, maxH, h;
  17736. if (self.is(':visible')) {
  17737. maxH = fm.getUI('workzone').height() * maxHeight;
  17738. if (! e.data || ! e.data.inNavdockResize) {
  17739. h = self.height();
  17740. if (maxH < basicHeight) {
  17741. if (Math.abs(h - maxH) > 1) {
  17742. resize(maxH);
  17743. }
  17744. } else {
  17745. if (Math.abs(h - basicHeight) > 1) {
  17746. resize(basicHeight);
  17747. }
  17748. }
  17749. }
  17750. self.resizable('option', 'maxHeight', maxH);
  17751. }
  17752. }).bind('themechange', function() {
  17753. var oldH = Math.round(self.height());
  17754. requestAnimationFrame(function() {
  17755. var curH = Math.round(self.height()),
  17756. diff = oldH - curH;
  17757. if (diff !== 0) {
  17758. resize(self.height(), curH - diff);
  17759. }
  17760. });
  17761. });
  17762. }
  17763. fm.bind('navbarshow navbarhide', function(e) {
  17764. self[hasNode && e.type === 'navbarshow'? 'show' : 'hide']();
  17765. });
  17766. });
  17767. }
  17768. });
  17769. return this;
  17770. };
  17771. /*
  17772. * File: /js/ui/overlay.js
  17773. */
  17774. $.fn.elfinderoverlay = function(opts) {
  17775. var fm = this.parent().elfinder('instance'),
  17776. o, cnt, show, hide;
  17777. this.filter(':not(.elfinder-overlay)').each(function() {
  17778. opts = Object.assign({}, opts);
  17779. $(this).addClass('ui-front ui-widget-overlay elfinder-overlay')
  17780. .hide()
  17781. .on('mousedown', function(e) {
  17782. e.preventDefault();
  17783. e.stopPropagation();
  17784. })
  17785. .data({
  17786. cnt : 0,
  17787. show : typeof(opts.show) == 'function' ? opts.show : function() { },
  17788. hide : typeof(opts.hide) == 'function' ? opts.hide : function() { }
  17789. });
  17790. });
  17791. if (opts == 'show') {
  17792. o = this.eq(0);
  17793. cnt = o.data('cnt') + 1;
  17794. show = o.data('show');
  17795. fm.toFront(o);
  17796. o.data('cnt', cnt);
  17797. if (o.is(':hidden')) {
  17798. o.show();
  17799. show();
  17800. }
  17801. }
  17802. if (opts == 'hide') {
  17803. o = this.eq(0);
  17804. cnt = o.data('cnt') - 1;
  17805. hide = o.data('hide');
  17806. o.data('cnt', cnt);
  17807. if (cnt <= 0) {
  17808. o.hide();
  17809. hide();
  17810. }
  17811. }
  17812. return this;
  17813. };
  17814. /*
  17815. * File: /js/ui/panel.js
  17816. */
  17817. $.fn.elfinderpanel = function(fm) {
  17818. return this.each(function() {
  17819. var panel = $(this).addClass('elfinder-panel ui-state-default ui-corner-all'),
  17820. margin = 'margin-'+(fm.direction == 'ltr' ? 'left' : 'right');
  17821. fm.one('load', function(e) {
  17822. var navbar = fm.getUI('navbar');
  17823. panel.css(margin, parseInt(navbar.outerWidth(true)));
  17824. navbar.on('resize', function(e) {
  17825. e.preventDefault();
  17826. e.stopPropagation();
  17827. panel.is(':visible') && panel.css(margin, parseInt(navbar.outerWidth(true)));
  17828. });
  17829. });
  17830. });
  17831. };
  17832. /*
  17833. * File: /js/ui/path.js
  17834. */
  17835. /**
  17836. * @class elFinder ui
  17837. * Display current folder path in statusbar.
  17838. * Click on folder name in path - open folder
  17839. *
  17840. * @author Dmitry (dio) Levashov
  17841. **/
  17842. $.fn.elfinderpath = function(fm, options) {
  17843. return this.each(function() {
  17844. var query = '',
  17845. target = '',
  17846. mimes = [],
  17847. place = 'statusbar',
  17848. clHover= fm.res('class', 'hover'),
  17849. prefix = 'path' + (elFinder.prototype.uniqueid? elFinder.prototype.uniqueid : '') + '-',
  17850. wzbase = $('<div class="ui-widget-header ui-helper-clearfix elfinder-workzone-path"></div>'),
  17851. path = $(this).addClass('elfinder-path').html('&nbsp;')
  17852. .on('mousedown', 'span.elfinder-path-dir', function(e) {
  17853. var hash = $(this).attr('id').substr(prefix.length);
  17854. e.preventDefault();
  17855. if (hash != fm.cwd().hash) {
  17856. $(this).addClass(clHover);
  17857. if (query) {
  17858. fm.exec('search', query, { target: hash, mime: mimes.join(' ') });
  17859. } else {
  17860. fm.trigger('select', {selected : [hash]}).exec('open', hash);
  17861. }
  17862. }
  17863. })
  17864. .prependTo(fm.getUI('statusbar').show()),
  17865. roots = $('<div class="elfinder-path-roots"></div>').on('click', function(e) {
  17866. e.stopPropagation();
  17867. e.preventDefault();
  17868. var roots = $.map(fm.roots, function(h) { return fm.file(h); }),
  17869. raw = [];
  17870. $.each(roots, function(i, f) {
  17871. if (! f.phash && fm.root(fm.cwd().hash, true) !== f.hash) {
  17872. raw.push({
  17873. label : fm.escape(f.i18 || f.name),
  17874. icon : 'home',
  17875. callback : function() { fm.exec('open', f.hash); },
  17876. options : {
  17877. iconClass : f.csscls || '',
  17878. iconImg : f.icon || ''
  17879. }
  17880. });
  17881. }
  17882. });
  17883. fm.trigger('contextmenu', {
  17884. raw: raw,
  17885. x: e.pageX,
  17886. y: e.pageY
  17887. });
  17888. }).append('<span class="elfinder-button-icon elfinder-button-icon-menu" ></span>').appendTo(wzbase),
  17889. render = function(cwd) {
  17890. var dirs = [],
  17891. names = [];
  17892. $.each(fm.parents(cwd), function(i, hash) {
  17893. var c = (cwd === hash)? 'elfinder-path-dir elfinder-path-cwd' : 'elfinder-path-dir',
  17894. f = fm.file(hash),
  17895. name = fm.escape(f.i18 || f.name);
  17896. names.push(name);
  17897. dirs.push('<span id="'+prefix+hash+'" class="'+c+'" title="'+names.join(fm.option('separator'))+'">'+name+'</span>');
  17898. });
  17899. return dirs.join('<span class="elfinder-path-other">'+fm.option('separator')+'</span>');
  17900. },
  17901. toWorkzone = function() {
  17902. var prev;
  17903. path.children('span.elfinder-path-dir').attr('style', '');
  17904. prev = fm.direction === 'ltr'? $('#'+prefix + fm.cwd().hash).prevAll('span.elfinder-path-dir:first') : $();
  17905. path.scrollLeft(prev.length? prev.position().left : 0);
  17906. },
  17907. fit = function() {
  17908. if (fm.UA.CSS.flex) {
  17909. return;
  17910. }
  17911. var dirs = path.children('span.elfinder-path-dir'),
  17912. cnt = dirs.length,
  17913. m, bg = 0, ids;
  17914. if (place === 'workzone' || cnt < 2) {
  17915. dirs.attr('style', '');
  17916. return;
  17917. }
  17918. path.width(path.css('max-width'));
  17919. dirs.css({maxWidth: (100/cnt)+'%', display: 'inline-block'});
  17920. m = path.width() - 9;
  17921. path.children('span.elfinder-path-other').each(function() {
  17922. m -= $(this).width();
  17923. });
  17924. ids = [];
  17925. dirs.each(function(i) {
  17926. var dir = $(this),
  17927. w = dir.width();
  17928. m -= w;
  17929. if (w < this.scrollWidth) {
  17930. ids.push(i);
  17931. }
  17932. });
  17933. path.width('');
  17934. if (ids.length) {
  17935. if (m > 0) {
  17936. m = m / ids.length;
  17937. $.each(ids, function(i, k) {
  17938. var d = $(dirs[k]);
  17939. d.css('max-width', d.width() + m);
  17940. });
  17941. }
  17942. dirs.last().attr('style', '');
  17943. } else {
  17944. dirs.attr('style', '');
  17945. }
  17946. },
  17947. hasUiTree, hasUiStat;
  17948. fm.one('init', function() {
  17949. hasUiTree = fm.getUI('tree').length;
  17950. hasUiStat = fm.getUI('stat').length;
  17951. if (! hasUiTree && options.toWorkzoneWithoutNavbar) {
  17952. wzbase.append(path).insertBefore(fm.getUI('workzone'));
  17953. place = 'workzone';
  17954. fm.bind('open', toWorkzone)
  17955. .one('opendone', function() {
  17956. fm.getUI().trigger('resize');
  17957. });
  17958. }
  17959. })
  17960. .bind('open searchend parents', function() {
  17961. var dirs = [];
  17962. query = '';
  17963. target = '';
  17964. mimes = [];
  17965. path.html(render(fm.cwd().hash));
  17966. if (Object.keys(fm.roots).length > 1) {
  17967. path.css('margin', '');
  17968. roots.show();
  17969. } else {
  17970. path.css('margin', 0);
  17971. roots.hide();
  17972. }
  17973. !hasUiStat && fit();
  17974. })
  17975. .bind('searchstart', function(e) {
  17976. if (e.data) {
  17977. query = e.data.query || '';
  17978. target = e.data.target || '';
  17979. mimes = e.data.mimes || [];
  17980. }
  17981. })
  17982. .bind('search', function(e) {
  17983. var dirs = [],
  17984. html = '';
  17985. if (target) {
  17986. html = render(target);
  17987. } else {
  17988. html = fm.i18n('btnAll');
  17989. }
  17990. path.html('<span class="elfinder-path-other">'+fm.i18n('searcresult') + ': </span>' + html);
  17991. fit();
  17992. })
  17993. // on swipe to navbar show/hide
  17994. .bind('navbarshow navbarhide', function() {
  17995. var wz = fm.getUI('workzone');
  17996. if (this.type === 'navbarshow') {
  17997. fm.unbind('open', toWorkzone);
  17998. path.prependTo(fm.getUI('statusbar'));
  17999. wzbase.detach();
  18000. place = 'statusbar';
  18001. } else {
  18002. wzbase.append(path).insertBefore(wz);
  18003. place = 'workzone';
  18004. toWorkzone();
  18005. fm.bind('open', toWorkzone);
  18006. }
  18007. fm.trigger('uiresize');
  18008. })
  18009. .bind('resize uistatchange', fit);
  18010. });
  18011. };
  18012. /*
  18013. * File: /js/ui/places.js
  18014. */
  18015. /**
  18016. * @class elFinder places/favorites ui
  18017. *
  18018. * @author Dmitry (dio) Levashov
  18019. * @author Naoki Sawada
  18020. **/
  18021. $.fn.elfinderplaces = function(fm, opts) {
  18022. return this.each(function() {
  18023. var dirs = {},
  18024. c = 'class',
  18025. navdir = fm.res(c, 'navdir'),
  18026. collapsed = fm.res(c, 'navcollapse'),
  18027. expanded = fm.res(c, 'navexpand'),
  18028. hover = fm.res(c, 'hover'),
  18029. clroot = fm.res(c, 'treeroot'),
  18030. dropover = fm.res(c, 'adroppable'),
  18031. tpl = fm.res('tpl', 'placedir'),
  18032. ptpl = fm.res('tpl', 'perms'),
  18033. spinner = $(fm.res('tpl', 'navspinner')),
  18034. suffix = opts.suffix? opts.suffix : '',
  18035. key = 'places' + suffix,
  18036. menuTimer = null,
  18037. /**
  18038. * Convert places dir node into dir hash
  18039. *
  18040. * @param String directory id
  18041. * @return String
  18042. **/
  18043. id2hash = function(id) { return id.substr(6); },
  18044. /**
  18045. * Convert places dir hash into dir node id
  18046. *
  18047. * @param String directory id
  18048. * @return String
  18049. **/
  18050. hash2id = function(hash) { return 'place-'+hash; },
  18051. /**
  18052. * Convert places dir hash into dir node elment (jQuery object)
  18053. *
  18054. * @param String directory id
  18055. * @return Object
  18056. **/
  18057. hash2elm = function(hash) { return $(document.getElementById(hash2id(hash))); },
  18058. /**
  18059. * Save current places state
  18060. *
  18061. * @return void
  18062. **/
  18063. save = function() {
  18064. var hashes = [], data = {};
  18065. hashes = $.map(subtree.children().find('[id]'), function(n) {
  18066. return id2hash(n.id);
  18067. });
  18068. if (hashes.length) {
  18069. $.each(hashes.reverse(), function(i, h) {
  18070. data[h] = dirs[h];
  18071. });
  18072. } else {
  18073. data = null;
  18074. }
  18075. fm.storage(key, data);
  18076. },
  18077. /**
  18078. * Init dir at places
  18079. *
  18080. * @return void
  18081. **/
  18082. init = function() {
  18083. var dat, hashes;
  18084. key = 'places'+(opts.suffix? opts.suffix : ''),
  18085. dirs = {};
  18086. dat = fm.storage(key);
  18087. if (typeof dat === 'string') {
  18088. // old data type elFinder <= 2.1.12
  18089. dat = $.grep(dat.split(','), function(hash) { return hash? true : false;});
  18090. $.each(dat, function(i, d) {
  18091. var dir = d.split('#');
  18092. dirs[dir[0]] = dir[1]? dir[1] : dir[0];
  18093. });
  18094. } else if ($.isPlainObject(dat)) {
  18095. dirs = dat;
  18096. }
  18097. // allow modify `dirs`
  18098. /**
  18099. * example for preset places
  18100. *
  18101. * elfinderInstance.bind('placesload', function(e, fm) {
  18102. * //if (fm.storage(e.data.storageKey) === null) { // for first time only
  18103. * if (!fm.storage(e.data.storageKey)) { // for empty places
  18104. * e.data.dirs[targetHash] = fallbackName; // preset folder
  18105. * }
  18106. * }
  18107. **/
  18108. fm.trigger('placesload', {dirs: dirs, storageKey: key}, true);
  18109. hashes = Object.keys(dirs);
  18110. if (hashes.length) {
  18111. root.prepend(spinner);
  18112. fm.request({
  18113. data : {cmd : 'info', targets : hashes},
  18114. preventDefault : true
  18115. })
  18116. .done(function(data) {
  18117. var exists = {};
  18118. data.files && data.files.length && fm.cache(data.files);
  18119. $.each(data.files, function(i, f) {
  18120. var hash = f.hash;
  18121. exists[hash] = f;
  18122. });
  18123. $.each(dirs, function(h, f) {
  18124. add(exists[h] || Object.assign({notfound: true}, f));
  18125. });
  18126. if (fm.storage('placesState') > 0) {
  18127. root.trigger('click');
  18128. }
  18129. })
  18130. .always(function() {
  18131. spinner.remove();
  18132. });
  18133. }
  18134. },
  18135. /**
  18136. * Return node for given dir object
  18137. *
  18138. * @param Object directory object
  18139. * @return jQuery
  18140. **/
  18141. create = function(dir, hash) {
  18142. return $(tpl.replace(/\{id\}/, hash2id(dir? dir.hash : hash))
  18143. .replace(/\{name\}/, fm.escape(dir? dir.i18 || dir.name : hash))
  18144. .replace(/\{cssclass\}/, dir? (fm.perms2class(dir) + (dir.notfound? ' elfinder-na' : '') + (dir.csscls? ' '+dir.csscls : '')) : '')
  18145. .replace(/\{permissions\}/, (dir && (!dir.read || !dir.write || dir.notfound))? ptpl : '')
  18146. .replace(/\{title\}/, dir? (' title="' + fm.escape(fm.path(dir.hash, true) || dir.i18 || dir.name) + '"') : '')
  18147. .replace(/\{symlink\}/, '')
  18148. .replace(/\{style\}/, (dir && dir.icon)? fm.getIconStyle(dir) : ''));
  18149. },
  18150. /**
  18151. * Add new node into places
  18152. *
  18153. * @param Object directory object
  18154. * @return void
  18155. **/
  18156. add = function(dir) {
  18157. var node, hash;
  18158. if (dir.mime !== 'directory') {
  18159. return false;
  18160. }
  18161. hash = dir.hash;
  18162. if (!fm.files().hasOwnProperty(hash)) {
  18163. // update cache
  18164. fm.trigger('tree', {tree: [dir]});
  18165. }
  18166. node = create(dir, hash);
  18167. dirs[hash] = dir;
  18168. subtree.prepend(node);
  18169. root.addClass(collapsed);
  18170. sortBtn.toggle(subtree.children().length > 1);
  18171. return true;
  18172. },
  18173. /**
  18174. * Remove dir from places
  18175. *
  18176. * @param String directory hash
  18177. * @return String removed name
  18178. **/
  18179. remove = function(hash) {
  18180. var name = null, tgt, cnt;
  18181. if (dirs[hash]) {
  18182. delete dirs[hash];
  18183. tgt = hash2elm(hash);
  18184. if (tgt.length) {
  18185. name = tgt.text();
  18186. tgt.parent().remove();
  18187. cnt = subtree.children().length;
  18188. sortBtn.toggle(cnt > 1);
  18189. if (! cnt) {
  18190. root.removeClass(collapsed);
  18191. places.removeClass(expanded);
  18192. subtree.slideToggle(false);
  18193. }
  18194. }
  18195. }
  18196. return name;
  18197. },
  18198. /**
  18199. * Move up dir on places
  18200. *
  18201. * @param String directory hash
  18202. * @return void
  18203. **/
  18204. moveup = function(hash) {
  18205. var self = hash2elm(hash),
  18206. tgt = self.parent(),
  18207. prev = tgt.prev('div'),
  18208. cls = 'ui-state-hover',
  18209. ctm = fm.getUI('contextmenu');
  18210. menuTimer && clearTimeout(menuTimer);
  18211. if (prev.length) {
  18212. ctm.find(':first').data('placesHash', hash);
  18213. self.addClass(cls);
  18214. tgt.insertBefore(prev);
  18215. prev = tgt.prev('div');
  18216. menuTimer = setTimeout(function() {
  18217. self.removeClass(cls);
  18218. if (ctm.find(':first').data('placesHash') === hash) {
  18219. ctm.hide().empty();
  18220. }
  18221. }, 1500);
  18222. }
  18223. if(!prev.length) {
  18224. self.removeClass(cls);
  18225. ctm.hide().empty();
  18226. }
  18227. },
  18228. /**
  18229. * Update dir at places
  18230. *
  18231. * @param Object directory
  18232. * @param String previous hash
  18233. * @return Boolean
  18234. **/
  18235. update = function(dir, preHash) {
  18236. var hash = dir.hash,
  18237. tgt = hash2elm(preHash || hash),
  18238. node = create(dir, hash);
  18239. if (tgt.length > 0) {
  18240. tgt.parent().replaceWith(node);
  18241. dirs[hash] = dir;
  18242. return true;
  18243. } else {
  18244. return false;
  18245. }
  18246. },
  18247. /**
  18248. * Remove all dir from places
  18249. *
  18250. * @return void
  18251. **/
  18252. clear = function() {
  18253. subtree.empty();
  18254. root.removeClass(collapsed);
  18255. places.removeClass(expanded);
  18256. subtree.slideToggle(false);
  18257. },
  18258. /**
  18259. * Sort places dirs A-Z
  18260. *
  18261. * @return void
  18262. **/
  18263. sort = function() {
  18264. $.each(dirs, function(h, f) {
  18265. var dir = fm.file(h) || f,
  18266. node = create(dir, h),
  18267. ret = null;
  18268. if (!dir) {
  18269. node.hide();
  18270. }
  18271. if (subtree.children().length) {
  18272. $.each(subtree.children(), function() {
  18273. var current = $(this);
  18274. if ((dir.i18 || dir.name).localeCompare(current.children('.'+navdir).text()) < 0) {
  18275. ret = !node.insertBefore(current);
  18276. return ret;
  18277. }
  18278. });
  18279. if (ret !== null) {
  18280. return true;
  18281. }
  18282. }
  18283. !hash2elm(h).length && subtree.append(node);
  18284. });
  18285. save();
  18286. },
  18287. // sort button
  18288. sortBtn = $('<span class="elfinder-button-icon elfinder-button-icon-sort elfinder-places-root-icon" title="'+fm.i18n('cmdsort')+'"></span>')
  18289. .hide()
  18290. .on('click', function(e) {
  18291. e.stopPropagation();
  18292. subtree.empty();
  18293. sort();
  18294. }
  18295. ),
  18296. /**
  18297. * Node - wrapper for places root
  18298. *
  18299. * @type jQuery
  18300. **/
  18301. wrapper = create({
  18302. hash : 'root-'+fm.namespace,
  18303. name : fm.i18n(opts.name, 'places'),
  18304. read : true,
  18305. write : true
  18306. }),
  18307. /**
  18308. * Places root node
  18309. *
  18310. * @type jQuery
  18311. **/
  18312. root = wrapper.children('.'+navdir)
  18313. .addClass(clroot)
  18314. .on('click', function(e) {
  18315. e.stopPropagation();
  18316. if (root.hasClass(collapsed)) {
  18317. places.toggleClass(expanded);
  18318. subtree.slideToggle();
  18319. fm.storage('placesState', places.hasClass(expanded)? 1 : 0);
  18320. }
  18321. })
  18322. .append(sortBtn),
  18323. /**
  18324. * Container for dirs
  18325. *
  18326. * @type jQuery
  18327. **/
  18328. subtree = wrapper.children('.'+fm.res(c, 'navsubtree')),
  18329. /**
  18330. * Main places container
  18331. *
  18332. * @type jQuery
  18333. **/
  18334. places = $(this).addClass(fm.res(c, 'tree')+' elfinder-places ui-corner-all')
  18335. .hide()
  18336. .append(wrapper)
  18337. .appendTo(fm.getUI('navbar'))
  18338. .on('mouseenter mouseleave', '.'+navdir, function(e) {
  18339. $(this).toggleClass('ui-state-hover', (e.type == 'mouseenter'));
  18340. })
  18341. .on('click', '.'+navdir, function(e) {
  18342. var p = $(this);
  18343. if (p.data('longtap')) {
  18344. e.stopPropagation();
  18345. return;
  18346. }
  18347. ! p.hasClass('elfinder-na') && fm.exec('open', p.attr('id').substr(6));
  18348. })
  18349. .on('contextmenu', '.'+navdir+':not(.'+clroot+')', function(e) {
  18350. var self = $(this),
  18351. hash = self.attr('id').substr(6);
  18352. e.preventDefault();
  18353. fm.trigger('contextmenu', {
  18354. raw : [{
  18355. label : fm.i18n('moveUp'),
  18356. icon : 'up',
  18357. remain : true,
  18358. callback : function() { moveup(hash); save(); }
  18359. },'|',{
  18360. label : fm.i18n('rmFromPlaces'),
  18361. icon : 'rm',
  18362. callback : function() { remove(hash); save(); }
  18363. }],
  18364. 'x' : e.pageX,
  18365. 'y' : e.pageY
  18366. });
  18367. self.addClass('ui-state-hover');
  18368. fm.getUI('contextmenu').children().on('mouseenter', function() {
  18369. self.addClass('ui-state-hover');
  18370. });
  18371. fm.bind('closecontextmenu', function() {
  18372. self.removeClass('ui-state-hover');
  18373. });
  18374. })
  18375. .droppable({
  18376. tolerance : 'pointer',
  18377. accept : '.elfinder-cwd-file-wrapper,.elfinder-tree-dir,.elfinder-cwd-file',
  18378. hoverClass : fm.res('class', 'adroppable'),
  18379. classes : { // Deprecated hoverClass jQueryUI>=1.12.0
  18380. 'ui-droppable-hover': fm.res('class', 'adroppable')
  18381. },
  18382. over : function(e, ui) {
  18383. var helper = ui.helper,
  18384. dir = $.grep(helper.data('files'), function(h) { return (fm.file(h).mime === 'directory' && !dirs[h])? true : false; });
  18385. e.stopPropagation();
  18386. helper.data('dropover', helper.data('dropover') + 1);
  18387. if (fm.insideWorkzone(e.pageX, e.pageY)) {
  18388. if (dir.length > 0) {
  18389. helper.addClass('elfinder-drag-helper-plus');
  18390. fm.trigger('unlockfiles', {files : helper.data('files'), helper: helper});
  18391. } else {
  18392. $(this).removeClass(dropover);
  18393. }
  18394. }
  18395. },
  18396. out : function(e, ui) {
  18397. var helper = ui.helper;
  18398. e.stopPropagation();
  18399. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0));
  18400. $(this).removeData('dropover')
  18401. .removeClass(dropover);
  18402. },
  18403. drop : function(e, ui) {
  18404. var helper = ui.helper,
  18405. resolve = true;
  18406. $.each(helper.data('files'), function(i, hash) {
  18407. var dir = fm.file(hash);
  18408. if (dir && dir.mime == 'directory' && !dirs[dir.hash]) {
  18409. add(dir);
  18410. } else {
  18411. resolve = false;
  18412. }
  18413. });
  18414. save();
  18415. resolve && helper.hide();
  18416. }
  18417. })
  18418. // for touch device
  18419. .on('touchstart', '.'+navdir+':not(.'+clroot+')', function(e) {
  18420. if (e.originalEvent.touches.length > 1) {
  18421. return;
  18422. }
  18423. var hash = $(this).attr('id').substr(6),
  18424. p = $(this)
  18425. .addClass(hover)
  18426. .data('longtap', null)
  18427. .data('tmlongtap', setTimeout(function(){
  18428. // long tap
  18429. p.data('longtap', true);
  18430. fm.trigger('contextmenu', {
  18431. raw : [{
  18432. label : fm.i18n('rmFromPlaces'),
  18433. icon : 'rm',
  18434. callback : function() { remove(hash); save(); }
  18435. }],
  18436. 'x' : e.originalEvent.touches[0].pageX,
  18437. 'y' : e.originalEvent.touches[0].pageY
  18438. });
  18439. }, 500));
  18440. })
  18441. .on('touchmove touchend', '.'+navdir+':not(.'+clroot+')', function(e) {
  18442. clearTimeout($(this).data('tmlongtap'));
  18443. if (e.type == 'touchmove') {
  18444. $(this).removeClass(hover);
  18445. }
  18446. });
  18447. if ($.fn.sortable) {
  18448. subtree.addClass('touch-punch')
  18449. .sortable({
  18450. appendTo : fm.getUI(),
  18451. revert : false,
  18452. helper : function(e) {
  18453. var dir = $(e.target).parent();
  18454. dir.children().removeClass('ui-state-hover');
  18455. return $('<div class="ui-widget elfinder-place-drag elfinder-'+fm.direction+'"></div>')
  18456. .append($('<div class="elfinder-navbar"></div>').show().append(dir.clone()));
  18457. },
  18458. stop : function(e, ui) {
  18459. var target = $(ui.item[0]),
  18460. top = places.offset().top,
  18461. left = places.offset().left,
  18462. width = places.width(),
  18463. height = places.height(),
  18464. x = e.pageX,
  18465. y = e.pageY;
  18466. if (!(x > left && x < left+width && y > top && y < y+height)) {
  18467. remove(id2hash(target.children(':first').attr('id')));
  18468. save();
  18469. }
  18470. },
  18471. update : function(e, ui) {
  18472. save();
  18473. }
  18474. });
  18475. }
  18476. // "on regist" for command exec
  18477. $(this).on('regist', function(e, files){
  18478. var added = false;
  18479. $.each(files, function(i, dir) {
  18480. if (dir && dir.mime == 'directory' && !dirs[dir.hash]) {
  18481. if (add(dir)) {
  18482. added = true;
  18483. }
  18484. }
  18485. });
  18486. added && save();
  18487. });
  18488. // on fm load - show places and load files from backend
  18489. fm.one('load', function() {
  18490. var dat, hashes;
  18491. if (fm.oldAPI) {
  18492. return;
  18493. }
  18494. places.show().parent().show();
  18495. init();
  18496. fm.change(function(e) {
  18497. var changed = false;
  18498. $.each(e.data.changed, function(i, file) {
  18499. if (dirs[file.hash]) {
  18500. if (file.mime !== 'directory') {
  18501. if (remove(file.hash)) {
  18502. changed = true;
  18503. }
  18504. } else {
  18505. if (update(file)) {
  18506. changed = true;
  18507. }
  18508. }
  18509. }
  18510. });
  18511. changed && save();
  18512. })
  18513. .bind('rename', function(e) {
  18514. var changed = false;
  18515. if (e.data.removed) {
  18516. $.each(e.data.removed, function(i, hash) {
  18517. if (e.data.added[i]) {
  18518. if (update(e.data.added[i], hash)) {
  18519. changed = true;
  18520. }
  18521. }
  18522. });
  18523. }
  18524. changed && save();
  18525. })
  18526. .bind('rm paste', function(e) {
  18527. var names = [],
  18528. changed = false;
  18529. if (e.data.removed) {
  18530. $.each(e.data.removed, function(i, hash) {
  18531. var name = remove(hash);
  18532. name && names.push(name);
  18533. });
  18534. }
  18535. if (names.length) {
  18536. changed = true;
  18537. }
  18538. if (e.data.added && names.length) {
  18539. $.each(e.data.added, function(i, file) {
  18540. if ($.inArray(file.name, names) !== 1) {
  18541. file.mime == 'directory' && add(file);
  18542. }
  18543. });
  18544. }
  18545. changed && save();
  18546. })
  18547. .bind('sync netmount', function() {
  18548. var ev = this,
  18549. opSuffix = opts.suffix? opts.suffix : '',
  18550. hashes;
  18551. if (ev.type === 'sync') {
  18552. // check is change of opts.suffix
  18553. if (suffix !== opSuffix) {
  18554. suffix = opSuffix;
  18555. clear();
  18556. init();
  18557. return;
  18558. }
  18559. }
  18560. hashes = Object.keys(dirs);
  18561. if (hashes.length) {
  18562. root.prepend(spinner);
  18563. fm.request({
  18564. data : {cmd : 'info', targets : hashes},
  18565. preventDefault : true
  18566. })
  18567. .done(function(data) {
  18568. var exists = {},
  18569. updated = false,
  18570. cwd = fm.cwd().hash;
  18571. $.each(data.files || [], function(i, file) {
  18572. var hash = file.hash;
  18573. exists[hash] = file;
  18574. if (!fm.files().hasOwnProperty(file.hash)) {
  18575. // update cache
  18576. fm.updateCache({tree: [file]});
  18577. }
  18578. });
  18579. $.each(dirs, function(h, f) {
  18580. if (Boolean(f.notfound) === Boolean(exists[h])) {
  18581. if ((f.phash === cwd && ev.type !== 'netmount') || (exists[h] && exists[h].mime !== 'directory')) {
  18582. if (remove(h)) {
  18583. updated = true;
  18584. }
  18585. } else {
  18586. if (update(exists[h] || Object.assign({notfound: true}, f))) {
  18587. updated = true;
  18588. }
  18589. }
  18590. } else if (exists[h] && exists[h].phash != cwd) {
  18591. // update permission of except cwd
  18592. update(exists[h]);
  18593. }
  18594. });
  18595. updated && save();
  18596. })
  18597. .always(function() {
  18598. spinner.remove();
  18599. });
  18600. }
  18601. });
  18602. });
  18603. });
  18604. };
  18605. /*
  18606. * File: /js/ui/searchbutton.js
  18607. */
  18608. /**
  18609. * @class elFinder toolbar search button widget.
  18610. *
  18611. * @author Dmitry (dio) Levashov
  18612. **/
  18613. $.fn.elfindersearchbutton = function(cmd) {
  18614. return this.each(function() {
  18615. var result = false,
  18616. fm = cmd.fm,
  18617. disabled = fm.res('class', 'disabled'),
  18618. isopts = cmd.options.incsearch || { enable: false },
  18619. sTypes = cmd.options.searchTypes,
  18620. id = function(name){return fm.namespace + fm.escape(name);},
  18621. toolbar= fm.getUI('toolbar'),
  18622. btnCls = fm.res('class', 'searchbtn'),
  18623. button = $(this)
  18624. .hide()
  18625. .addClass('ui-widget-content elfinder-button '+btnCls)
  18626. .on('click', function(e) {
  18627. e.stopPropagation();
  18628. }),
  18629. getMenuOffset = function() {
  18630. var fmNode = fm.getUI(),
  18631. baseOffset = fmNode.offset(),
  18632. buttonOffset = button.offset();
  18633. return {
  18634. top : buttonOffset.top - baseOffset.top,
  18635. maxHeight : fmNode.height() - 40
  18636. };
  18637. },
  18638. search = function() {
  18639. input.data('inctm') && clearTimeout(input.data('inctm'));
  18640. var val = $.trim(input.val()),
  18641. from = !$('#' + id('SearchFromAll')).prop('checked'),
  18642. mime = $('#' + id('SearchMime')).prop('checked'),
  18643. type = '';
  18644. if (from) {
  18645. if ($('#' + id('SearchFromVol')).prop('checked')) {
  18646. from = fm.root(fm.cwd().hash);
  18647. } else {
  18648. from = fm.cwd().hash;
  18649. }
  18650. }
  18651. if (mime) {
  18652. mime = val;
  18653. val = '.';
  18654. }
  18655. if (typeSet) {
  18656. type = typeSet.children('input:checked').val();
  18657. }
  18658. if (val) {
  18659. input.trigger('focus');
  18660. cmd.exec(val, from, mime, type).done(function() {
  18661. result = true;
  18662. }).fail(function() {
  18663. abort();
  18664. });
  18665. } else {
  18666. fm.trigger('searchend');
  18667. }
  18668. },
  18669. abort = function() {
  18670. input.data('inctm') && clearTimeout(input.data('inctm'));
  18671. input.val('').trigger('blur');
  18672. if (result || incVal) {
  18673. result = false;
  18674. incVal = '';
  18675. fm.lazy(function() {
  18676. fm.trigger('searchend');
  18677. });
  18678. }
  18679. },
  18680. incVal = '',
  18681. input = $('<input type="text" size="42"/>')
  18682. .on('focus', function() {
  18683. // close other menus
  18684. !button.hasClass('ui-state-active') && fm.getUI().click();
  18685. inFocus = true;
  18686. incVal = '';
  18687. button.addClass('ui-state-active');
  18688. fm.trigger('uiresize');
  18689. opts && opts.css(getMenuOffset()).slideDown(function() {
  18690. // Care for on browser window re-active
  18691. button.addClass('ui-state-active');
  18692. fm.toFront(opts);
  18693. });
  18694. })
  18695. .on('blur', function() {
  18696. inFocus = false;
  18697. if (opts) {
  18698. if (!opts.data('infocus')) {
  18699. opts.slideUp(function() {
  18700. button.removeClass('ui-state-active');
  18701. fm.trigger('uiresize');
  18702. fm.toHide(opts);
  18703. });
  18704. } else {
  18705. opts.data('infocus', false);
  18706. }
  18707. } else {
  18708. button.removeClass('ui-state-active');
  18709. }
  18710. })
  18711. .appendTo(button)
  18712. // to avoid fm shortcuts on arrows
  18713. .on('keypress', function(e) {
  18714. e.stopPropagation();
  18715. })
  18716. .on('keydown', function(e) {
  18717. e.stopPropagation();
  18718. if (e.keyCode === $.ui.keyCode.ENTER) {
  18719. search();
  18720. } else if (e.keyCode === $.ui.keyCode.ESCAPE) {
  18721. e.preventDefault();
  18722. abort();
  18723. }
  18724. }),
  18725. opts, typeSet, cwdReady, inFocus;
  18726. if (isopts.enable) {
  18727. isopts.minlen = isopts.minlen || 2;
  18728. isopts.wait = isopts.wait || 500;
  18729. input
  18730. .attr('title', fm.i18n('incSearchOnly'))
  18731. .on('compositionstart', function() {
  18732. input.data('composing', true);
  18733. })
  18734. .on('compositionend', function() {
  18735. input.removeData('composing');
  18736. input.trigger('input'); // for IE, edge
  18737. })
  18738. .on('input', function() {
  18739. if (! input.data('composing')) {
  18740. input.data('inctm') && clearTimeout(input.data('inctm'));
  18741. input.data('inctm', setTimeout(function() {
  18742. var val = input.val();
  18743. if (val.length === 0 || val.length >= isopts.minlen) {
  18744. (incVal !== val) && fm.trigger('incsearchstart', {
  18745. query: val,
  18746. type: typeSet? typeSet.children('input:checked').val() : 'searchName'
  18747. });
  18748. incVal = val;
  18749. if (val === '' && fm.searchStatus.state > 1 && fm.searchStatus.query) {
  18750. input.val(fm.searchStatus.query).trigger('select');
  18751. }
  18752. }
  18753. }, isopts.wait));
  18754. }
  18755. });
  18756. if (fm.UA.ltIE8) {
  18757. input.on('keydown', function(e) {
  18758. if (e.keyCode === 229) {
  18759. input.data('imetm') && clearTimeout(input.data('imetm'));
  18760. input.data('composing', true);
  18761. input.data('imetm', setTimeout(function() {
  18762. input.removeData('composing');
  18763. }, 100));
  18764. }
  18765. })
  18766. .on('keyup', function(e) {
  18767. input.data('imetm') && clearTimeout(input.data('imetm'));
  18768. if (input.data('composing')) {
  18769. e.keyCode === $.ui.keyCode.ENTER && input.trigger('compositionend');
  18770. } else {
  18771. input.trigger('input');
  18772. }
  18773. });
  18774. }
  18775. }
  18776. $('<span class="ui-icon ui-icon-search" title="'+cmd.title+'"></span>')
  18777. .appendTo(button)
  18778. .on('mousedown', function(e) {
  18779. e.stopPropagation();
  18780. e.preventDefault();
  18781. if (button.hasClass('ui-state-active')) {
  18782. search();
  18783. } else {
  18784. input.trigger('focus');
  18785. }
  18786. });
  18787. $('<span class="ui-icon ui-icon-close"></span>')
  18788. .appendTo(button)
  18789. .on('mousedown', function(e) {
  18790. e.stopPropagation();
  18791. e.preventDefault();
  18792. if (input.val() === '' && !button.hasClass('ui-state-active')) {
  18793. input.trigger('focus');
  18794. } else {
  18795. abort();
  18796. }
  18797. });
  18798. // wait when button will be added to DOM
  18799. fm.bind('toolbarload', function(){
  18800. var parent = button.parent();
  18801. if (parent.length) {
  18802. toolbar.prepend(button.show());
  18803. parent.remove();
  18804. // position icons for ie7
  18805. if (fm.UA.ltIE7) {
  18806. var icon = button.children(fm.direction == 'ltr' ? '.ui-icon-close' : '.ui-icon-search');
  18807. icon.css({
  18808. right : '',
  18809. left : parseInt(button.width())-icon.outerWidth(true)
  18810. });
  18811. }
  18812. }
  18813. });
  18814. fm
  18815. .one('init', function() {
  18816. fm.getUI('cwd').on('touchstart click', function() {
  18817. inFocus && input.trigger('blur');
  18818. });
  18819. })
  18820. .one('open', function() {
  18821. opts = (fm.api < 2.1)? null : $('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu elfinder-button-search-menu ui-corner-all"></div>')
  18822. .append(
  18823. $('<div class="buttonset"></div>')
  18824. .append(
  18825. $('<input id="'+id('SearchFromCwd')+'" name="serchfrom" type="radio" checked="checked"/><label for="'+id('SearchFromCwd')+'">'+fm.i18n('btnCwd')+'</label>'),
  18826. $('<input id="'+id('SearchFromVol')+'" name="serchfrom" type="radio"/><label for="'+id('SearchFromVol')+'">'+fm.i18n('btnVolume')+'</label>'),
  18827. $('<input id="'+id('SearchFromAll')+'" name="serchfrom" type="radio"/><label for="'+id('SearchFromAll')+'">'+fm.i18n('btnAll')+'</label>')
  18828. ),
  18829. $('<div class="buttonset elfinder-search-type"></div>')
  18830. .append(
  18831. $('<input id="'+id('SearchName')+'" name="serchcol" type="radio" checked="checked" value="SearchName"/><label for="'+id('SearchName')+'">'+fm.i18n('btnFileName')+'</label>')
  18832. )
  18833. )
  18834. .hide()
  18835. .appendTo(fm.getUI());
  18836. if (opts) {
  18837. if (sTypes) {
  18838. typeSet = opts.find('.elfinder-search-type');
  18839. $.each(cmd.options.searchTypes, function(i, v) {
  18840. typeSet.append($('<input id="'+id(i)+'" name="serchcol" type="radio" value="'+fm.escape(i)+'"/><label for="'+id(i)+'">'+fm.i18n(v.name)+'</label>'));
  18841. });
  18842. }
  18843. opts.find('div.buttonset').buttonset();
  18844. $('#'+id('SearchFromAll')).next('label').attr('title', fm.i18n('searchTarget', fm.i18n('btnAll')));
  18845. if (sTypes) {
  18846. $.each(sTypes, function(i, v) {
  18847. if (v.title) {
  18848. $('#'+id(i)).next('label').attr('title', fm.i18n(v.title));
  18849. }
  18850. });
  18851. }
  18852. opts.on('mousedown', 'div.buttonset', function(e){
  18853. e.stopPropagation();
  18854. opts.data('infocus', true);
  18855. })
  18856. .on('click', 'input', function(e) {
  18857. e.stopPropagation();
  18858. $.trim(input.val())? search() : input.trigger('focus');
  18859. })
  18860. .on('close', function() {
  18861. input.trigger('blur');
  18862. });
  18863. }
  18864. })
  18865. .bind('searchend', function() {
  18866. input.val('');
  18867. })
  18868. .bind('open parents', function() {
  18869. var dirs = [],
  18870. volroot = fm.file(fm.root(fm.cwd().hash));
  18871. if (volroot) {
  18872. $.each(fm.parents(fm.cwd().hash), function(i, hash) {
  18873. dirs.push(fm.file(hash).name);
  18874. });
  18875. $('#'+id('SearchFromCwd')).next('label').attr('title', fm.i18n('searchTarget', dirs.join(fm.option('separator'))));
  18876. $('#'+id('SearchFromVol')).next('label').attr('title', fm.i18n('searchTarget', volroot.name));
  18877. }
  18878. })
  18879. .bind('open', function() {
  18880. incVal && abort();
  18881. })
  18882. .bind('cwdinit', function() {
  18883. cwdReady = false;
  18884. })
  18885. .bind('cwdrender',function() {
  18886. cwdReady = true;
  18887. })
  18888. .bind('keydownEsc', function() {
  18889. if (incVal && incVal.substr(0, 1) === '/') {
  18890. incVal = '';
  18891. input.val('');
  18892. fm.trigger('searchend');
  18893. }
  18894. })
  18895. .shortcut({
  18896. pattern : 'ctrl+f f3',
  18897. description : cmd.title,
  18898. callback : function() {
  18899. input.trigger('select').trigger('focus');
  18900. }
  18901. })
  18902. .shortcut({
  18903. pattern : 'a b c d e f g h i j k l m n o p q r s t u v w x y z dig0 dig1 dig2 dig3 dig4 dig5 dig6 dig7 dig8 dig9 num0 num1 num2 num3 num4 num5 num6 num7 num8 num9',
  18904. description : fm.i18n('firstLetterSearch'),
  18905. callback : function(e) {
  18906. if (! cwdReady) { return; }
  18907. var code = e.originalEvent.keyCode,
  18908. next = function() {
  18909. var sel = fm.selected(),
  18910. key = $.ui.keyCode[(!sel.length || fm.cwdHash2Elm(sel[0]).next('[id]').length)? 'RIGHT' : 'HOME'];
  18911. $(document).trigger($.Event('keydown', { keyCode: key, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false }));
  18912. },
  18913. val;
  18914. if (code >= 96 && code <= 105) {
  18915. code -= 48;
  18916. }
  18917. val = '/' + String.fromCharCode(code);
  18918. if (incVal !== val) {
  18919. input.val(val);
  18920. incVal = val;
  18921. fm
  18922. .trigger('incsearchstart', { query: val })
  18923. .one('cwdrender', next);
  18924. } else{
  18925. next();
  18926. }
  18927. }
  18928. });
  18929. });
  18930. };
  18931. /*
  18932. * File: /js/ui/sortbutton.js
  18933. */
  18934. /**
  18935. * @class elFinder toolbar button menu with sort variants.
  18936. *
  18937. * @author Dmitry (dio) Levashov
  18938. **/
  18939. $.fn.elfindersortbutton = function(cmd) {
  18940. return this.each(function() {
  18941. var fm = cmd.fm,
  18942. name = cmd.name,
  18943. c = 'class',
  18944. disabled = fm.res(c, 'disabled'),
  18945. hover = fm.res(c, 'hover'),
  18946. item = 'elfinder-button-menu-item',
  18947. selected = item+'-selected',
  18948. asc = selected+'-asc',
  18949. desc = selected+'-desc',
  18950. text = $('<span class="elfinder-button-text">'+cmd.title+'</span>'),
  18951. button = $(this).addClass('ui-state-default elfinder-button elfinder-menubutton elfiner-button-'+name)
  18952. .attr('title', cmd.title)
  18953. .append('<span class="elfinder-button-icon elfinder-button-icon-'+name+'"></span>', text)
  18954. .on('mouseenter mouseleave', function(e) { !button.hasClass(disabled) && button.toggleClass(hover, e.type === 'mouseenter'); })
  18955. .on('click', function(e) {
  18956. if (!button.hasClass(disabled)) {
  18957. e.stopPropagation();
  18958. menu.is(':hidden') && fm.getUI().click();
  18959. menu.css(getMenuOffset()).slideToggle({
  18960. duration: 100,
  18961. done: function(e) {
  18962. fm[menu.is(':visible')? 'toFront' : 'toHide'](menu);
  18963. }
  18964. });
  18965. }
  18966. }),
  18967. hide = function() { fm.toHide(menu); },
  18968. menu = $('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu elfinder-button-sort-menu ui-corner-all"></div>')
  18969. .hide()
  18970. .appendTo(fm.getUI())
  18971. .on('mouseenter mouseleave', '.'+item, function(e) { $(this).toggleClass(hover, e.type === 'mouseenter'); })
  18972. .on('click', function(e) {
  18973. e.preventDefault();
  18974. e.stopPropagation();
  18975. })
  18976. .on('close', hide),
  18977. update = function() {
  18978. menu.children('[rel]').removeClass(selected+' '+asc+' '+desc)
  18979. .filter('[rel="'+fm.sortType+'"]')
  18980. .addClass(selected+' '+(fm.sortOrder == 'asc' ? asc : desc));
  18981. menu.children('.elfinder-sort-stick').toggleClass(selected, fm.sortStickFolders);
  18982. menu.children('.elfinder-sort-tree').toggleClass(selected, fm.sortAlsoTreeview);
  18983. },
  18984. getMenuOffset = function() {
  18985. var baseOffset = fm.getUI().offset(),
  18986. buttonOffset = button.offset();
  18987. return {
  18988. top : buttonOffset.top - baseOffset.top,
  18989. left : buttonOffset.left - baseOffset.left
  18990. };
  18991. },
  18992. tm;
  18993. text.hide();
  18994. $.each(fm.sortRules, function(name, value) {
  18995. menu.append($('<div class="'+item+'" rel="'+name+'"><span class="ui-icon ui-icon-arrowthick-1-n"></span><span class="ui-icon ui-icon-arrowthick-1-s"></span>'+fm.i18n('sort'+name)+'</div>').data('type', name));
  18996. });
  18997. menu.children().on('click', function(e) {
  18998. cmd.exec([], $(this).removeClass(hover).attr('rel'));
  18999. });
  19000. $('<div class="'+item+' '+item+'-separated elfinder-sort-ext elfinder-sort-stick"><span class="ui-icon ui-icon-check"></span>'+fm.i18n('sortFoldersFirst')+'</div>')
  19001. .appendTo(menu)
  19002. .on('click', function() {
  19003. cmd.exec([], 'stick');
  19004. });
  19005. fm.one('init', function() {
  19006. if (fm.ui.tree && fm.options.sortAlsoTreeview !== null) {
  19007. $('<div class="'+item+' '+item+'-separated elfinder-sort-ext elfinder-sort-tree"><span class="ui-icon ui-icon-check"></span>'+fm.i18n('sortAlsoTreeview')+'</div>')
  19008. .appendTo(menu)
  19009. .on('click', function() {
  19010. cmd.exec([], 'tree');
  19011. });
  19012. }
  19013. })
  19014. .bind('disable select', hide)
  19015. .bind('sortchange', update).getUI().on('click', hide);
  19016. if (menu.children().length > 1) {
  19017. cmd.change(function() {
  19018. tm && cancelAnimationFrame(tm);
  19019. tm = requestAnimationFrame(function() {
  19020. button.toggleClass(disabled, cmd.disabled());
  19021. update();
  19022. });
  19023. })
  19024. .change();
  19025. } else {
  19026. button.addClass(disabled);
  19027. }
  19028. });
  19029. };
  19030. /*
  19031. * File: /js/ui/stat.js
  19032. */
  19033. /**
  19034. * @class elFinder ui
  19035. * Display number of files/selected files and its size in statusbar
  19036. *
  19037. * @author Dmitry (dio) Levashov
  19038. **/
  19039. $.fn.elfinderstat = function(fm) {
  19040. return this.each(function() {
  19041. var size = $(this).addClass('elfinder-stat-size'),
  19042. sel = $('<div class="elfinder-stat-selected"></div>')
  19043. .on('click', 'a', function(e) {
  19044. var hash = $(this).data('hash');
  19045. e.preventDefault();
  19046. fm.exec('opendir', [ hash ]);
  19047. }),
  19048. titleitems = fm.i18n('items'),
  19049. titlesel = fm.i18n('selected'),
  19050. titlesize = fm.i18n('size'),
  19051. setstat = function(files) {
  19052. var c = 0,
  19053. s = 0,
  19054. cwd = fm.cwd(),
  19055. calc = true,
  19056. hasSize = true;
  19057. if (cwd.sizeInfo || cwd.size) {
  19058. s = cwd.size;
  19059. calc = false;
  19060. }
  19061. $.each(files, function(i, file) {
  19062. c++;
  19063. if (calc) {
  19064. s += parseInt(file.size) || 0;
  19065. if (hasSize === true && file.mime === 'directory' && !file.sizeInfo) {
  19066. hasSize = false;
  19067. }
  19068. }
  19069. });
  19070. size.html(titleitems+': <span class="elfinder-stat-incsearch"></span>'+c+',&nbsp;<span class="elfinder-stat-size'+(hasSize? ' elfinder-stat-size-recursive' : '')+'">'+fm.i18n(hasSize? 'sum' : 'size')+': '+fm.formatSize(s)+'</span>')
  19071. .attr('title', size.text());
  19072. fm.trigger('uistatchange');
  19073. },
  19074. setIncsearchStat = function(data) {
  19075. size.find('span.elfinder-stat-incsearch').html(data? data.hashes.length + ' / ' : '');
  19076. size.attr('title', size.text());
  19077. fm.trigger('uistatchange');
  19078. },
  19079. setSelect = function(files) {
  19080. var s = 0,
  19081. c = 0,
  19082. dirs = [],
  19083. path, file;
  19084. if (files.length === 1) {
  19085. file = files[0];
  19086. s = file.size;
  19087. if (fm.searchStatus.state === 2) {
  19088. path = fm.escape(file.path? file.path.replace(/\/[^\/]*$/, '') : '..');
  19089. dirs.push('<a href="#elf_'+file.phash+'" data-hash="'+file.hash+'" title="'+path+'">'+path+'</a>');
  19090. }
  19091. dirs.push(fm.escape(file.i18 || file.name));
  19092. sel.html(dirs.join('/') + (s > 0 ? ', '+fm.formatSize(s) : ''));
  19093. } else if (files.length) {
  19094. $.each(files, function(i, file) {
  19095. c++;
  19096. s += parseInt(file.size)||0;
  19097. });
  19098. sel.html(c ? titlesel+': '+c+', '+titlesize+': '+fm.formatSize(s) : '&nbsp;');
  19099. } else {
  19100. sel.html('');
  19101. }
  19102. sel.attr('title', sel.text());
  19103. fm.trigger('uistatchange');
  19104. };
  19105. fm.getUI('statusbar').prepend(size).append(sel).show();
  19106. if (fm.UA.Mobile && $.fn.tooltip) {
  19107. fm.getUI('statusbar').tooltip({
  19108. classes: {
  19109. 'ui-tooltip': 'elfinder-ui-tooltip ui-widget-shadow'
  19110. },
  19111. tooltipClass: 'elfinder-ui-tooltip ui-widget-shadow',
  19112. track: true
  19113. });
  19114. }
  19115. fm
  19116. .bind('cwdhasheschange', function(e) {
  19117. setstat($.map(e.data, function(h) { return fm.file(h); }));
  19118. })
  19119. .change(function(e) {
  19120. var files = e.data.changed || [],
  19121. cwdHash = fm.cwd().hash;
  19122. $.each(files, function() {
  19123. if (this.hash === cwdHash) {
  19124. if (this.size) {
  19125. size.children('.elfinder-stat-size').addClass('elfinder-stat-size-recursive').html(fm.i18n('sum')+': '+fm.formatSize(this.size));
  19126. size.attr('title', size.text());
  19127. }
  19128. return false;
  19129. }
  19130. });
  19131. })
  19132. .select(function() {
  19133. setSelect(fm.selectedFiles());
  19134. })
  19135. .bind('open', function() {
  19136. setSelect([]);
  19137. })
  19138. .bind('incsearch', function(e) {
  19139. setIncsearchStat(e.data);
  19140. })
  19141. .bind('incsearchend', function() {
  19142. setIncsearchStat();
  19143. })
  19144. ;
  19145. });
  19146. };
  19147. /*
  19148. * File: /js/ui/toast.js
  19149. */
  19150. /**
  19151. * @class elFinder toast
  19152. *
  19153. * This was created inspired by the toastr. Thanks to developers of toastr.
  19154. * CodeSeven/toastr: http://johnpapa.net <https://github.com/CodeSeven/toastr>
  19155. *
  19156. * @author Naoki Sawada
  19157. **/
  19158. $.fn.elfindertoast = function(opts, fm) {
  19159. var defOpts = Object.assign({
  19160. mode: 'success', // or 'info', 'warning' and 'error'
  19161. msg: '',
  19162. showMethod: 'fadeIn', //fadeIn, slideDown, and show are built into jQuery
  19163. showDuration: 300,
  19164. showEasing: 'swing', //swing and linear are built into jQuery
  19165. onShown: undefined,
  19166. hideMethod: 'fadeOut',
  19167. hideDuration: 1500,
  19168. hideEasing: 'swing',
  19169. onHidden: undefined,
  19170. timeOut: 3000,
  19171. extNode: undefined,
  19172. button: undefined,
  19173. width: undefined
  19174. }, $.isPlainObject(fm.options.uiOptions.toast.defaults)? fm.options.uiOptions.toast.defaults : {});
  19175. return this.each(function() {
  19176. opts = Object.assign({}, defOpts, opts || {});
  19177. var self = $(this),
  19178. show = function(notm) {
  19179. self.stop();
  19180. fm.toFront(self);
  19181. self[opts.showMethod]({
  19182. duration: opts.showDuration,
  19183. easing: opts.showEasing,
  19184. complete: function() {
  19185. opts.onShown && opts.onShown();
  19186. if (!notm && opts.timeOut) {
  19187. rmTm = setTimeout(rm, opts.timeOut);
  19188. }
  19189. }
  19190. });
  19191. },
  19192. rm = function() {
  19193. self[opts.hideMethod]({
  19194. duration: opts.hideDuration,
  19195. easing: opts.hideEasing,
  19196. complete: function() {
  19197. opts.onHidden && opts.onHidden();
  19198. self.remove();
  19199. }
  19200. });
  19201. },
  19202. rmTm;
  19203. self.on('click', function(e) {
  19204. e.stopPropagation();
  19205. e.preventDefault();
  19206. rmTm && clearTimeout(rmTm);
  19207. opts.onHidden && opts.onHidden();
  19208. self.stop().remove();
  19209. }).on('mouseenter mouseleave', function(e) {
  19210. if (opts.timeOut) {
  19211. rmTm && clearTimeout(rmTm);
  19212. rmTm = null;
  19213. if (e.type === 'mouseenter') {
  19214. show(true);
  19215. } else {
  19216. rmTm = setTimeout(rm, opts.timeOut);
  19217. }
  19218. }
  19219. }).hide().addClass('toast-' + opts.mode).append($('<div class="elfinder-toast-msg"></div>').html(opts.msg.replace(/%([a-zA-Z0-9]+)%/g, function(m, m1) {
  19220. return fm.i18n(m1);
  19221. })));
  19222. if (opts.extNode) {
  19223. self.append(opts.extNode);
  19224. }
  19225. if (opts.button) {
  19226. self.append(
  19227. $('<button class="ui-button ui-widget ui-state-default ui-corner-all elfinder-tabstop"></button>')
  19228. .append($('<span class="ui-button-text"></span>').text(fm.i18n(opts.button.text)))
  19229. .on('mouseenter mouseleave', function(e) {
  19230. $(this).toggleClass('ui-state-hover', e.type == 'mouseenter');
  19231. })
  19232. .on('click', opts.button.click || function(){})
  19233. );
  19234. }
  19235. if (opts.width) {
  19236. self.css('max-width', opts.width);
  19237. }
  19238. show();
  19239. });
  19240. };
  19241. /*
  19242. * File: /js/ui/toolbar.js
  19243. */
  19244. /**
  19245. * @class elFinder toolbar
  19246. *
  19247. * @author Dmitry (dio) Levashov
  19248. **/
  19249. $.fn.elfindertoolbar = function(fm, opts) {
  19250. this.not('.elfinder-toolbar').each(function() {
  19251. var commands = fm._commands,
  19252. self = $(this).addClass('ui-helper-clearfix ui-widget-header elfinder-toolbar'),
  19253. options = {
  19254. // default options
  19255. displayTextLabel: false,
  19256. labelExcludeUA: ['Mobile'],
  19257. autoHideUA: ['Mobile'],
  19258. showPreferenceButton: 'none'
  19259. },
  19260. filter = function(opts) {
  19261. return $.grep(opts, function(v) {
  19262. if ($.isPlainObject(v)) {
  19263. options = Object.assign(options, v);
  19264. return false;
  19265. }
  19266. return true;
  19267. });
  19268. },
  19269. render = function(disabled){
  19270. var name,cmdPref;
  19271. $.each(buttons, function(i, b) { b.detach(); });
  19272. self.empty();
  19273. l = panels.length;
  19274. while (l--) {
  19275. if (panels[l]) {
  19276. panel = $('<div class="ui-widget-content ui-corner-all elfinder-buttonset"></div>');
  19277. i = panels[l].length;
  19278. while (i--) {
  19279. name = panels[l][i];
  19280. if ((!disabled || !disabled[name]) && (cmd = commands[name])) {
  19281. button = 'elfinder'+cmd.options.ui;
  19282. if (! buttons[name] && $.fn[button]) {
  19283. buttons[name] = $('<div></div>')[button](cmd);
  19284. }
  19285. if (buttons[name]) {
  19286. buttons[name].children('.elfinder-button-text')[textLabel? 'show' : 'hide']();
  19287. panel.prepend(buttons[name]);
  19288. }
  19289. }
  19290. }
  19291. panel.children().length && self.prepend(panel);
  19292. panel.children(':gt(0)').before('<span class="ui-widget-content elfinder-toolbar-button-separator"></span>');
  19293. }
  19294. }
  19295. if (cmdPref = commands['preference']) {
  19296. //cmdPref.state = !self.children().length? 0 : -1;
  19297. if (options.showPreferenceButton === 'always' || (!self.children().length && options.showPreferenceButton === 'auto')) {
  19298. //cmdPref.state = 0;
  19299. panel = $('<div class="ui-widget-content ui-corner-all elfinder-buttonset"></div>');
  19300. name = 'preference';
  19301. button = 'elfinder'+cmd.options.ui;
  19302. buttons[name] = $('<div></div>')[button](cmdPref);
  19303. buttons[name].children('.elfinder-button-text')[textLabel? 'show' : 'hide']();
  19304. panel.prepend(buttons[name]);
  19305. self.append(panel);
  19306. }
  19307. }
  19308. (! self.data('swipeClose') && self.children().length)? self.show() : self.hide();
  19309. prevHeight = self[0].clientHeight;
  19310. fm.trigger('toolbarload').trigger('uiresize');
  19311. },
  19312. buttons = {},
  19313. panels = filter(opts || []),
  19314. dispre = null,
  19315. uiCmdMapPrev = '',
  19316. prevHeight = 0,
  19317. contextRaw = [],
  19318. l, i, cmd, panel, button, swipeHandle, autoHide, textLabel, resizeTm;
  19319. // normalize options
  19320. options.showPreferenceButton = options.showPreferenceButton.toLowerCase();
  19321. if (options.displayTextLabel !== 'none') {
  19322. // correction of options.displayTextLabel
  19323. textLabel = fm.storage('toolbarTextLabel');
  19324. if (textLabel === null) {
  19325. textLabel = (options.displayTextLabel && (! options.labelExcludeUA || ! options.labelExcludeUA.length || ! $.grep(options.labelExcludeUA, function(v){ return fm.UA[v]? true : false; }).length));
  19326. } else {
  19327. textLabel = (textLabel == 1);
  19328. }
  19329. contextRaw.push({
  19330. label : fm.i18n('textLabel'),
  19331. icon : 'text',
  19332. callback : function() {
  19333. textLabel = ! textLabel;
  19334. self.css('height', '').find('.elfinder-button-text')[textLabel? 'show':'hide']();
  19335. fm.trigger('uiresize').storage('toolbarTextLabel', textLabel? '1' : '0');
  19336. },
  19337. });
  19338. }
  19339. if (options.preferenceInContextmenu && commands['preference']) {
  19340. contextRaw.push({
  19341. label : fm.i18n('toolbarPref'),
  19342. icon : 'preference',
  19343. callback : function() {
  19344. fm.exec('preference', void(0), {tab: 'toolbar'});
  19345. }
  19346. });
  19347. }
  19348. // add contextmenu
  19349. if (contextRaw.length) {
  19350. self.on('contextmenu', function(e) {
  19351. e.stopPropagation();
  19352. e.preventDefault();
  19353. fm.trigger('contextmenu', {
  19354. raw: contextRaw,
  19355. x: e.pageX,
  19356. y: e.pageY
  19357. });
  19358. }).on('touchstart', function(e) {
  19359. if (e.originalEvent.touches.length > 1) {
  19360. return;
  19361. }
  19362. self.data('tmlongtap') && clearTimeout(self.data('tmlongtap'));
  19363. self.removeData('longtap')
  19364. .data('longtap', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY})
  19365. .data('tmlongtap', setTimeout(function() {
  19366. self.removeData('longtapTm')
  19367. .trigger({
  19368. type: 'contextmenu',
  19369. pageX: self.data('longtap').x,
  19370. pageY: self.data('longtap').y
  19371. })
  19372. .data('longtap', {longtap: true});
  19373. }, 500));
  19374. }).on('touchmove touchend', function(e) {
  19375. if (self.data('tmlongtap')) {
  19376. if (e.type === 'touchend' ||
  19377. ( Math.abs(self.data('longtap').x - e.originalEvent.touches[0].pageX)
  19378. + Math.abs(self.data('longtap').y - e.originalEvent.touches[0].pageY)) > 4)
  19379. clearTimeout(self.data('tmlongtap'));
  19380. self.removeData('longtapTm');
  19381. }
  19382. }).on('click', function(e) {
  19383. if (self.data('longtap') && self.data('longtap').longtap) {
  19384. e.stopImmediatePropagation();
  19385. e.preventDefault();
  19386. }
  19387. }).on('touchend click', '.elfinder-button', function(e) {
  19388. if (self.data('longtap') && self.data('longtap').longtap) {
  19389. e.stopImmediatePropagation();
  19390. e.preventDefault();
  19391. }
  19392. }
  19393. );
  19394. }
  19395. self.prev().length && self.parent().prepend(this);
  19396. render();
  19397. fm.bind('open sync select toolbarpref', function() {
  19398. var disabled = Object.assign({}, fm.option('disabledFlip')),
  19399. userHides = fm.storage('toolbarhides'),
  19400. doRender, sel, disabledKeys;
  19401. if (! userHides && Array.isArray(options.defaultHides)) {
  19402. userHides = {};
  19403. $.each(options.defaultHides, function() {
  19404. userHides[this] = true;
  19405. });
  19406. fm.storage('toolbarhides', userHides);
  19407. }
  19408. if (this.type === 'select') {
  19409. if (fm.searchStatus.state < 2) {
  19410. return;
  19411. }
  19412. sel = fm.selected();
  19413. if (sel.length) {
  19414. disabled = fm.getDisabledCmds(sel, true);
  19415. }
  19416. }
  19417. $.each(userHides, function(n) {
  19418. if (!disabled[n]) {
  19419. disabled[n] = true;
  19420. }
  19421. });
  19422. if (Object.keys(fm.commandMap).length) {
  19423. $.each(fm.commandMap, function(from, to){
  19424. if (to === 'hidden') {
  19425. disabled[from] = true;
  19426. }
  19427. });
  19428. }
  19429. disabledKeys = Object.keys(disabled);
  19430. if (!dispre || dispre.toString() !== disabledKeys.sort().toString()) {
  19431. render(disabledKeys.length? disabled : null);
  19432. doRender = true;
  19433. }
  19434. dispre = disabledKeys.sort();
  19435. if (doRender || uiCmdMapPrev !== JSON.stringify(fm.commandMap)) {
  19436. uiCmdMapPrev = JSON.stringify(fm.commandMap);
  19437. if (! doRender) {
  19438. // reset toolbar
  19439. $.each($('div.elfinder-button'), function(){
  19440. var origin = $(this).data('origin');
  19441. if (origin) {
  19442. $(this).after(origin).detach();
  19443. }
  19444. });
  19445. }
  19446. if (Object.keys(fm.commandMap).length) {
  19447. $.each(fm.commandMap, function(from, to){
  19448. var cmd = fm._commands[to],
  19449. button = cmd? 'elfinder'+cmd.options.ui : null,
  19450. btn;
  19451. if (button && $.fn[button]) {
  19452. btn = buttons[from];
  19453. if (btn) {
  19454. if (! buttons[to] && $.fn[button]) {
  19455. buttons[to] = $('<div></div>')[button](cmd);
  19456. if (buttons[to]) {
  19457. buttons[to].children('.elfinder-button-text')[textLabel? 'show' : 'hide']();
  19458. if (cmd.extendsCmd) {
  19459. buttons[to].children('span.elfinder-button-icon').addClass('elfinder-button-icon-' + cmd.extendsCmd);
  19460. }
  19461. }
  19462. }
  19463. if (buttons[to]) {
  19464. btn.after(buttons[to]);
  19465. buttons[to].data('origin', btn.detach());
  19466. }
  19467. }
  19468. }
  19469. });
  19470. }
  19471. }
  19472. }).bind('resize', function(e) {
  19473. resizeTm && cancelAnimationFrame(resizeTm);
  19474. resizeTm = requestAnimationFrame(function() {
  19475. var h = self[0].clientHeight;
  19476. if (prevHeight !== h) {
  19477. prevHeight = h;
  19478. fm.trigger('uiresize');
  19479. }
  19480. });
  19481. });
  19482. if (fm.UA.Touch) {
  19483. autoHide = fm.storage('autoHide') || {};
  19484. if (typeof autoHide.toolbar === 'undefined') {
  19485. autoHide.toolbar = (options.autoHideUA && options.autoHideUA.length > 0 && $.grep(options.autoHideUA, function(v){ return fm.UA[v]? true : false; }).length);
  19486. fm.storage('autoHide', autoHide);
  19487. }
  19488. if (autoHide.toolbar) {
  19489. fm.one('init', function() {
  19490. fm.uiAutoHide.push(function(){ self.stop(true, true).trigger('toggle', { duration: 500, init: true }); });
  19491. });
  19492. }
  19493. fm.bind('load', function() {
  19494. swipeHandle = $('<div class="elfinder-toolbar-swipe-handle"></div>').hide().appendTo(fm.getUI());
  19495. if (swipeHandle.css('pointer-events') !== 'none') {
  19496. swipeHandle.remove();
  19497. swipeHandle = null;
  19498. }
  19499. });
  19500. self.on('toggle', function(e, data) {
  19501. var wz = fm.getUI('workzone'),
  19502. toshow= self.is(':hidden'),
  19503. wzh = wz.height(),
  19504. h = self.height(),
  19505. tbh = self.outerHeight(true),
  19506. delta = tbh - h,
  19507. opt = Object.assign({
  19508. step: function(now) {
  19509. wz.height(wzh + (toshow? (now + delta) * -1 : h - now));
  19510. fm.trigger('resize');
  19511. },
  19512. always: function() {
  19513. requestAnimationFrame(function() {
  19514. self.css('height', '');
  19515. fm.trigger('uiresize');
  19516. if (swipeHandle) {
  19517. if (toshow) {
  19518. swipeHandle.stop(true, true).hide();
  19519. } else {
  19520. swipeHandle.height(data.handleH? data.handleH : '');
  19521. fm.resources.blink(swipeHandle, 'slowonce');
  19522. }
  19523. }
  19524. toshow && self.scrollTop('0px');
  19525. data.init && fm.trigger('uiautohide');
  19526. });
  19527. }
  19528. }, data);
  19529. self.data('swipeClose', ! toshow).stop(true, true).animate({height : 'toggle'}, opt);
  19530. autoHide.toolbar = !toshow;
  19531. fm.storage('autoHide', Object.assign(fm.storage('autoHide'), {toolbar: autoHide.toolbar}));
  19532. }).on('touchstart', function(e) {
  19533. if (self.scrollBottom() > 5) {
  19534. e.originalEvent._preventSwipeY = true;
  19535. }
  19536. });
  19537. }
  19538. });
  19539. return this;
  19540. };
  19541. /*
  19542. * File: /js/ui/tree.js
  19543. */
  19544. /**
  19545. * @class elFinder folders tree
  19546. *
  19547. * @author Dmitry (dio) Levashov
  19548. **/
  19549. $.fn.elfindertree = function(fm, opts) {
  19550. var treeclass = fm.res('class', 'tree');
  19551. this.not('.'+treeclass).each(function() {
  19552. var c = 'class', mobile = fm.UA.Mobile,
  19553. /**
  19554. * Root directory class name
  19555. *
  19556. * @type String
  19557. */
  19558. root = fm.res(c, 'treeroot'),
  19559. /**
  19560. * Open root dir if not opened yet
  19561. *
  19562. * @type Boolean
  19563. */
  19564. openRoot = opts.openRootOnLoad,
  19565. /**
  19566. * Open current work dir if not opened yet
  19567. *
  19568. * @type Boolean
  19569. */
  19570. openCwd = opts.openCwdOnOpen,
  19571. /**
  19572. * Auto loading current directory parents and do expand their node
  19573. *
  19574. * @type Boolean
  19575. */
  19576. syncTree = openCwd || opts.syncTree,
  19577. /**
  19578. * Subtree class name
  19579. *
  19580. * @type String
  19581. */
  19582. subtree = fm.res(c, 'navsubtree'),
  19583. /**
  19584. * Directory class name
  19585. *
  19586. * @type String
  19587. */
  19588. navdir = fm.res(c, 'treedir'),
  19589. /**
  19590. * Directory CSS selector
  19591. *
  19592. * @type String
  19593. */
  19594. selNavdir = 'span.' + navdir,
  19595. /**
  19596. * Collapsed arrow class name
  19597. *
  19598. * @type String
  19599. */
  19600. collapsed = fm.res(c, 'navcollapse'),
  19601. /**
  19602. * Expanded arrow class name
  19603. *
  19604. * @type String
  19605. */
  19606. expanded = fm.res(c, 'navexpand'),
  19607. /**
  19608. * Class name to mark arrow for directory with already loaded children
  19609. *
  19610. * @type String
  19611. */
  19612. loaded = 'elfinder-subtree-loaded',
  19613. /**
  19614. * Class name to mark need subdirs request
  19615. *
  19616. * @type String
  19617. */
  19618. chksubdir = 'elfinder-subtree-chksubdir',
  19619. /**
  19620. * Arraw class name
  19621. *
  19622. * @type String
  19623. */
  19624. arrow = fm.res(c, 'navarrow'),
  19625. /**
  19626. * Current directory class name
  19627. *
  19628. * @type String
  19629. */
  19630. active = fm.res(c, 'active'),
  19631. /**
  19632. * Droppable dirs dropover class
  19633. *
  19634. * @type String
  19635. */
  19636. dropover = fm.res(c, 'adroppable'),
  19637. /**
  19638. * Hover class name
  19639. *
  19640. * @type String
  19641. */
  19642. hover = fm.res(c, 'hover'),
  19643. /**
  19644. * Disabled dir class name
  19645. *
  19646. * @type String
  19647. */
  19648. disabled = fm.res(c, 'disabled'),
  19649. /**
  19650. * Draggable dir class name
  19651. *
  19652. * @type String
  19653. */
  19654. draggable = fm.res(c, 'draggable'),
  19655. /**
  19656. * Droppable dir class name
  19657. *
  19658. * @type String
  19659. */
  19660. droppable = fm.res(c, 'droppable'),
  19661. /**
  19662. * root wrapper class
  19663. *
  19664. * @type String
  19665. */
  19666. wrapperRoot = 'elfinder-navbar-wrapper-root',
  19667. /**
  19668. * Un-disabled cmd `paste` volume's root wrapper class
  19669. *
  19670. * @type String
  19671. */
  19672. pastable = 'elfinder-navbar-wrapper-pastable',
  19673. /**
  19674. * Un-disabled cmd `upload` volume's root wrapper class
  19675. *
  19676. * @type String
  19677. */
  19678. uploadable = 'elfinder-navbar-wrapper-uploadable',
  19679. /**
  19680. * Is position x inside Navbar
  19681. *
  19682. * @param x Numbar
  19683. *
  19684. * @return
  19685. */
  19686. insideNavbar = function(x) {
  19687. var left = navbar.offset().left;
  19688. return left <= x && x <= left + navbar.width();
  19689. },
  19690. /**
  19691. * To call subdirs elements queue
  19692. *
  19693. * @type Object
  19694. */
  19695. subdirsQue = {},
  19696. /**
  19697. * To exec subdirs elements ids
  19698. *
  19699. */
  19700. subdirsExecQue = [],
  19701. /**
  19702. * Request subdirs to backend
  19703. *
  19704. * @param id String
  19705. *
  19706. * @return Deferred
  19707. */
  19708. subdirs = function(ids) {
  19709. var targets = [];
  19710. $.each(ids, function(i, id) {
  19711. subdirsQue[id] && targets.push(fm.navId2Hash(id));
  19712. delete subdirsQue[id];
  19713. });
  19714. if (targets.length) {
  19715. return fm.request({
  19716. data: {
  19717. cmd: 'subdirs',
  19718. targets: targets,
  19719. preventDefault : true
  19720. }
  19721. }).done(function(res) {
  19722. if (res && res.subdirs) {
  19723. $.each(res.subdirs, function(hash, subdirs) {
  19724. var elm = fm.navHash2Elm(hash);
  19725. elm.removeClass(chksubdir);
  19726. elm[subdirs? 'addClass' : 'removeClass'](collapsed);
  19727. });
  19728. }
  19729. });
  19730. }
  19731. },
  19732. subdirsJobRes = null,
  19733. /**
  19734. * To check target element is in window of subdirs
  19735. *
  19736. * @return void
  19737. */
  19738. checkSubdirs = function() {
  19739. var ids = Object.keys(subdirsQue);
  19740. if (ids.length) {
  19741. subdirsJobRes && subdirsJobRes._abort();
  19742. execSubdirsTm && clearTimeout(execSubdirsTm);
  19743. subdirsExecQue = [];
  19744. subdirsJobRes = fm.asyncJob(function(id) {
  19745. return fm.isInWindow($('#'+id))? id : null;
  19746. }, ids, { numPerOnce: 200 })
  19747. .done(function(arr) {
  19748. if (arr.length) {
  19749. subdirsExecQue = arr;
  19750. execSubdirs();
  19751. }
  19752. });
  19753. }
  19754. },
  19755. subdirsPending = 0,
  19756. execSubdirsTm,
  19757. /**
  19758. * Exec subdirs as batch request
  19759. *
  19760. * @return void
  19761. */
  19762. execSubdirs = function() {
  19763. var cnt = opts.subdirsMaxConn - subdirsPending,
  19764. atOnce = fm.maxTargets? Math.min(fm.maxTargets, opts.subdirsAtOnce) : opts.subdirsAtOnce,
  19765. i, ids;
  19766. execSubdirsTm && cancelAnimationFrame(execSubdirsTm);
  19767. if (subdirsExecQue.length) {
  19768. if (cnt > 0) {
  19769. for (i = 0; i < cnt; i++) {
  19770. if (subdirsExecQue.length) {
  19771. subdirsPending++;
  19772. subdirs(subdirsExecQue.splice(0, atOnce)).always(function() {
  19773. subdirsPending--;
  19774. execSubdirs();
  19775. });
  19776. }
  19777. }
  19778. } else {
  19779. execSubdirsTm = requestAnimationFrame(function() {
  19780. subdirsExecQue.length && execSubdirs();
  19781. });
  19782. }
  19783. }
  19784. },
  19785. drop = fm.droppable.drop,
  19786. /**
  19787. * Droppable options
  19788. *
  19789. * @type Object
  19790. */
  19791. droppableopts = $.extend(true, {}, fm.droppable, {
  19792. // show subfolders on dropover
  19793. over : function(e, ui) {
  19794. var dst = $(this),
  19795. helper = ui.helper,
  19796. cl = hover+' '+dropover,
  19797. hash, status;
  19798. e.stopPropagation();
  19799. helper.data('dropover', helper.data('dropover') + 1);
  19800. dst.data('dropover', true);
  19801. if (ui.helper.data('namespace') !== fm.namespace || ! fm.insideWorkzone(e.pageX, e.pageY)) {
  19802. dst.removeClass(cl);
  19803. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  19804. return;
  19805. }
  19806. if (! insideNavbar(e.clientX)) {
  19807. dst.removeClass(cl);
  19808. return;
  19809. }
  19810. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  19811. dst.addClass(hover);
  19812. if (dst.is('.'+collapsed+':not(.'+expanded+')')) {
  19813. dst.data('expandTimer', setTimeout(function() {
  19814. dst.is('.'+collapsed+'.'+hover) && dst.children('.'+arrow).trigger('click');
  19815. }, 500));
  19816. }
  19817. if (dst.is('.elfinder-ro,.elfinder-na')) {
  19818. dst.removeClass(dropover);
  19819. //helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  19820. return;
  19821. }
  19822. hash = fm.navId2Hash(dst.attr('id'));
  19823. dst.data('dropover', hash);
  19824. $.each(ui.helper.data('files'), function(i, h) {
  19825. if (h === hash || (fm.file(h).phash === hash && !ui.helper.hasClass('elfinder-drag-helper-plus'))) {
  19826. dst.removeClass(cl);
  19827. return false; // break $.each
  19828. }
  19829. });
  19830. if (helper.data('locked')) {
  19831. status = 'elfinder-drag-helper-plus';
  19832. } else {
  19833. status = 'elfinder-drag-helper-move';
  19834. if (e.shiftKey || e.ctrlKey || e.metaKey) {
  19835. status += ' elfinder-drag-helper-plus';
  19836. }
  19837. }
  19838. dst.hasClass(dropover) && helper.addClass(status);
  19839. requestAnimationFrame(function(){ dst.hasClass(dropover) && helper.addClass(status); });
  19840. },
  19841. out : function(e, ui) {
  19842. var dst = $(this),
  19843. helper = ui.helper;
  19844. e.stopPropagation();
  19845. if (insideNavbar(e.clientX)) {
  19846. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  19847. }
  19848. helper.data('dropover', Math.max(helper.data('dropover') - 1, 0));
  19849. dst.data('expandTimer') && clearTimeout(dst.data('expandTimer'));
  19850. dst.removeData('dropover')
  19851. .removeClass(hover+' '+dropover);
  19852. },
  19853. deactivate : function() {
  19854. $(this).removeData('dropover')
  19855. .removeClass(hover+' '+dropover);
  19856. },
  19857. drop : function(e, ui) {
  19858. insideNavbar(e.clientX) && drop.call(this, e, ui);
  19859. }
  19860. }),
  19861. spinner = $(fm.res('tpl', 'navspinner')),
  19862. /**
  19863. * Directory html template
  19864. *
  19865. * @type String
  19866. */
  19867. tpl = fm.res('tpl', 'navdir'),
  19868. /**
  19869. * Permissions marker html template
  19870. *
  19871. * @type String
  19872. */
  19873. ptpl = fm.res('tpl', 'perms'),
  19874. /**
  19875. * Lock marker html template
  19876. *
  19877. * @type String
  19878. */
  19879. ltpl = fm.res('tpl', 'lock'),
  19880. /**
  19881. * Symlink marker html template
  19882. *
  19883. * @type String
  19884. */
  19885. stpl = fm.res('tpl', 'symlink'),
  19886. /**
  19887. * Directory hashes that has more pages
  19888. *
  19889. * @type Object
  19890. */
  19891. hasMoreDirs = {},
  19892. /**
  19893. * Html template replacement methods
  19894. *
  19895. * @type Object
  19896. */
  19897. replace = {
  19898. id : function(dir) { return fm.navHash2Id(dir.hash); },
  19899. name : function(dir) { return fm.escape(dir.i18 || dir.name); },
  19900. cssclass : function(dir) {
  19901. var cname = (dir.phash && ! dir.isroot ? '' : root)+' '+navdir+' '+fm.perms2class(dir);
  19902. dir.dirs && !dir.link && (cname += ' ' + collapsed) && dir.dirs == -1 && (cname += ' ' + chksubdir);
  19903. opts.getClass && (cname += ' ' + opts.getClass(dir));
  19904. dir.csscls && (cname += ' ' + fm.escape(dir.csscls));
  19905. return cname;
  19906. },
  19907. title : function(dir) { return opts.attrTitle? (' title="' + fm.escape(fm.path(dir.hash, true) || dir.i18 || dir.name) + '"') : ''; },
  19908. root : function(dir) {
  19909. var cls = '';
  19910. if (!dir.phash || dir.isroot) {
  19911. cls += ' '+wrapperRoot;
  19912. if (!dir.disabled || dir.disabled.length < 1) {
  19913. cls += ' '+pastable+' '+uploadable;
  19914. } else {
  19915. if ($.inArray('paste', dir.disabled) === -1) {
  19916. cls += ' '+pastable;
  19917. }
  19918. if ($.inArray('upload', dir.disabled) === -1) {
  19919. cls += ' '+uploadable;
  19920. }
  19921. }
  19922. return cls;
  19923. } else {
  19924. return '';
  19925. }
  19926. },
  19927. permissions : function(dir) { return !dir.read || !dir.write ? ptpl : ''; },
  19928. symlink : function(dir) { return dir.alias ? stpl : ''; },
  19929. style : function(dir) { return dir.icon ? fm.getIconStyle(dir) : ''; }
  19930. },
  19931. /**
  19932. * Return html for given dir
  19933. *
  19934. * @param Object directory
  19935. * @return String
  19936. */
  19937. itemhtml = function(dir) {
  19938. return tpl.replace(/(?:\{([a-z]+)\})/ig, function(m, key) {
  19939. var res = replace[key] ? replace[key](dir) : (dir[key] || '');
  19940. if (key === 'id' && dir.dirs == -1) {
  19941. subdirsQue[res] = res;
  19942. }
  19943. return res;
  19944. });
  19945. },
  19946. /**
  19947. * Return only dirs from files list
  19948. *
  19949. * @param Array files list
  19950. * @param Boolean do check exists
  19951. * @return Array
  19952. */
  19953. filter = function(files, checkExists) {
  19954. return $.map(files || [], function(f) {
  19955. return (f.mime === 'directory' && (!checkExists || fm.navHash2Elm(f.hash).length)) ? f : null;
  19956. });
  19957. },
  19958. /**
  19959. * Find parent subtree for required directory
  19960. *
  19961. * @param String dir hash
  19962. * @return jQuery
  19963. */
  19964. findSubtree = function(hash) {
  19965. return hash ? fm.navHash2Elm(hash).next('.'+subtree) : tree;
  19966. },
  19967. /**
  19968. * Find directory (wrapper) in required node
  19969. * before which we can insert new directory
  19970. *
  19971. * @param jQuery parent directory
  19972. * @param Object new directory
  19973. * @return jQuery
  19974. */
  19975. findSibling = function(subtree, dir) {
  19976. var node = subtree.children(':first'),
  19977. info;
  19978. while (node.length) {
  19979. info = fm.file(fm.navId2Hash(node.children('[id]').attr('id')));
  19980. if ((info = fm.file(fm.navId2Hash(node.children('[id]').attr('id'))))
  19981. && compare(dir, info) < 0) {
  19982. return node;
  19983. }
  19984. node = node.next();
  19985. }
  19986. return subtree.children('button.elfinder-navbar-pager-next');
  19987. },
  19988. /**
  19989. * Add new dirs in tree
  19990. *
  19991. * @param Array dirs list
  19992. * @return void
  19993. */
  19994. updateTree = function(dirs) {
  19995. var length = dirs.length,
  19996. orphans = [],
  19997. i = length,
  19998. tgts = $(),
  19999. done = {},
  20000. cwd = fm.cwd(),
  20001. append = function(parent, dirs, start, direction) {
  20002. var hashes = {},
  20003. curStart = 0,
  20004. max = fm.newAPI? Math.min(10000, Math.max(10, opts.subTreeMax)) : 10000,
  20005. setHashes = function() {
  20006. hashes = {};
  20007. $.each(dirs, function(i, d) {
  20008. hashes[d.hash] = i;
  20009. });
  20010. },
  20011. change = function(mode) {
  20012. if (mode === 'prepare') {
  20013. $.each(dirs, function(i, d) {
  20014. d.node && parent.append(d.node.hide());
  20015. });
  20016. } else if (mode === 'done') {
  20017. $.each(dirs, function(i, d) {
  20018. d.node && d.node.detach().show();
  20019. });
  20020. }
  20021. },
  20022. update = function(e, data) {
  20023. var i, changed;
  20024. e.stopPropagation();
  20025. if (data.select) {
  20026. render(getStart(data.select));
  20027. return;
  20028. }
  20029. if (data.change) {
  20030. change(data.change);
  20031. return;
  20032. }
  20033. if (data.removed && data.removed.length) {
  20034. dirs = $.grep(dirs, function(d) {
  20035. if (data.removed.indexOf(d.hash) === -1) {
  20036. return true;
  20037. } else {
  20038. !changed && (changed = true);
  20039. return false;
  20040. }
  20041. });
  20042. }
  20043. if (data.added && data.added.length) {
  20044. dirs = dirs.concat($.grep(data.added, function(d) {
  20045. if (hashes[d.hash] === void(0)) {
  20046. !changed && (changed = true);
  20047. return true;
  20048. } else {
  20049. return false;
  20050. }
  20051. }));
  20052. }
  20053. if (changed) {
  20054. dirs.sort(compare);
  20055. setHashes();
  20056. render(curStart);
  20057. }
  20058. },
  20059. getStart = function(target) {
  20060. if (hashes[target] !== void(0)) {
  20061. return Math.floor(hashes[target] / max) * max;
  20062. }
  20063. return void(0);
  20064. },
  20065. target = fm.navId2Hash(parent.prev('[id]').attr('id')),
  20066. render = function(start, direction) {
  20067. var html = [],
  20068. nodes = {},
  20069. total, page, s, parts, prev, next, prevBtn, nextBtn;
  20070. delete hasMoreDirs[target];
  20071. curStart = start;
  20072. parent.off('update.'+fm.namespace, update);
  20073. if (dirs.length > max) {
  20074. parent.on('update.'+fm.namespace, update);
  20075. if (start === void(0)) {
  20076. s = 0;
  20077. setHashes();
  20078. start = getStart(cwd.hash);
  20079. if (start === void(0)) {
  20080. start = 0;
  20081. }
  20082. }
  20083. parts = dirs.slice(start, start + max);
  20084. hasMoreDirs[target] = parent;
  20085. prev = start? Math.max(-1, start - max) : -1;
  20086. next = (start + max >= dirs.length)? 0 : start + max;
  20087. total = Math.ceil(dirs.length/max);
  20088. page = Math.ceil(start/max);
  20089. }
  20090. $.each(parts || dirs, function(i, d) {
  20091. html.push(itemhtml(d));
  20092. if (d.node) {
  20093. nodes[d.hash] = d.node;
  20094. }
  20095. });
  20096. if (prev > -1) {
  20097. prevBtn = $('<button class="elfinder-navbar-pager elfinder-navbar-pager-prev"></button>')
  20098. .text(fm.i18n('btnPrevious', page, total))
  20099. .button({
  20100. icons: {
  20101. primary: "ui-icon-caret-1-n"
  20102. }
  20103. })
  20104. .on('click', function(e) {
  20105. e.preventDefault();
  20106. e.stopPropagation();
  20107. render(prev, 'up');
  20108. });
  20109. } else {
  20110. prevBtn = $();
  20111. }
  20112. if (next) {
  20113. nextBtn = $('<button class="elfinder-navbar-pager elfinder-navbar-pager-next"></button>')
  20114. .text(fm.i18n('btnNext', page + 2, total))
  20115. .button({
  20116. icons: {
  20117. primary: "ui-icon-caret-1-s"
  20118. }
  20119. })
  20120. .on('click', function(e) {
  20121. e.preventDefault();
  20122. e.stopPropagation();
  20123. render(next, 'down');
  20124. });
  20125. } else {
  20126. nextBtn = $();
  20127. }
  20128. detach();
  20129. parent.empty()[parts? 'addClass' : 'removeClass']('elfinder-navbar-hasmore').append(prevBtn, html.join(''), nextBtn);
  20130. $.each(nodes, function(h, n) {
  20131. fm.navHash2Elm(h).parent().replaceWith(n);
  20132. });
  20133. if (direction) {
  20134. autoScroll(fm.navHash2Id(parts[direction === 'up'? parts.length - 1 : 0].hash));
  20135. }
  20136. ! mobile && fm.lazy(function() { updateDroppable(null, parent); });
  20137. },
  20138. detach = function() {
  20139. $.each(parent.children('.elfinder-navbar-wrapper'), function(i, elm) {
  20140. var n = $(elm),
  20141. ch = n.children('[id]:first'),
  20142. h, c;
  20143. if (ch.hasClass(loaded)) {
  20144. h = fm.navId2Hash(ch.attr('id'));
  20145. if (h && (c = hashes[h]) !== void(0)) {
  20146. dirs[c].node = n.detach();
  20147. }
  20148. }
  20149. });
  20150. };
  20151. render();
  20152. },
  20153. dir, html, parent, sibling, init, atonce = {}, updates = [], base, node,
  20154. lastKey, lastNodes = {};
  20155. while (i--) {
  20156. dir = dirs[i];
  20157. if (done[dir.hash] || fm.navHash2Elm(dir.hash).length) {
  20158. continue;
  20159. }
  20160. done[dir.hash] = true;
  20161. if ((parent = findSubtree(dir.phash)).length) {
  20162. lastKey = dir.phash || 'treeroot';
  20163. if (typeof lastNodes[lastKey] === 'undefined') {
  20164. lastNodes[lastKey] = parent.children(':last');
  20165. }
  20166. init = !lastNodes[lastKey].length;
  20167. if (dir.phash && (init || parent.hasClass('elfinder-navbar-hasmore') || (sibling = findSibling(parent, dir)).length)) {
  20168. if (init) {
  20169. if (!atonce[dir.phash]) {
  20170. atonce[dir.phash] = [];
  20171. }
  20172. atonce[dir.phash].push(dir);
  20173. } else {
  20174. if (sibling) {
  20175. node = itemhtml(dir);
  20176. sibling.before(node);
  20177. ! mobile && (tgts = tgts.add(node));
  20178. } else {
  20179. updates.push(dir);
  20180. }
  20181. }
  20182. } else {
  20183. node = itemhtml(dir);
  20184. if (init) {
  20185. parent.prepend(node);
  20186. } else {
  20187. lastNodes[lastKey].after(node);
  20188. }
  20189. if (!dir.phash || dir.isroot) {
  20190. base = fm.navHash2Elm(dir.hash).parent();
  20191. }
  20192. ! mobile && updateDroppable(null, base);
  20193. }
  20194. } else {
  20195. orphans.push(dir);
  20196. }
  20197. }
  20198. // When init, html append at once
  20199. if (Object.keys(atonce).length){
  20200. $.each(atonce, function(p, dirs){
  20201. var parent = findSubtree(p),
  20202. html = [];
  20203. dirs.sort(compare);
  20204. append(parent, dirs);
  20205. });
  20206. }
  20207. if (updates.length) {
  20208. parent.trigger('update.' + fm.namespace, { added : updates });
  20209. }
  20210. if (orphans.length && orphans.length < length) {
  20211. updateTree(orphans);
  20212. return;
  20213. }
  20214. ! mobile && tgts.length && fm.lazy(function() { updateDroppable(tgts); });
  20215. },
  20216. /**
  20217. * sort function by dir.name
  20218. *
  20219. */
  20220. compare = function(dir1, dir2) {
  20221. if (! fm.sortAlsoTreeview) {
  20222. return fm.sortRules.name(dir1, dir2);
  20223. } else {
  20224. var asc = fm.sortOrder == 'asc',
  20225. type = fm.sortType,
  20226. rules = fm.sortRules,
  20227. res;
  20228. res = asc? rules[fm.sortType](dir1, dir2) : rules[fm.sortType](dir2, dir1);
  20229. return type !== 'name' && res === 0
  20230. ? res = asc ? rules.name(dir1, dir2) : rules.name(dir2, dir1)
  20231. : res;
  20232. }
  20233. },
  20234. /**
  20235. * Timer ID of autoScroll
  20236. *
  20237. * @type Integer
  20238. */
  20239. autoScrTm,
  20240. /**
  20241. * Auto scroll to cwd
  20242. *
  20243. * @return Object jQuery Deferred
  20244. */
  20245. autoScroll = function(target) {
  20246. var dfrd = $.Deferred(),
  20247. current, parent, top, treeH, bottom, tgtTop;
  20248. autoScrTm && clearTimeout(autoScrTm);
  20249. autoScrTm = setTimeout(function() {
  20250. current = $(document.getElementById((target || fm.navHash2Id(fm.cwd().hash))));
  20251. if (current.length) {
  20252. // expand parents directory
  20253. (openCwd? current : current.parent()).parents('.elfinder-navbar-wrapper').children('.'+loaded).addClass(expanded).next('.'+subtree).show();
  20254. parent = tree.parent().stop(false, true);
  20255. top = parent.offset().top;
  20256. treeH = parent.height();
  20257. bottom = top + treeH - current.outerHeight();
  20258. tgtTop = current.offset().top;
  20259. if (tgtTop < top || tgtTop > bottom) {
  20260. parent.animate({
  20261. scrollTop : parent.scrollTop() + tgtTop - top - treeH / 3
  20262. }, {
  20263. duration : opts.durations.autoScroll,
  20264. complete : function() { dfrd.resolve(); }
  20265. });
  20266. } else {
  20267. dfrd.resolve();
  20268. }
  20269. } else {
  20270. dfrd.reject();
  20271. }
  20272. }, 100);
  20273. return dfrd;
  20274. },
  20275. /**
  20276. * Get hashes array of items of the bottom of the leaf root back from the target
  20277. *
  20278. * @param Object elFinder item(directory) object
  20279. * @return Array hashes
  20280. */
  20281. getEnds = function(d) {
  20282. var cur = d || fm.cwd(),
  20283. res = cur.hash? [ cur.hash ] : [],
  20284. phash, root, dir;
  20285. root = fm.root(cur.hash);
  20286. dir = fm.file(root);
  20287. while (dir && (phash = dir.phash)) {
  20288. res.unshift(phash);
  20289. root = fm.root(phash);
  20290. dir = fm.file(root);
  20291. if (fm.navHash2Elm(dir.hash).hasClass(loaded)) {
  20292. break;
  20293. }
  20294. }
  20295. return res;
  20296. },
  20297. /**
  20298. * Select pages back in order to display the target
  20299. *
  20300. * @param Object elFinder item(directory) object
  20301. * @return Object jQuery node object of target node
  20302. */
  20303. selectPages = function(current) {
  20304. var cur = current || fm.cwd(),
  20305. curHash = cur.hash,
  20306. node = fm.navHash2Elm(curHash);
  20307. if (!node.length) {
  20308. while(cur && cur.phash) {
  20309. if (hasMoreDirs[cur.phash] && !fm.navHash2Elm(cur.hash).length) {
  20310. hasMoreDirs[cur.phash].trigger('update.'+fm.namespace, { select : cur.hash });
  20311. }
  20312. cur = fm.file(cur.phash);
  20313. }
  20314. node = fm.navHash2Elm(curHash);
  20315. }
  20316. return node;
  20317. },
  20318. /**
  20319. * Flag indicating that synchronization is currently in progress
  20320. *
  20321. * @type Boolean
  20322. */
  20323. syncing,
  20324. /**
  20325. * Mark current directory as active
  20326. * If current directory is not in tree - load it and its parents
  20327. *
  20328. * @param Array directory objects of cwd
  20329. * @param Boolean do auto scroll
  20330. * @return Object jQuery Deferred
  20331. */
  20332. sync = function(cwdDirs, aScr) {
  20333. var cwd = fm.cwd(),
  20334. cwdhash = cwd.hash,
  20335. autoScr = aScr === void(0)? syncTree : aScr,
  20336. loadParents = function(dir) {
  20337. var dfd = $.Deferred(),
  20338. reqs = [],
  20339. ends = getEnds(dir),
  20340. makeReq = function(cmd, h, until) {
  20341. var data = {
  20342. cmd : cmd,
  20343. target : h
  20344. };
  20345. if (until) {
  20346. data.until = until;
  20347. }
  20348. return fm.request({
  20349. data : data,
  20350. preventFail : true
  20351. });
  20352. },
  20353. baseHash, baseId;
  20354. reqs = $.map(ends, function(h) {
  20355. var d = fm.file(h),
  20356. isRoot = d? fm.isRoot(d) : false,
  20357. node = fm.navHash2Elm(h),
  20358. getPhash = function(h, dep) {
  20359. var d, ph,
  20360. depth = dep || 1;
  20361. ph = (d = fm.file(h))? d.phash : false;
  20362. if (ph && depth > 1) {
  20363. return getPhash(ph, --depth);
  20364. }
  20365. return ph;
  20366. },
  20367. until,
  20368. closest = (function() {
  20369. var phash = getPhash(h);
  20370. until = phash;
  20371. while (phash) {
  20372. if (fm.navHash2Elm(phash).hasClass(loaded)) {
  20373. break;
  20374. }
  20375. until = phash;
  20376. phash = getPhash(phash);
  20377. }
  20378. if (!phash) {
  20379. until = void(0);
  20380. phash = fm.root(h);
  20381. }
  20382. return phash;
  20383. })(),
  20384. cmd;
  20385. if (!node.hasClass(loaded) && (isRoot || !d || !fm.navHash2Elm(d.phash).hasClass(loaded))) {
  20386. if (isRoot || closest === getPhash(h) || closest === getPhash(h, 2)) {
  20387. until = void(0);
  20388. cmd = 'tree';
  20389. if (!isRoot) {
  20390. h = getPhash(h);
  20391. }
  20392. } else {
  20393. cmd = 'parents';
  20394. }
  20395. if (!baseHash) {
  20396. baseHash = (cmd === 'tree')? h : closest;
  20397. }
  20398. return makeReq(cmd, h, until);
  20399. }
  20400. return null;
  20401. });
  20402. if (reqs.length) {
  20403. selectPages(fm.file(baseHash));
  20404. baseId = fm.navHash2Id(baseHash);
  20405. autoScr && autoScroll(baseId);
  20406. baseNode = $('#'+baseId);
  20407. spinner = $(fm.res('tpl', 'navspinner')).insertBefore(baseNode.children('.'+arrow));
  20408. baseNode.removeClass(collapsed);
  20409. $.when.apply($, reqs)
  20410. .done(function() {
  20411. var res = {},data, treeDirs, dirs, argLen, i;
  20412. argLen = arguments.length;
  20413. if (argLen > 0) {
  20414. for (i = 0; i < argLen; i++) {
  20415. data = arguments[i].tree || [];
  20416. res[ends[i]] = Object.assign([], filter(data));
  20417. }
  20418. }
  20419. dfd.resolve(res);
  20420. })
  20421. .fail(function() {
  20422. dfd.reject();
  20423. });
  20424. return dfd;
  20425. } else {
  20426. return dfd.resolve();
  20427. }
  20428. },
  20429. done= function(res, dfrd) {
  20430. var open = function() {
  20431. if (openRoot && baseNode) {
  20432. findSubtree(baseNode.hash).show().prev(selNavdir).addClass(expanded);
  20433. openRoot = false;
  20434. }
  20435. if (autoScr) {
  20436. autoScroll().done(checkSubdirs);
  20437. } else {
  20438. checkSubdirs();
  20439. }
  20440. },
  20441. current;
  20442. if (res) {
  20443. $.each(res, function(endHash, dirs) {
  20444. dirs && updateTree(dirs);
  20445. selectPages(fm.file(endHash));
  20446. dirs && updateArrows(dirs, loaded);
  20447. });
  20448. }
  20449. if (cwdDirs) {
  20450. (fm.api < 2.1) && cwdDirs.push(cwd);
  20451. updateTree(cwdDirs);
  20452. }
  20453. // set current node
  20454. current = selectPages();
  20455. if (!current.hasClass(active)) {
  20456. tree.find(selNavdir+'.'+active).removeClass(active);
  20457. current.addClass(active);
  20458. }
  20459. // mark as loaded to cwd parents
  20460. current.parents('.elfinder-navbar-wrapper').children('.'+navdir).addClass(loaded);
  20461. if (res) {
  20462. fm.lazy(open).done(function() {
  20463. dfrd.resolve();
  20464. });
  20465. } else {
  20466. open();
  20467. dfrd.resolve();
  20468. }
  20469. },
  20470. rmSpinner = function(fail) {
  20471. if (baseNode) {
  20472. spinner.remove();
  20473. baseNode.addClass(collapsed + (fail? '' : (' ' + loaded)));
  20474. }
  20475. },
  20476. dfrd = $.Deferred(),
  20477. baseNode, spinner;
  20478. if (!fm.navHash2Elm(cwdhash).length) {
  20479. syncing = true;
  20480. loadParents()
  20481. .done(function(res) {
  20482. done(res, dfrd);
  20483. rmSpinner();
  20484. })
  20485. .fail(function() {
  20486. rmSpinner(true);
  20487. dfrd.reject();
  20488. })
  20489. .always(function() {
  20490. syncing = false;
  20491. });
  20492. } else {
  20493. done(void(0), dfrd);
  20494. }
  20495. // trigger 'treesync' with my $.Deferred
  20496. fm.trigger('treesync', dfrd);
  20497. return dfrd;
  20498. },
  20499. /**
  20500. * Make writable and not root dirs droppable
  20501. *
  20502. * @return void
  20503. */
  20504. updateDroppable = function(target, node) {
  20505. var limit = 100,
  20506. next;
  20507. if (!target) {
  20508. if (!node || node.closest('div.'+wrapperRoot).hasClass(uploadable)) {
  20509. (node || tree.find('div.'+uploadable)).find(selNavdir+':not(.elfinder-ro,.elfinder-na)').addClass('native-droppable');
  20510. }
  20511. if (!node || node.closest('div.'+wrapperRoot).hasClass(pastable)) {
  20512. target = (node || tree.find('div.'+pastable)).find(selNavdir+':not(.'+droppable+')');
  20513. } else {
  20514. target = $();
  20515. }
  20516. if (node) {
  20517. // check leaf roots
  20518. node.children('div.'+wrapperRoot).each(function() {
  20519. updateDroppable(null, $(this));
  20520. });
  20521. }
  20522. }
  20523. // make droppable on async
  20524. if (target.length) {
  20525. fm.asyncJob(function(elm) {
  20526. $(elm).droppable(droppableopts);
  20527. }, $.makeArray(target), {
  20528. interval : 20,
  20529. numPerOnce : 100
  20530. });
  20531. }
  20532. },
  20533. /**
  20534. * Check required folders for subfolders and update arrow classes
  20535. *
  20536. * @param Array folders to check
  20537. * @param String css class
  20538. * @return void
  20539. */
  20540. updateArrows = function(dirs, cls) {
  20541. var sel = cls == loaded
  20542. ? '.'+collapsed+':not(.'+loaded+')'
  20543. : ':not(.'+collapsed+')';
  20544. $.each(dirs, function(i, dir) {
  20545. fm.navHash2Elm(dir.phash).filter(sel)
  20546. .filter(function() { return $.grep($(this).next('.'+subtree).children(), function(n) {
  20547. return ($(n).children().hasClass(root))? false : true;
  20548. }).length > 0; })
  20549. .addClass(cls);
  20550. });
  20551. },
  20552. /**
  20553. * Navigation tree
  20554. *
  20555. * @type JQuery
  20556. */
  20557. tree = $(this).addClass(treeclass)
  20558. // make dirs draggable and toggle hover class
  20559. .on('mouseenter mouseleave', selNavdir, function(e) {
  20560. var enter = (e.type === 'mouseenter');
  20561. if (enter && scrolling) { return; }
  20562. var link = $(this),
  20563. hash, dir;
  20564. if (!link.hasClass(dropover+' '+disabled)) {
  20565. if (!mobile && enter && !link.data('dragRegisted') && !link.hasClass(root+' '+draggable+' elfinder-na elfinder-wo')) {
  20566. link.data('dragRegisted', true);
  20567. if (fm.isCommandEnabled('copy', (hash = fm.navId2Hash(link.attr('id'))))) {
  20568. link.draggable(fm.draggable);
  20569. }
  20570. }
  20571. link.toggleClass(hover, enter);
  20572. }
  20573. // update title attr if necessary
  20574. if (enter && opts.attrTitle) {
  20575. dir = fm.file(hash || fm.navId2Hash(link.attr('id')));
  20576. if (!dir.isroot && link.attr('title') === (dir.i18 || dir.name)) {
  20577. link.attr('title', fm.path(hash, true));
  20578. }
  20579. }
  20580. })
  20581. // native drag enter
  20582. .on('dragenter', selNavdir, function(e) {
  20583. if (e.originalEvent.dataTransfer) {
  20584. var dst = $(this);
  20585. dst.addClass(hover);
  20586. if (dst.is('.'+collapsed+':not(.'+expanded+')')) {
  20587. dst.data('expandTimer', setTimeout(function() {
  20588. dst.is('.'+collapsed+'.'+hover) && dst.children('.'+arrow).trigger('click');
  20589. }, 500));
  20590. }
  20591. }
  20592. })
  20593. // native drag leave
  20594. .on('dragleave', selNavdir, function(e) {
  20595. if (e.originalEvent.dataTransfer) {
  20596. var dst = $(this);
  20597. dst.data('expandTimer') && clearTimeout(dst.data('expandTimer'));
  20598. dst.removeClass(hover);
  20599. }
  20600. })
  20601. // open dir or open subfolders in tree
  20602. .on('click', selNavdir, function(e) {
  20603. var link = $(this),
  20604. hash = fm.navId2Hash(link.attr('id')),
  20605. file = fm.file(hash);
  20606. if (link.data('longtap')) {
  20607. link.removeData('longtap');
  20608. e.stopPropagation();
  20609. return;
  20610. }
  20611. if (!link.hasClass(active)) {
  20612. tree.find(selNavdir+'.'+active).removeClass(active);
  20613. link.addClass(active);
  20614. }
  20615. if (hash != fm.cwd().hash && !link.hasClass(disabled)) {
  20616. fm.exec('open', hash).done(function() {
  20617. fm.one('opendone', function() {
  20618. fm.select({selected: [hash], origin: 'navbar'});
  20619. });
  20620. });
  20621. } else {
  20622. if (link.hasClass(collapsed)) {
  20623. link.children('.'+arrow).trigger('click');
  20624. }
  20625. fm.select({selected: [hash], origin: 'navbar'});
  20626. }
  20627. })
  20628. // for touch device
  20629. .on('touchstart', selNavdir, function(e) {
  20630. if (e.originalEvent.touches.length > 1) {
  20631. return;
  20632. }
  20633. var evt = e.originalEvent,
  20634. p;
  20635. if (e.target.nodeName === 'INPUT') {
  20636. e.stopPropagation();
  20637. return;
  20638. }
  20639. p = $(this).addClass(hover)
  20640. .removeData('longtap')
  20641. .data('tmlongtap', setTimeout(function(e){
  20642. // long tap
  20643. p.data('longtap', true);
  20644. fm.trigger('contextmenu', {
  20645. 'type' : 'navbar',
  20646. 'targets' : [fm.navId2Hash(p.attr('id'))],
  20647. 'x' : evt.touches[0].pageX,
  20648. 'y' : evt.touches[0].pageY
  20649. });
  20650. }, 500));
  20651. })
  20652. .on('touchmove touchend', selNavdir, function(e) {
  20653. if (e.target.nodeName === 'INPUT') {
  20654. e.stopPropagation();
  20655. return;
  20656. }
  20657. clearTimeout($(this).data('tmlongtap'));
  20658. $(this).removeData('tmlongtap');
  20659. if (e.type == 'touchmove') {
  20660. $(this).removeClass(hover);
  20661. }
  20662. })
  20663. // toggle subfolders in tree
  20664. .on('click', selNavdir+'.'+collapsed+' .'+arrow, function(e) {
  20665. var arrow = $(this),
  20666. link = arrow.parent(selNavdir),
  20667. stree = link.next('.'+subtree),
  20668. dfrd = $.Deferred(),
  20669. slideTH = 30, cnt;
  20670. e.stopPropagation();
  20671. if (link.hasClass(loaded)) {
  20672. link.toggleClass(expanded);
  20673. fm.lazy(function() {
  20674. cnt = link.hasClass(expanded)? stree.children().length + stree.find('div.elfinder-navbar-subtree[style*=block]').children().length : stree.find('div:visible').length;
  20675. if (cnt > slideTH) {
  20676. stree.toggle();
  20677. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  20678. checkSubdirs();
  20679. } else {
  20680. stree.stop(true, true)[link.hasClass(expanded)? 'slideDown' : 'slideUp'](opts.durations.slideUpDown, function(){
  20681. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  20682. checkSubdirs();
  20683. });
  20684. }
  20685. }).always(function() {
  20686. dfrd.resolve();
  20687. });
  20688. } else {
  20689. spinner.insertBefore(arrow);
  20690. link.removeClass(collapsed);
  20691. fm.request({cmd : 'tree', target : fm.navId2Hash(link.attr('id'))})
  20692. .done(function(data) {
  20693. updateTree(Object.assign([], filter(data.tree)));
  20694. if (stree.children().length) {
  20695. link.addClass(collapsed+' '+expanded);
  20696. if (stree.children().length > slideTH) {
  20697. stree.show();
  20698. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  20699. checkSubdirs();
  20700. } else {
  20701. stree.stop(true, true).slideDown(opts.durations.slideUpDown, function(){
  20702. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  20703. checkSubdirs();
  20704. });
  20705. }
  20706. }
  20707. })
  20708. .always(function(data) {
  20709. spinner.remove();
  20710. link.addClass(loaded);
  20711. fm.one('treedone', function() {
  20712. dfrd.resolve();
  20713. });
  20714. });
  20715. }
  20716. arrow.data('dfrd', dfrd);
  20717. })
  20718. .on('contextmenu', selNavdir, function(e) {
  20719. var self = $(this);
  20720. // now dirname editing
  20721. if (self.find('input:text').length) {
  20722. e.stopPropagation();
  20723. return;
  20724. }
  20725. e.preventDefault();
  20726. if (!self.data('tmlongtap')) {
  20727. fm.trigger('contextmenu', {
  20728. 'type' : 'navbar',
  20729. 'targets' : [fm.navId2Hash($(this).attr('id'))],
  20730. 'x' : e.pageX,
  20731. 'y' : e.pageY
  20732. });
  20733. }
  20734. self.addClass('ui-state-hover');
  20735. fm.getUI('contextmenu').children().on('mouseenter', function() {
  20736. self.addClass('ui-state-hover');
  20737. });
  20738. fm.bind('closecontextmenu', function() {
  20739. self.removeClass('ui-state-hover');
  20740. });
  20741. })
  20742. .on('scrolltoview', selNavdir, function(e, data) {
  20743. var self = $(this);
  20744. autoScroll(self.attr('id')).done(function() {
  20745. if (!data || data.blink === 'undefined' || data.blink) {
  20746. fm.resources.blink(self, 'lookme');
  20747. }
  20748. });
  20749. })
  20750. // prepend fake dir
  20751. .on('create.'+fm.namespace, function(e, item) {
  20752. var pdir = findSubtree(item.phash),
  20753. lock = item.move || false,
  20754. dir = $(itemhtml(item)).addClass('elfinder-navbar-wrapper-tmp'),
  20755. selected = fm.selected();
  20756. lock && selected.length && fm.trigger('lockfiles', {files: selected});
  20757. pdir.prepend(dir);
  20758. }),
  20759. scrolling = false,
  20760. navbarScrTm,
  20761. // move tree into navbar
  20762. navbar = fm.getUI('navbar').append(tree).show().on('scroll', function() {
  20763. scrolling = true;
  20764. navbarScrTm && cancelAnimationFrame(navbarScrTm);
  20765. navbarScrTm = requestAnimationFrame(function() {
  20766. scrolling = false;
  20767. checkSubdirs();
  20768. });
  20769. }),
  20770. prevSortTreeview = fm.sortAlsoTreeview;
  20771. fm.open(function(e) {
  20772. var data = e.data,
  20773. dirs = filter(data.files),
  20774. contextmenu = fm.getUI('contextmenu');
  20775. data.init && tree.empty();
  20776. if (fm.UA.iOS) {
  20777. navbar.removeClass('overflow-scrolling-touch').addClass('overflow-scrolling-touch');
  20778. }
  20779. if (dirs.length) {
  20780. fm.lazy(function() {
  20781. if (!contextmenu.data('cmdMaps')) {
  20782. contextmenu.data('cmdMaps', {});
  20783. }
  20784. updateTree(dirs);
  20785. updateArrows(dirs, loaded);
  20786. sync(dirs);
  20787. });
  20788. } else {
  20789. sync();
  20790. }
  20791. })
  20792. // add new dirs
  20793. .add(function(e) {
  20794. var dirs = filter(e.data.added);
  20795. if (dirs.length) {
  20796. updateTree(dirs);
  20797. updateArrows(dirs, collapsed);
  20798. }
  20799. })
  20800. // update changed dirs
  20801. .change(function(e) {
  20802. // do ot perfome while syncing
  20803. if (syncing) {
  20804. return;
  20805. }
  20806. var dirs = filter(e.data.changed, true),
  20807. length = dirs.length,
  20808. l = length,
  20809. tgts = $(),
  20810. changed = {},
  20811. dir, phash, node, tmp, realParent, reqParent, realSibling, reqSibling, isExpanded, isLoaded, parent, subdirs;
  20812. $.each(hasMoreDirs, function(h, node) {
  20813. node.trigger('update.'+fm.namespace, { change: 'prepare' });
  20814. });
  20815. while (l--) {
  20816. dir = dirs[l];
  20817. phash = dir.phash;
  20818. if ((node = fm.navHash2Elm(dir.hash)).length) {
  20819. parent = node.parent();
  20820. if (phash) {
  20821. realParent = node.closest('.'+subtree);
  20822. reqParent = findSubtree(phash);
  20823. realSibling = node.parent().next();
  20824. reqSibling = findSibling(reqParent, dir);
  20825. if (!reqParent.length) {
  20826. continue;
  20827. }
  20828. if (reqParent[0] !== realParent[0] || realSibling.get(0) !== reqSibling.get(0)) {
  20829. reqSibling.length ? reqSibling.before(parent) : reqParent.append(parent);
  20830. }
  20831. }
  20832. isExpanded = node.hasClass(expanded);
  20833. isLoaded = node.hasClass(loaded);
  20834. tmp = $(itemhtml(dir));
  20835. node.replaceWith(tmp.children(selNavdir));
  20836. ! mobile && updateDroppable(null, parent);
  20837. if (dir.dirs
  20838. && (isExpanded || isLoaded)
  20839. && (node = fm.navHash2Elm(dir.hash))
  20840. && node.next('.'+subtree).children().length) {
  20841. isExpanded && node.addClass(expanded);
  20842. isLoaded && node.addClass(loaded);
  20843. }
  20844. subdirs |= dir.dirs == -1;
  20845. }
  20846. }
  20847. // to check subdirs
  20848. if (subdirs) {
  20849. checkSubdirs();
  20850. }
  20851. $.each(hasMoreDirs, function(h, node) {
  20852. node.trigger('update.'+fm.namespace, { change: 'done' });
  20853. });
  20854. length && sync(void(0), false);
  20855. })
  20856. // remove dirs
  20857. .remove(function(e) {
  20858. var dirs = e.data.removed,
  20859. l = dirs.length,
  20860. node, stree, removed;
  20861. $.each(hasMoreDirs, function(h, node) {
  20862. node.trigger('update.'+fm.namespace, { removed : dirs });
  20863. node.trigger('update.'+fm.namespace, { change: 'prepare' });
  20864. });
  20865. while (l--) {
  20866. if ((node = fm.navHash2Elm(dirs[l])).length) {
  20867. removed = true;
  20868. stree = node.closest('.'+subtree);
  20869. node.parent().detach();
  20870. if (!stree.children().length) {
  20871. stree.hide().prev(selNavdir).removeClass(collapsed+' '+expanded+' '+loaded);
  20872. }
  20873. }
  20874. }
  20875. removed && fm.getUI('navbar').children('.ui-resizable-handle').trigger('resize');
  20876. $.each(hasMoreDirs, function(h, node) {
  20877. node.trigger('update.'+fm.namespace, { change: 'done' });
  20878. });
  20879. })
  20880. // lock/unlock dirs while moving
  20881. .bind('lockfiles unlockfiles', function(e) {
  20882. var lock = e.type == 'lockfiles',
  20883. helperLocked = e.data.helper? e.data.helper.data('locked') : false,
  20884. act = (lock && !helperLocked) ? 'disable' : 'enable',
  20885. dirs = $.grep(e.data.files||[], function(h) {
  20886. var dir = fm.file(h);
  20887. return dir && dir.mime == 'directory' ? true : false;
  20888. });
  20889. $.each(dirs, function(i, hash) {
  20890. var dir = fm.navHash2Elm(hash);
  20891. if (dir.length && !helperLocked) {
  20892. dir.hasClass(draggable) && dir.draggable(act);
  20893. dir.hasClass(droppable) && dir.droppable(act);
  20894. dir[lock ? 'addClass' : 'removeClass'](disabled);
  20895. }
  20896. });
  20897. })
  20898. .bind('sortchange', function() {
  20899. if (fm.sortAlsoTreeview || prevSortTreeview !== fm.sortAlsoTreeview) {
  20900. var dirs,
  20901. ends = [],
  20902. endsMap = {},
  20903. endsVid = {},
  20904. topVid = '',
  20905. single = false,
  20906. current;
  20907. fm.lazy(function() {
  20908. dirs = filter(fm.files());
  20909. prevSortTreeview = fm.sortAlsoTreeview;
  20910. tree.empty();
  20911. // append volume roots at first
  20912. updateTree($.map(fm.roots, function(h) {
  20913. var dir = fm.file(h);
  20914. return dir && !dir.phash? dir : null;
  20915. }));
  20916. if (!Object.keys(hasMoreDirs).length) {
  20917. updateTree(dirs);
  20918. current = selectPages();
  20919. updateArrows(dirs, loaded);
  20920. } else {
  20921. ends = getEnds();
  20922. if (ends.length > 1) {
  20923. $.each(ends, function(i, end) {
  20924. var vid = fm.file(fm.root(end)).volumeid;
  20925. if (i === 0) {
  20926. topVid = vid;
  20927. }
  20928. endsVid[vid] = end;
  20929. endsMap[end] = [];
  20930. });
  20931. $.each(dirs, function(i, d) {
  20932. if (!d.volumeid) {
  20933. single = true;
  20934. return false;
  20935. }
  20936. endsMap[endsVid[d.volumeid] || endsVid[topVid]].push(d);
  20937. });
  20938. } else {
  20939. single = true;
  20940. }
  20941. if (single) {
  20942. $.each(ends, function(i, endHash) {
  20943. updateTree(dirs);
  20944. current = selectPages(fm.file(endHash));
  20945. updateArrows(dirs, loaded);
  20946. });
  20947. } else {
  20948. $.each(endsMap, function(endHash, dirs) {
  20949. updateTree(dirs);
  20950. current = selectPages(fm.file(endHash));
  20951. updateArrows(dirs, loaded);
  20952. });
  20953. }
  20954. }
  20955. sync();
  20956. }, 100);
  20957. }
  20958. });
  20959. });
  20960. return this;
  20961. };
  20962. /*
  20963. * File: /js/ui/uploadButton.js
  20964. */
  20965. /**
  20966. * @class elFinder toolbar's button tor upload file
  20967. *
  20968. * @author Dmitry (dio) Levashov
  20969. **/
  20970. $.fn.elfinderuploadbutton = function(cmd) {
  20971. return this.each(function() {
  20972. var fm = cmd.fm,
  20973. button = $(this).elfinderbutton(cmd)
  20974. .off('click'),
  20975. form = $('<form></form>').appendTo(button),
  20976. input = $('<input type="file" multiple="true" title="'+cmd.fm.i18n('selectForUpload')+'"/>')
  20977. .on('change', function() {
  20978. var _input = $(this);
  20979. if (_input.val()) {
  20980. fm.exec('upload', {input : _input.remove()[0]}, void(0), fm.cwd().hash);
  20981. input.clone(true).appendTo(form);
  20982. }
  20983. })
  20984. .on('dragover', function(e) {
  20985. e.originalEvent.dataTransfer.dropEffect = 'copy';
  20986. }),
  20987. tm;
  20988. form.append(input.clone(true));
  20989. cmd.change(function() {
  20990. tm && cancelAnimationFrame(tm);
  20991. tm = requestAnimationFrame(function() {
  20992. var toShow = cmd.disabled();
  20993. if (form.is('visible')) {
  20994. !toShow && form.hide();
  20995. } else {
  20996. toShow && form.show();
  20997. }
  20998. });
  20999. })
  21000. .change();
  21001. });
  21002. };
  21003. /*
  21004. * File: /js/ui/viewbutton.js
  21005. */
  21006. /**
  21007. * @class elFinder toolbar button to switch current directory view.
  21008. *
  21009. * @author Dmitry (dio) Levashov
  21010. **/
  21011. $.fn.elfinderviewbutton = function(cmd) {
  21012. return this.each(function() {
  21013. var button = $(this).elfinderbutton(cmd),
  21014. icon = button.children('.elfinder-button-icon'),
  21015. text = button.children('.elfinder-button-text'),
  21016. tm;
  21017. cmd.change(function() {
  21018. tm && cancelAnimationFrame(tm);
  21019. tm = requestAnimationFrame(function() {
  21020. var icons = cmd.value == 'icons';
  21021. icon.toggleClass('elfinder-button-icon-view-list', icons);
  21022. cmd.className = icons? 'view-list' : '';
  21023. cmd.title = cmd.fm.i18n(icons ? 'viewlist' : 'viewicons');
  21024. button.attr('title', cmd.title);
  21025. text.html(cmd.title);
  21026. });
  21027. });
  21028. });
  21029. };
  21030. /*
  21031. * File: /js/ui/workzone.js
  21032. */
  21033. /**
  21034. * @class elfinderworkzone - elFinder container for nav and current directory
  21035. * @author Dmitry (dio) Levashov
  21036. **/
  21037. $.fn.elfinderworkzone = function(fm) {
  21038. var cl = 'elfinder-workzone';
  21039. this.not('.'+cl).each(function() {
  21040. var wz = $(this).addClass(cl),
  21041. prevH = Math.round(wz.height()),
  21042. parent = wz.parent(),
  21043. setDelta = function() {
  21044. wdelta = wz.outerHeight(true) - wz.height();
  21045. },
  21046. fitsize = function(e) {
  21047. var height = parent.height() - wdelta,
  21048. style = parent.attr('style'),
  21049. curH = Math.round(wz.height());
  21050. if (e) {
  21051. e.preventDefault();
  21052. e.stopPropagation();
  21053. }
  21054. parent.css('overflow', 'hidden')
  21055. .children(':visible:not(.'+cl+')').each(function() {
  21056. var ch = $(this);
  21057. if (ch.css('position') != 'absolute' && ch.css('position') != 'fixed') {
  21058. height -= ch.outerHeight(true);
  21059. }
  21060. });
  21061. parent.attr('style', style || '');
  21062. height = Math.max(0, Math.round(height));
  21063. if (prevH !== height || curH !== height) {
  21064. prevH = Math.round(wz.height());
  21065. wz.height(height);
  21066. fm.trigger('wzresize');
  21067. }
  21068. },
  21069. cssloaded = function() {
  21070. wdelta = wz.outerHeight(true) - wz.height();
  21071. fitsize();
  21072. },
  21073. wdelta;
  21074. setDelta();
  21075. parent.on('resize.' + fm.namespace, fitsize);
  21076. fm.one('cssloaded', cssloaded)
  21077. .bind('uiresize', fitsize)
  21078. .bind('themechange', setDelta);
  21079. });
  21080. return this;
  21081. };
  21082. /*
  21083. * File: /js/commands/archive.js
  21084. */
  21085. /**
  21086. * @class elFinder command "archive"
  21087. * Archive selected files
  21088. *
  21089. * @author Dmitry (dio) Levashov
  21090. **/
  21091. elFinder.prototype.commands.archive = function() {
  21092. var self = this,
  21093. fm = self.fm,
  21094. mimes = [],
  21095. dfrd;
  21096. this.variants = [];
  21097. this.disableOnSearch = false;
  21098. this.nextAction = {};
  21099. /**
  21100. * Update mimes on open/reload
  21101. *
  21102. * @return void
  21103. **/
  21104. fm.bind('open reload', function() {
  21105. self.variants = [];
  21106. $.each((mimes = fm.option('archivers')['create'] || []), function(i, mime) {
  21107. self.variants.push([mime, fm.mime2kind(mime)]);
  21108. });
  21109. self.change();
  21110. });
  21111. this.getstate = function(select) {
  21112. var sel = this.files(select),
  21113. cnt = sel.length,
  21114. chk = (cnt && ! fm.isRoot(sel[0]) && (fm.file(sel[0].phash) || {}).write && ! $.grep(sel, function(f){ return f.read ? false : true; }).length),
  21115. cwdId;
  21116. if (chk && fm.searchStatus.state > 1) {
  21117. cwdId = fm.cwd().volumeid;
  21118. chk = (cnt === $.grep(sel, function(f) { return f.read && f.hash.indexOf(cwdId) === 0 ? true : false; }).length);
  21119. }
  21120. return chk && !this._disabled && mimes.length && (cnt || (dfrd && dfrd.state() == 'pending')) ? 0 : -1;
  21121. };
  21122. this.exec = function(hashes, type) {
  21123. var files = this.files(hashes),
  21124. cnt = files.length,
  21125. mime = type || mimes[0],
  21126. cwd = fm.file(files[0].phash) || null,
  21127. error = ['errArchive', 'errPerm', 'errCreatingTempDir', 'errFtpDownloadFile', 'errFtpUploadFile', 'errFtpMkdir', 'errArchiveExec', 'errExtractExec', 'errRm'],
  21128. i, open;
  21129. dfrd = $.Deferred().fail(function(error) {
  21130. error && fm.error(error);
  21131. });
  21132. if (! (cnt && mimes.length && $.inArray(mime, mimes) !== -1)) {
  21133. return dfrd.reject();
  21134. }
  21135. if (!cwd.write) {
  21136. return dfrd.reject(error);
  21137. }
  21138. for (i = 0; i < cnt; i++) {
  21139. if (!files[i].read) {
  21140. return dfrd.reject(error);
  21141. }
  21142. }
  21143. self.mime = mime;
  21144. self.prefix = ((cnt > 1)? 'Archive' : files[0].name) + (fm.option('archivers')['createext']? '.' + fm.option('archivers')['createext'][mime] : '');
  21145. self.data = {targets : self.hashes(hashes), type : mime};
  21146. if (fm.cwd().hash !== cwd.hash) {
  21147. open = fm.exec('open', cwd.hash).done(function() {
  21148. fm.one('cwdrender', function() {
  21149. fm.selectfiles({files : hashes});
  21150. dfrd = $.proxy(fm.res('mixin', 'make'), self)();
  21151. });
  21152. });
  21153. } else {
  21154. fm.selectfiles({files : hashes});
  21155. dfrd = $.proxy(fm.res('mixin', 'make'), self)();
  21156. }
  21157. return dfrd;
  21158. };
  21159. };
  21160. /*
  21161. * File: /js/commands/back.js
  21162. */
  21163. /**
  21164. * @class elFinder command "back"
  21165. * Open last visited folder
  21166. *
  21167. * @author Dmitry (dio) Levashov
  21168. **/
  21169. (elFinder.prototype.commands.back = function() {
  21170. this.alwaysEnabled = true;
  21171. this.updateOnSelect = false;
  21172. this.shortcuts = [{
  21173. pattern : 'ctrl+left backspace'
  21174. }];
  21175. this.getstate = function() {
  21176. return this.fm.history.canBack() ? 0 : -1;
  21177. };
  21178. this.exec = function() {
  21179. return this.fm.history.back();
  21180. };
  21181. }).prototype = { forceLoad : true }; // this is required command
  21182. /*
  21183. * File: /js/commands/chmod.js
  21184. */
  21185. /**
  21186. * @class elFinder command "chmod".
  21187. * Chmod files.
  21188. *
  21189. * @type elFinder.command
  21190. * @author Naoki Sawada
  21191. */
  21192. elFinder.prototype.commands.chmod = function() {
  21193. this.updateOnSelect = false;
  21194. var fm = this.fm,
  21195. level = {
  21196. 0 : 'owner',
  21197. 1 : 'group',
  21198. 2 : 'other'
  21199. },
  21200. msg = {
  21201. read : fm.i18n('read'),
  21202. write : fm.i18n('write'),
  21203. execute : fm.i18n('execute'),
  21204. perm : fm.i18n('perm'),
  21205. kind : fm.i18n('kind'),
  21206. files : fm.i18n('files')
  21207. },
  21208. isPerm = function(perm){
  21209. return (!isNaN(parseInt(perm, 8) && parseInt(perm, 8) <= 511) || perm.match(/^([r-][w-][x-]){3}$/i));
  21210. };
  21211. this.tpl = {
  21212. main : '<div class="ui-helper-clearfix elfinder-info-title"><span class="elfinder-cwd-icon {class} ui-corner-all"></span>{title}</div>'
  21213. +'{dataTable}',
  21214. itemTitle : '<strong>{name}</strong><span id="elfinder-info-kind">{kind}</span>',
  21215. groupTitle : '<strong>{items}: {num}</strong>',
  21216. dataTable : '<table id="{id}-table-perm"><tr><td>{0}</td><td>{1}</td><td>{2}</td></tr></table>'
  21217. +'<div class="">'+msg.perm+': <input class="elfinder-tabstop elfinder-focus" id="{id}-perm" type="text" size="4" maxlength="3" value="{value}"></div>',
  21218. fieldset : '<fieldset id="{id}-fieldset-{level}"><legend>{f_title}{name}</legend>'
  21219. +'<input type="checkbox" value="4" class="elfinder-tabstop" id="{id}-read-{level}-perm"{checked-r}> <label for="{id}-read-{level}-perm">'+msg.read+'</label><br>'
  21220. +'<input type="checkbox" value="6" class="elfinder-tabstop" id="{id}-write-{level}-perm"{checked-w}> <label for="{id}-write-{level}-perm">'+msg.write+'</label><br>'
  21221. +'<input type="checkbox" value="5" class="elfinder-tabstop" id="{id}-execute-{level}-perm"{checked-x}> <label for="{id}-execute-{level}-perm">'+msg.execute+'</label><br>'
  21222. };
  21223. this.shortcuts = [{
  21224. //pattern : 'ctrl+p'
  21225. }];
  21226. this.getstate = function(sel) {
  21227. var fm = this.fm;
  21228. sel = sel || fm.selected();
  21229. if (sel.length == 0) {
  21230. sel = [ fm.cwd().hash ];
  21231. }
  21232. return this.checkstate(this.files(sel)) ? 0 : -1;
  21233. };
  21234. this.checkstate = function(sel) {
  21235. var cnt = sel.length;
  21236. if (!cnt) return false;
  21237. var chk = $.grep(sel, function(f) {
  21238. return (f.isowner && f.perm && isPerm(f.perm) && (cnt == 1 || f.mime != 'directory')) ? true : false;
  21239. }).length;
  21240. return (cnt == chk)? true : false;
  21241. };
  21242. this.exec = function(select) {
  21243. var hashes = this.hashes(select),
  21244. files = this.files(hashes);
  21245. if (! files.length) {
  21246. hashes = [ this.fm.cwd().hash ];
  21247. files = this.files(hashes);
  21248. }
  21249. var fm = this.fm,
  21250. dfrd = $.Deferred().always(function() {
  21251. fm.enable();
  21252. }),
  21253. tpl = this.tpl,
  21254. cnt = files.length,
  21255. file = files[0],
  21256. id = fm.namespace + '-perm-' + file.hash,
  21257. view = tpl.main,
  21258. checked = ' checked="checked"',
  21259. buttons = function() {
  21260. var buttons = {};
  21261. buttons[fm.i18n('btnApply')] = save;
  21262. buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); };
  21263. return buttons;
  21264. },
  21265. save = function() {
  21266. var perm = $.trim($('#'+id+'-perm').val()),
  21267. reqData;
  21268. if (!isPerm(perm)) return false;
  21269. dialog.elfinderdialog('close');
  21270. reqData = {
  21271. cmd : 'chmod',
  21272. targets : hashes,
  21273. mode : perm
  21274. };
  21275. fm.request({
  21276. data : reqData,
  21277. notify : {type : 'chmod', cnt : cnt}
  21278. })
  21279. .fail(function(error) {
  21280. dfrd.reject(error);
  21281. })
  21282. .done(function(data) {
  21283. if (data.changed && data.changed.length) {
  21284. data.undo = {
  21285. cmd : 'chmod',
  21286. callback : function() {
  21287. var reqs = [];
  21288. $.each(prevVals, function(perm, hashes) {
  21289. reqs.push(fm.request({
  21290. data : {cmd : 'chmod', targets : hashes, mode : perm},
  21291. notify : {type : 'undo', cnt : hashes.length}
  21292. }));
  21293. });
  21294. return $.when.apply(null, reqs);
  21295. }
  21296. };
  21297. data.redo = {
  21298. cmd : 'chmod',
  21299. callback : function() {
  21300. return fm.request({
  21301. data : reqData,
  21302. notify : {type : 'redo', cnt : hashes.length}
  21303. });
  21304. }
  21305. };
  21306. }
  21307. dfrd.resolve(data);
  21308. });
  21309. },
  21310. setperm = function() {
  21311. var perm = '';
  21312. var _perm;
  21313. for (var i = 0; i < 3; i++){
  21314. _perm = 0;
  21315. if ($("#"+id+"-read-"+level[i]+'-perm').is(':checked')) {
  21316. _perm = (_perm | 4);
  21317. }
  21318. if ($("#"+id+"-write-"+level[i]+'-perm').is(':checked')) {
  21319. _perm = (_perm | 2);
  21320. }
  21321. if ($("#"+id+"-execute-"+level[i]+'-perm').is(':checked')) {
  21322. _perm = (_perm | 1);
  21323. }
  21324. perm += _perm.toString(8);
  21325. }
  21326. $('#'+id+'-perm').val(perm);
  21327. },
  21328. setcheck = function(perm) {
  21329. var _perm;
  21330. for (var i = 0; i < 3; i++){
  21331. _perm = parseInt(perm.slice(i, i+1), 8);
  21332. $("#"+id+"-read-"+level[i]+'-perm').prop("checked", false);
  21333. $("#"+id+"-write-"+level[i]+'-perm').prop("checked", false);
  21334. $("#"+id+"-execute-"+level[i]+'-perm').prop("checked", false);
  21335. if ((_perm & 4) == 4) {
  21336. $("#"+id+"-read-"+level[i]+'-perm').prop("checked", true);
  21337. }
  21338. if ((_perm & 2) == 2) {
  21339. $("#"+id+"-write-"+level[i]+'-perm').prop("checked", true);
  21340. }
  21341. if ((_perm & 1) == 1) {
  21342. $("#"+id+"-execute-"+level[i]+'-perm').prop("checked", true);
  21343. }
  21344. }
  21345. setperm();
  21346. },
  21347. makeperm = function(files) {
  21348. var perm = '777', ret = '', chk, _chk, _perm;
  21349. var len = files.length;
  21350. for (var i2 = 0; i2 < len; i2++) {
  21351. chk = getPerm(files[i2].perm);
  21352. if (! prevVals[chk]) {
  21353. prevVals[chk] = [];
  21354. }
  21355. prevVals[chk].push(files[i2].hash);
  21356. ret = '';
  21357. for (var i = 0; i < 3; i++){
  21358. _chk = parseInt(chk.slice(i, i+1), 8);
  21359. _perm = parseInt(perm.slice(i, i+1), 8);
  21360. if ((_chk & 4) != 4 && (_perm & 4) == 4) {
  21361. _perm -= 4;
  21362. }
  21363. if ((_chk & 2) != 2 && (_perm & 2) == 2) {
  21364. _perm -= 2;
  21365. }
  21366. if ((_chk & 1) != 1 && (_perm & 1) == 1) {
  21367. _perm -= 1;
  21368. }
  21369. ret += _perm.toString(8);
  21370. }
  21371. perm = ret;
  21372. }
  21373. return perm;
  21374. },
  21375. makeName = function(name) {
  21376. return name? ':'+name : '';
  21377. },
  21378. makeDataTable = function(perm, f) {
  21379. var _perm, fieldset;
  21380. var value = '';
  21381. var dataTable = tpl.dataTable;
  21382. for (var i = 0; i < 3; i++){
  21383. _perm = parseInt(perm.slice(i, i+1), 8);
  21384. value += _perm.toString(8);
  21385. fieldset = tpl.fieldset.replace('{f_title}', fm.i18n(level[i])).replace('{name}', makeName(f[level[i]])).replace(/\{level\}/g, level[i]);
  21386. dataTable = dataTable.replace('{'+i+'}', fieldset)
  21387. .replace('{checked-r}', ((_perm & 4) == 4)? checked : '')
  21388. .replace('{checked-w}', ((_perm & 2) == 2)? checked : '')
  21389. .replace('{checked-x}', ((_perm & 1) == 1)? checked : '');
  21390. }
  21391. dataTable = dataTable.replace('{value}', value).replace('{valueCaption}', msg['perm']);
  21392. return dataTable;
  21393. },
  21394. getPerm = function(perm){
  21395. if (isNaN(parseInt(perm, 8))) {
  21396. var mode_array = perm.split('');
  21397. var a = [];
  21398. for (var i = 0, l = mode_array.length; i < l; i++) {
  21399. if (i === 0 || i === 3 || i === 6) {
  21400. if (mode_array[i].match(/[r]/i)) {
  21401. a.push(1);
  21402. } else if (mode_array[i].match(/[-]/)) {
  21403. a.push(0);
  21404. }
  21405. } else if ( i === 1 || i === 4 || i === 7) {
  21406. if (mode_array[i].match(/[w]/i)) {
  21407. a.push(1);
  21408. } else if (mode_array[i].match(/[-]/)) {
  21409. a.push(0);
  21410. }
  21411. } else {
  21412. if (mode_array[i].match(/[x]/i)) {
  21413. a.push(1);
  21414. } else if (mode_array[i].match(/[-]/)) {
  21415. a.push(0);
  21416. }
  21417. }
  21418. }
  21419. a.splice(3, 0, ",");
  21420. a.splice(7, 0, ",");
  21421. var b = a.join("");
  21422. var b_array = b.split(",");
  21423. var c = [];
  21424. for (var j = 0, m = b_array.length; j < m; j++) {
  21425. var p = parseInt(b_array[j], 2).toString(8);
  21426. c.push(p);
  21427. }
  21428. perm = c.join('');
  21429. } else {
  21430. perm = parseInt(perm, 8).toString(8);
  21431. }
  21432. return perm;
  21433. },
  21434. opts = {
  21435. title : this.title,
  21436. width : 'auto',
  21437. buttons : buttons(),
  21438. close : function() { $(this).elfinderdialog('destroy'); }
  21439. },
  21440. dialog = fm.getUI().find('#'+id),
  21441. prevVals = {},
  21442. tmb = '', title, dataTable;
  21443. if (dialog.length) {
  21444. dialog.elfinderdialog('toTop');
  21445. return $.Deferred().resolve();
  21446. }
  21447. view = view.replace('{class}', cnt > 1 ? 'elfinder-cwd-icon-group' : fm.mime2class(file.mime));
  21448. if (cnt > 1) {
  21449. title = tpl.groupTitle.replace('{items}', fm.i18n('items')).replace('{num}', cnt);
  21450. } else {
  21451. title = tpl.itemTitle.replace('{name}', file.name).replace('{kind}', fm.mime2kind(file));
  21452. tmb = fm.tmb(file);
  21453. }
  21454. dataTable = makeDataTable(makeperm(files), files.length == 1? files[0] : {});
  21455. view = view.replace('{title}', title).replace('{dataTable}', dataTable).replace(/{id}/g, id);
  21456. dialog = this.fmDialog(view, opts);
  21457. dialog.attr('id', id);
  21458. // load thumbnail
  21459. if (tmb) {
  21460. $('<img/>')
  21461. .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  21462. .attr('src', tmb.url);
  21463. }
  21464. $('#' + id + '-table-perm :checkbox').on('click', function(){setperm('perm');});
  21465. $('#' + id + '-perm').on('keydown', function(e) {
  21466. var c = e.keyCode;
  21467. if (c == $.ui.keyCode.ENTER) {
  21468. e.stopPropagation();
  21469. save();
  21470. return;
  21471. }
  21472. }).on('focus', function(e){
  21473. $(this).trigger('select');
  21474. }).on('keyup', function(e) {
  21475. if ($(this).val().length == 3) {
  21476. $(this).trigger('select');
  21477. setcheck($(this).val());
  21478. }
  21479. });
  21480. return dfrd;
  21481. };
  21482. };
  21483. /*
  21484. * File: /js/commands/colwidth.js
  21485. */
  21486. /**
  21487. * @class elFinder command "colwidth"
  21488. * CWD list table columns width to auto
  21489. *
  21490. * @author Naoki Sawada
  21491. **/
  21492. elFinder.prototype.commands.colwidth = function() {
  21493. this.alwaysEnabled = true;
  21494. this.updateOnSelect = false;
  21495. this.getstate = function() {
  21496. return this.fm.getUI('cwd').find('table').css('table-layout') === 'fixed' ? 0 : -1;
  21497. };
  21498. this.exec = function() {
  21499. this.fm.getUI('cwd').trigger('colwidth');
  21500. return $.Deferred().resolve();
  21501. };
  21502. };
  21503. /*
  21504. * File: /js/commands/copy.js
  21505. */
  21506. /**
  21507. * @class elFinder command "copy".
  21508. * Put files in filemanager clipboard.
  21509. *
  21510. * @type elFinder.command
  21511. * @author Dmitry (dio) Levashov
  21512. */
  21513. elFinder.prototype.commands.copy = function() {
  21514. this.shortcuts = [{
  21515. pattern : 'ctrl+c ctrl+insert'
  21516. }];
  21517. this.getstate = function(select) {
  21518. var sel = this.files(select),
  21519. cnt = sel.length;
  21520. return cnt && $.grep(sel, function(f) { return f.read ? true : false; }).length == cnt ? 0 : -1;
  21521. };
  21522. this.exec = function(hashes) {
  21523. var fm = this.fm,
  21524. dfrd = $.Deferred()
  21525. .fail(function(error) {
  21526. fm.error(error);
  21527. });
  21528. $.each(this.files(hashes), function(i, file) {
  21529. if (! file.read) {
  21530. return !dfrd.reject(['errCopy', file.name, 'errPerm']);
  21531. }
  21532. });
  21533. return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes)));
  21534. };
  21535. };
  21536. /*
  21537. * File: /js/commands/cut.js
  21538. */
  21539. /**
  21540. * @class elFinder command "copy".
  21541. * Put files in filemanager clipboard.
  21542. *
  21543. * @type elFinder.command
  21544. * @author Dmitry (dio) Levashov
  21545. */
  21546. elFinder.prototype.commands.cut = function() {
  21547. var fm = this.fm;
  21548. this.shortcuts = [{
  21549. pattern : 'ctrl+x shift+insert'
  21550. }];
  21551. this.getstate = function(select) {
  21552. var sel = this.files(select),
  21553. cnt = sel.length;
  21554. return cnt && $.grep(sel, function(f) { return f.read && ! f.locked && ! fm.isRoot(f) ? true : false; }).length == cnt ? 0 : -1;
  21555. };
  21556. this.exec = function(hashes) {
  21557. var dfrd = $.Deferred()
  21558. .fail(function(error) {
  21559. fm.error(error);
  21560. });
  21561. $.each(this.files(hashes), function(i, file) {
  21562. if (!(file.read && ! file.locked && ! fm.isRoot(file)) ) {
  21563. return !dfrd.reject(['errCopy', file.name, 'errPerm']);
  21564. }
  21565. if (file.locked) {
  21566. return !dfrd.reject(['errLocked', file.name]);
  21567. }
  21568. });
  21569. return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes), true));
  21570. };
  21571. };
  21572. /*
  21573. * File: /js/commands/download.js
  21574. */
  21575. /**
  21576. * @class elFinder command "download".
  21577. * Download selected files.
  21578. * Only for new api
  21579. *
  21580. * @author Dmitry (dio) Levashov, dio@std42.ru
  21581. **/
  21582. elFinder.prototype.commands.zipdl = function() {};
  21583. elFinder.prototype.commands.download = function() {
  21584. var self = this,
  21585. fm = this.fm,
  21586. czipdl = null,
  21587. zipOn = false,
  21588. mixed = false,
  21589. dlntf = false,
  21590. cpath = window.location.pathname || '/',
  21591. filter = function(hashes, inExec) {
  21592. var volumeid, mixedCmd;
  21593. if (czipdl !== null) {
  21594. if (fm.searchStatus.state > 1) {
  21595. mixed = fm.searchStatus.mixed;
  21596. } else if (fm.leafRoots[fm.cwd().hash]) {
  21597. volumeid = fm.cwd().volumeid;
  21598. $.each(hashes, function(i, h) {
  21599. if (h.indexOf(volumeid) !== 0) {
  21600. mixed = true;
  21601. return false;
  21602. }
  21603. });
  21604. }
  21605. zipOn = (fm.isCommandEnabled('zipdl', hashes[0]));
  21606. }
  21607. if (mixed) {
  21608. mixedCmd = czipdl? 'zipdl' : 'download';
  21609. hashes = $.grep(hashes, function(h) {
  21610. var f = fm.file(h),
  21611. res = (! f || (! czipdl && f.mime === 'directory') || ! fm.isCommandEnabled(mixedCmd, h))? false : true;
  21612. if (f && inExec && ! res) {
  21613. fm.cwdHash2Elm(f.hash).trigger('unselect');
  21614. }
  21615. return res;
  21616. });
  21617. if (! hashes.length) {
  21618. return [];
  21619. }
  21620. } else {
  21621. if (!fm.isCommandEnabled('download', hashes[0])) {
  21622. return [];
  21623. }
  21624. }
  21625. return $.grep(self.files(hashes), function(f) {
  21626. var res = (! f.read || (! zipOn && f.mime == 'directory')) ? false : true;
  21627. if (inExec && ! res) {
  21628. fm.cwdHash2Elm(f.hash).trigger('unselect');
  21629. }
  21630. return res;
  21631. });
  21632. };
  21633. this.linkedCmds = ['zipdl'];
  21634. this.shortcuts = [{
  21635. pattern : 'shift+enter'
  21636. }];
  21637. this.getstate = function(select) {
  21638. var sel = this.hashes(select),
  21639. cnt = sel.length,
  21640. maxReq = this.options.maxRequests || 10,
  21641. mixed = false,
  21642. croot = '';
  21643. if (cnt < 1) {
  21644. return -1;
  21645. }
  21646. cnt = filter(sel).length;
  21647. return (cnt && (zipOn || (cnt <= maxReq && ((!fm.UA.IE && !fm.UA.Mobile) || cnt == 1))) ? 0 : -1);
  21648. };
  21649. fm.bind('contextmenu', function(e){
  21650. var fm = self.fm,
  21651. helper = null,
  21652. targets, file, link,
  21653. getExtra = function(file) {
  21654. var link = file.url || fm.url(file.hash);
  21655. return {
  21656. icon: 'link',
  21657. node: $('<a></a>')
  21658. .attr({href: link, target: '_blank', title: fm.i18n('link')})
  21659. .text(file.name)
  21660. .on('mousedown click touchstart touchmove touchend contextmenu', function(e){
  21661. e.stopPropagation();
  21662. })
  21663. .on('dragstart', function(e) {
  21664. var dt = e.dataTransfer || e.originalEvent.dataTransfer || null;
  21665. helper = null;
  21666. if (dt) {
  21667. var icon = function(f) {
  21668. var mime = f.mime, i, tmb = fm.tmb(f);
  21669. i = '<div class="elfinder-cwd-icon '+fm.mime2class(mime)+' ui-corner-all"></div>';
  21670. if (tmb) {
  21671. i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML;
  21672. }
  21673. return i;
  21674. };
  21675. dt.effectAllowed = 'copyLink';
  21676. if (dt.setDragImage) {
  21677. helper = $('<div class="elfinder-drag-helper html5-native">').append(icon(file)).appendTo($(document.body));
  21678. dt.setDragImage(helper.get(0), 50, 47);
  21679. }
  21680. if (!fm.UA.IE) {
  21681. dt.setData('elfinderfrom', window.location.href + file.phash);
  21682. dt.setData('elfinderfrom:' + dt.getData('elfinderfrom'), '');
  21683. }
  21684. }
  21685. })
  21686. .on('dragend', function(e) {
  21687. helper && helper.remove();
  21688. })
  21689. };
  21690. };
  21691. self.extra = null;
  21692. if (e.data) {
  21693. targets = e.data.targets || [];
  21694. if (targets.length === 1 && (file = fm.file(targets[0])) && file.mime !== 'directory') {
  21695. if (file.url != '1') {
  21696. self.extra = getExtra(file);
  21697. } else {
  21698. // Get URL ondemand
  21699. var node;
  21700. self.extra = {
  21701. icon: 'link',
  21702. node: $('<a></a>')
  21703. .attr({href: '#', title: fm.i18n('getLink'), draggable: 'false'})
  21704. .text(file.name)
  21705. .on('click touchstart', function(e){
  21706. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  21707. return;
  21708. }
  21709. var parent = node.parent();
  21710. e.stopPropagation();
  21711. e.preventDefault();
  21712. parent.removeClass('ui-state-disabled').addClass('elfinder-button-icon-spinner');
  21713. fm.request({
  21714. data : {cmd : 'url', target : file.hash},
  21715. preventDefault : true
  21716. })
  21717. .always(function(data) {
  21718. parent.removeClass('elfinder-button-icon-spinner');
  21719. if (data.url) {
  21720. var rfile = fm.file(file.hash);
  21721. rfile.url = data.url;
  21722. node.replaceWith(getExtra(file).node);
  21723. } else {
  21724. parent.addClass('ui-state-disabled');
  21725. }
  21726. });
  21727. })
  21728. };
  21729. node = self.extra.node;
  21730. node.ready(function(){
  21731. requestAnimationFrame(function(){
  21732. node.parent().addClass('ui-state-disabled').css('pointer-events', 'auto');
  21733. });
  21734. });
  21735. }
  21736. }
  21737. }
  21738. }).one('open', function() {
  21739. if (fm.api >= 2.1012) {
  21740. czipdl = fm.getCommand('zipdl');
  21741. }
  21742. dlntf = fm.cookieEnabled && fm.api > 2.1038 && !fm.isCORS;
  21743. });
  21744. this.exec = function(select) {
  21745. var hashes = this.hashes(select),
  21746. fm = this.fm,
  21747. base = fm.options.url,
  21748. files = filter(hashes, true),
  21749. dfrd = $.Deferred(),
  21750. iframes = '',
  21751. cdata = '',
  21752. targets = {},
  21753. i, url,
  21754. linkdl = false,
  21755. getTask = function(hashes) {
  21756. return function() {
  21757. var dfd = $.Deferred(),
  21758. root = fm.file(fm.root(hashes[0])),
  21759. single = (hashes.length === 1),
  21760. volName = root? (root.i18 || root.name) : null,
  21761. dir, dlName, phash;
  21762. if (single) {
  21763. if (dir = fm.file(hashes[0])) {
  21764. dlName = (dir.i18 || dir.name);
  21765. }
  21766. } else {
  21767. $.each(hashes, function() {
  21768. var d = fm.file(this);
  21769. if (d && (!phash || phash === d.phash)) {
  21770. phash = d.phash;
  21771. } else {
  21772. phash = null;
  21773. return false;
  21774. }
  21775. });
  21776. if (phash && (dir = fm.file(phash))) {
  21777. dlName = (dir.i18 || dir.name) + '-' + hashes.length;
  21778. }
  21779. }
  21780. if (dlName) {
  21781. volName = dlName;
  21782. }
  21783. volName && (volName = ' (' + volName + ')');
  21784. fm.request({
  21785. data : {cmd : 'zipdl', targets : hashes},
  21786. notify : {type : 'zipdl', cnt : 1, hideCnt : true, msg : fm.i18n('ntfzipdl') + volName},
  21787. cancel : true,
  21788. eachCancel : true,
  21789. preventDefault : true
  21790. }).done(function(e) {
  21791. var zipdl, dialog, btn = {}, dllink, form, iframe, m,
  21792. uniq = 'dlw' + (+new Date()),
  21793. zipdlFn = function(url) {
  21794. dllink = $('<a></a>')
  21795. .attr('href', url)
  21796. .attr('download', fm.escape(dlName))
  21797. .on('click', function() {
  21798. dfd.resolve();
  21799. dialog && dialog.elfinderdialog('destroy');
  21800. });
  21801. if (linkdl) {
  21802. dllink.attr('target', '_blank')
  21803. .append('<span class="elfinder-button-icon elfinder-button-icon-download"></span>'+fm.escape(dlName));
  21804. btn[fm.i18n('btnCancel')] = function() {
  21805. dialog.elfinderdialog('destroy');
  21806. };
  21807. dialog = self.fmDialog(dllink, {
  21808. title: fm.i18n('link'),
  21809. buttons: btn,
  21810. width: '200px',
  21811. destroyOnClose: true,
  21812. close: function() {
  21813. (dfd.state() !== 'resolved') && dfd.resolve();
  21814. }
  21815. });
  21816. } else {
  21817. click(dllink.hide().appendTo('body').get(0));
  21818. dllink.remove();
  21819. }
  21820. };
  21821. if (e.error) {
  21822. fm.error(e.error);
  21823. dfd.resolve();
  21824. } else if (e.zipdl) {
  21825. zipdl = e.zipdl;
  21826. if (dlName) {
  21827. m = fm.splitFileExtention(zipdl.name || '');
  21828. dlName += m[1]? ('.' + m[1]) : '.zip';
  21829. } else {
  21830. dlName = zipdl.name;
  21831. }
  21832. if (html5dl || linkdl) {
  21833. url = fm.options.url + (fm.options.url.indexOf('?') === -1 ? '?' : '&')
  21834. + 'cmd=zipdl&download=1';
  21835. $.each([hashes[0], zipdl.file, dlName, zipdl.mime], function(key, val) {
  21836. url += '&targets%5B%5D='+encodeURIComponent(val);
  21837. });
  21838. $.each(fm.customData, function(key, val) {
  21839. url += '&'+encodeURIComponent(key)+'='+encodeURIComponent(val);
  21840. });
  21841. url += '&'+encodeURIComponent(dlName);
  21842. if (fm.hasParrotHeaders()) {
  21843. fm.getBinaryByUrl({url: url}, function(blob) {
  21844. if (blob instanceof Blob) {
  21845. url = (window.URL || window.webkitURL).createObjectURL(blob);
  21846. zipdlFn(url);
  21847. } else {
  21848. fm.error(['errUploadTransfer', fm.i18n('kindZIP')]);
  21849. }
  21850. });
  21851. } else {
  21852. zipdlFn(url);
  21853. }
  21854. } else {
  21855. form = $('<form action="'+fm.options.url+'" method="post" target="'+uniq+'" style="display:none"></form>')
  21856. .append('<input type="hidden" name="cmd" value="zipdl"/>')
  21857. .append('<input type="hidden" name="download" value="1"/>');
  21858. $.each([hashes[0], zipdl.file, dlName, zipdl.mime], function(key, val) {
  21859. form.append('<input type="hidden" name="targets[]" value="'+fm.escape(val)+'"/>');
  21860. });
  21861. $.each(fm.customData, function(key, val) {
  21862. form.append('<input type="hidden" name="'+key+'" value="'+fm.escape(val)+'"/>');
  21863. });
  21864. form.attr('target', uniq).appendTo('body');
  21865. iframe = $('<iframe style="display:none" name="'+uniq+'">')
  21866. .appendTo('body')
  21867. .ready(function() {
  21868. form.submit().remove();
  21869. dfd.resolve();
  21870. setTimeout(function() {
  21871. iframe.remove();
  21872. }, 20000); // give 20 sec file to be saved
  21873. });
  21874. }
  21875. }
  21876. }).fail(function(error) {
  21877. error && fm.error(error);
  21878. dfd.resolve();
  21879. });
  21880. return dfd.promise();
  21881. };
  21882. },
  21883. // use MouseEvent to click element for Safari etc
  21884. click = function(a) {
  21885. var clickEv;
  21886. if (typeof MouseEvent === 'function') {
  21887. clickEv = new MouseEvent('click');
  21888. } else {
  21889. clickEv = document.createEvent('MouseEvents');
  21890. clickEv.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  21891. }
  21892. fm.pauseUnloadCheck(true);
  21893. a.dispatchEvent(clickEv);
  21894. },
  21895. checkCookie = function(id) {
  21896. var name = 'elfdl' + id,
  21897. parts;
  21898. parts = document.cookie.split(name + "=");
  21899. if (parts.length === 2) {
  21900. ntftm && clearTimeout(ntftm);
  21901. document.cookie = name + '=; path=' + cpath + '; max-age=0';
  21902. closeNotify();
  21903. } else {
  21904. setTimeout(function() { checkCookie(id); }, 200);
  21905. }
  21906. },
  21907. closeNotify = function() {
  21908. if (fm.ui.notify.children('.elfinder-notify-download').length) {
  21909. fm.notify({
  21910. type : 'download',
  21911. cnt : -1
  21912. });
  21913. }
  21914. },
  21915. reqids = [],
  21916. link, html5dl, fileCnt, clickEv, cid, ntftm, reqid, getUrlDfrd, urls;
  21917. if (!files.length) {
  21918. return dfrd.reject();
  21919. }
  21920. fileCnt = $.grep(files, function(f) { return f.mime === 'directory'? false : true; }).length;
  21921. link = $('<a>').hide().appendTo('body');
  21922. html5dl = (typeof link.get(0).download === 'string');
  21923. if (zipOn && (fileCnt !== files.length || fileCnt >= (this.options.minFilesZipdl || 1))) {
  21924. link.remove();
  21925. linkdl = (!html5dl && fm.UA.Mobile);
  21926. if (mixed) {
  21927. targets = {};
  21928. $.each(files, function(i, f) {
  21929. var p = f.hash.split('_', 2);
  21930. if (! targets[p[0]]) {
  21931. targets[p[0]] = [ f.hash ];
  21932. } else {
  21933. targets[p[0]].push(f.hash);
  21934. }
  21935. });
  21936. if (!linkdl && fm.UA.Mobile && Object.keys(targets).length > 1) {
  21937. linkdl = true;
  21938. }
  21939. } else {
  21940. targets = [ $.map(files, function(f) { return f.hash; }) ];
  21941. }
  21942. dfrd = fm.sequence($.map(targets, function(t) { return getTask(t); })).always(
  21943. function() {
  21944. fm.trigger('download', {files : files});
  21945. }
  21946. );
  21947. return dfrd;
  21948. } else {
  21949. reqids = [];
  21950. getUrlDfrd = $.Deferred().done(function(urls) {
  21951. for (i = 0; i < urls.length; i++) {
  21952. url = urls[i];
  21953. if (dlntf && url.substr(0, fm.options.url.length) === fm.options.url) {
  21954. reqid = fm.getRequestId();
  21955. reqids.push(reqid);
  21956. url += '&cpath=' + cpath + '&reqid=' + reqid;
  21957. ntftm = setTimeout(function() {
  21958. fm.notify({
  21959. type : 'download',
  21960. cnt : 1,
  21961. cancel : (fm.UA.IE || fm.UA.Edge)? void(0) : function() {
  21962. if (reqids.length) {
  21963. $.each(reqids, function() {
  21964. fm.request({
  21965. data: {
  21966. cmd: 'abort',
  21967. id: this
  21968. },
  21969. preventDefault: true
  21970. });
  21971. });
  21972. }
  21973. reqids = [];
  21974. }
  21975. });
  21976. }, fm.notifyDelay);
  21977. checkCookie(reqid);
  21978. }
  21979. if (html5dl) {
  21980. click(link.attr('href', url)
  21981. .attr('download', fm.escape(files[i].name))
  21982. .get(0)
  21983. );
  21984. } else {
  21985. if (fm.UA.Mobile) {
  21986. setTimeout(function(){
  21987. if (! window.open(url)) {
  21988. fm.error('errPopup');
  21989. ntftm && cleaerTimeout(ntftm);
  21990. closeNotify();
  21991. }
  21992. }, 100);
  21993. } else {
  21994. iframes += '<iframe class="downloader" id="downloader-' + files[i].hash+'" style="display:none" src="'+url+'"></iframe>';
  21995. }
  21996. }
  21997. }
  21998. link.remove();
  21999. $(iframes)
  22000. .appendTo('body')
  22001. .ready(function() {
  22002. setTimeout(function() {
  22003. $(iframes).each(function() {
  22004. $('#' + $(this).attr('id')).remove();
  22005. });
  22006. }, 20000 + (10000 * i)); // give 20 sec + 10 sec for each file to be saved
  22007. });
  22008. fm.trigger('download', {files : files});
  22009. dfrd.resolve();
  22010. });
  22011. fileCnt = files.length;
  22012. urls = [];
  22013. for (i = 0; i < files.length; i++) {
  22014. fm.openUrl(files[i].hash, true, function(v) {
  22015. v && urls.push(v);
  22016. if (--fileCnt < 1) {
  22017. getUrlDfrd.resolve(urls);
  22018. }
  22019. });
  22020. }
  22021. return dfrd;
  22022. }
  22023. };
  22024. };
  22025. /*
  22026. * File: /js/commands/duplicate.js
  22027. */
  22028. /**
  22029. * @class elFinder command "duplicate"
  22030. * Create file/folder copy with suffix "copy Number"
  22031. *
  22032. * @type elFinder.command
  22033. * @author Dmitry (dio) Levashov
  22034. */
  22035. elFinder.prototype.commands.duplicate = function() {
  22036. var fm = this.fm;
  22037. this.getstate = function(select) {
  22038. var sel = this.files(select),
  22039. cnt = sel.length;
  22040. return cnt && fm.cwd().write && $.grep(sel, function(f) { return f.read && f.phash === fm.cwd().hash && ! fm.isRoot(f)? true : false; }).length == cnt ? 0 : -1;
  22041. };
  22042. this.exec = function(hashes) {
  22043. var fm = this.fm,
  22044. files = this.files(hashes),
  22045. cnt = files.length,
  22046. dfrd = $.Deferred()
  22047. .fail(function(error) {
  22048. error && fm.error(error);
  22049. }),
  22050. args = [];
  22051. if (! cnt) {
  22052. return dfrd.reject();
  22053. }
  22054. $.each(files, function(i, file) {
  22055. if (!file.read || !fm.file(file.phash).write) {
  22056. return !dfrd.reject(['errCopy', file.name, 'errPerm']);
  22057. }
  22058. });
  22059. if (dfrd.state() == 'rejected') {
  22060. return dfrd;
  22061. }
  22062. return fm.request({
  22063. data : {cmd : 'duplicate', targets : this.hashes(hashes)},
  22064. notify : {type : 'copy', cnt : cnt},
  22065. navigate : {
  22066. toast : {
  22067. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmdduplicate')])}
  22068. }
  22069. }
  22070. });
  22071. };
  22072. };
  22073. /*
  22074. * File: /js/commands/edit.js
  22075. */
  22076. /**
  22077. * @class elFinder command "edit".
  22078. * Edit text file in dialog window
  22079. *
  22080. * @author Dmitry (dio) Levashov, dio@std42.ru
  22081. **/
  22082. elFinder.prototype.commands.edit = function() {
  22083. var self = this,
  22084. fm = this.fm,
  22085. clsEditing = fm.res('class', 'editing'),
  22086. mimesSingle = [],
  22087. mimes = [],
  22088. allowAll = false,
  22089. rtrim = function(str){
  22090. return str.replace(/\s+$/, '');
  22091. },
  22092. getEncSelect = function(heads) {
  22093. var sel = $('<select class="ui-corner-all"></select>'),
  22094. hval;
  22095. if (heads) {
  22096. $.each(heads, function(i, head) {
  22097. hval = fm.escape(head.value);
  22098. sel.append('<option value="'+hval+'">'+(head.caption? fm.escape(head.caption) : hval)+'</option>');
  22099. });
  22100. }
  22101. $.each(self.options.encodings, function(i, v) {
  22102. sel.append('<option value="'+v+'">'+v+'</option>');
  22103. });
  22104. return sel;
  22105. },
  22106. getDlgWidth = function() {
  22107. var win = fm.options.dialogContained? fm.getUI() : $(window),
  22108. m, width;
  22109. if (typeof self.options.dialogWidth === 'string' && (m = self.options.dialogWidth.match(/(\d+)%/))) {
  22110. width = parseInt(win.width() * (m[1] / 100));
  22111. } else {
  22112. width = parseInt(self.options.dialogWidth || 650);
  22113. }
  22114. return Math.min(width, win.width());
  22115. },
  22116. getDlgHeight = function() {
  22117. if (!self.options.dialogHeight) {
  22118. return void(0);
  22119. }
  22120. var win = fm.options.dialogContained? fm.getUI() : $(window),
  22121. m, height;
  22122. if (typeof self.options.dialogHeight === 'string' && (m = self.options.dialogHeight.match(/(\d+)%/))) {
  22123. height = parseInt(win.height() * (m[1] / 100));
  22124. } else {
  22125. height = parseInt(self.options.dialogHeight || win.height());
  22126. }
  22127. return Math.min(height, win.height());
  22128. },
  22129. /**
  22130. * Return files acceptable to edit
  22131. *
  22132. * @param Array files hashes
  22133. * @return Array
  22134. **/
  22135. filter = function(files) {
  22136. var cnt = files.length,
  22137. mime, ext, skip;
  22138. if (cnt > 1) {
  22139. mime = files[0].mime;
  22140. ext = files[0].name.replace(/^.*(\.[^.]+)$/, '$1');
  22141. }
  22142. return $.grep(files, function(file) {
  22143. var res;
  22144. if (skip || file.mime === 'directory') {
  22145. return false;
  22146. }
  22147. res = file.read
  22148. && (allowAll || fm.mimeIsText(file.mime) || $.inArray(file.mime, cnt === 1? mimesSingle : mimes) !== -1)
  22149. && (!self.onlyMimes.length || $.inArray(file.mime, self.onlyMimes) !== -1)
  22150. && (cnt === 1 || (file.mime === mime && file.name.substr(ext.length * -1) === ext))
  22151. && (fm.uploadMimeCheck(file.mime, file.phash)? true : false)
  22152. && setEditors(file, cnt)
  22153. && Object.keys(editors).length;
  22154. if (!res) {
  22155. skip = true;
  22156. }
  22157. return res;
  22158. });
  22159. },
  22160. fileSync = function(hash) {
  22161. var old = fm.file(hash),
  22162. f;
  22163. fm.request({
  22164. cmd: 'info',
  22165. targets: [hash],
  22166. preventDefault: true
  22167. }).done(function(data) {
  22168. var changed;
  22169. if (data && data.files && data.files.length) {
  22170. f = data.files[0];
  22171. if (old.ts != f.ts || old.size != f.size) {
  22172. changed = { changed: [ f ] };
  22173. fm.updateCache(changed);
  22174. fm.change(changed);
  22175. }
  22176. }
  22177. });
  22178. },
  22179. /**
  22180. * Open dialog with textarea to edit file
  22181. *
  22182. * @param String id dialog id
  22183. * @param Object file file object
  22184. * @param String content file content
  22185. * @return $.Deferred
  22186. **/
  22187. dialog = function(id, file, content, encoding, editor, toasts) {
  22188. var dfrd = $.Deferred(),
  22189. _loaded = false,
  22190. loaded = function() {
  22191. if (!_loaded) {
  22192. fm.toast({
  22193. mode: 'warning',
  22194. msg: fm.i18n('nowLoading')
  22195. });
  22196. return false;
  22197. }
  22198. return true;
  22199. },
  22200. makeToasts = function() {
  22201. // make toast message
  22202. if (toasts && Array.isArray(toasts)) {
  22203. $.each(toasts, function() {
  22204. this.msg && fm.toast(this);
  22205. });
  22206. }
  22207. },
  22208. save = function() {
  22209. var encord = selEncoding? selEncoding.val():void(0),
  22210. saveDfd = $.Deferred().fail(function(err) {
  22211. dialogNode.show().find('button.elfinder-btncnt-0,button.elfinder-btncnt-1').hide();
  22212. }),
  22213. conf, res, tm;
  22214. if (!loaded()) {
  22215. return saveDfd.resolve();
  22216. }
  22217. if (ta.editor) {
  22218. ta.editor.save(ta[0], ta.editor.instance);
  22219. conf = ta.editor.confObj;
  22220. if (conf.info && (conf.info.schemeContent || conf.info.arrayBufferContent)) {
  22221. encord = 'scheme';
  22222. }
  22223. }
  22224. res = getContent();
  22225. setOld(res);
  22226. if (res.promise) {
  22227. tm = setTimeout(function() {
  22228. fm.notify({
  22229. type : 'chkcontent',
  22230. cnt : 1,
  22231. hideCnt: true,
  22232. cancel : function() {
  22233. res.reject();
  22234. }
  22235. });
  22236. }, 100);
  22237. res.always(function() {
  22238. tm && clearTimeout(tm);
  22239. fm.notify({ type : 'chkcontent', cnt: -1 });
  22240. }).done(function(data) {
  22241. dfrd.notifyWith(ta, [encord, ta.data('hash'), old, saveDfd]);
  22242. }).fail(function(err) {
  22243. saveDfd.reject(err);
  22244. });
  22245. } else {
  22246. dfrd.notifyWith(ta, [encord, ta.data('hash'), old, saveDfd]);
  22247. }
  22248. return saveDfd;
  22249. },
  22250. saveon = function() {
  22251. if (!loaded()) { return; }
  22252. save().fail(function(err) {
  22253. err && fm.error(err);
  22254. });
  22255. },
  22256. cancel = function() {
  22257. ta.elfinderdialog('close');
  22258. },
  22259. savecl = function() {
  22260. if (!loaded()) { return; }
  22261. dialogNode.hide();
  22262. save().done(function() {
  22263. _loaded = false;
  22264. dialogNode.show();
  22265. cancel();
  22266. }).fail(function(err) {
  22267. dialogNode.show();
  22268. err && fm.error(err);
  22269. });
  22270. },
  22271. saveAs = function() {
  22272. if (!loaded()) { return; }
  22273. var prevOld = old,
  22274. phash = file.phash,
  22275. fail = function(err) {
  22276. dialogs.addClass(clsEditing).fadeIn(function() {
  22277. err && fm.error(err);
  22278. });
  22279. old = prevOld;
  22280. fm.disable();
  22281. },
  22282. make = function() {
  22283. self.mime = saveAsFile.mime || file.mime;
  22284. self.prefix = (saveAsFile.name || file.name).replace(/ \d+(\.[^.]+)?$/, '$1');
  22285. self.requestCmd = 'mkfile';
  22286. self.nextAction = {};
  22287. self.data = {target : phash};
  22288. $.proxy(fm.res('mixin', 'make'), self)()
  22289. .done(function(data) {
  22290. var oldHash;
  22291. if (data.added && data.added.length) {
  22292. oldHash = ta.data('hash');
  22293. ta.data('hash', data.added[0].hash);
  22294. save().done(function() {
  22295. _loaded = false;
  22296. dialogNode.show();
  22297. cancel();
  22298. dialogs.fadeIn();
  22299. }).fail(function() {
  22300. fm.exec('rm', [data.added[0].hash], { forceRm: true, quiet: true });
  22301. ta.data('hash', oldHash);
  22302. dialogNode.find('button.elfinder-btncnt-2').hide();
  22303. fail();
  22304. });
  22305. } else {
  22306. fail();
  22307. }
  22308. })
  22309. .progress(function(err) {
  22310. if (err && err === 'errUploadMime') {
  22311. ta.trigger('saveAsFail');
  22312. }
  22313. })
  22314. .fail(fail)
  22315. .always(function() {
  22316. delete self.mime;
  22317. delete self.prefix;
  22318. delete self.nextAction;
  22319. delete self.data;
  22320. });
  22321. fm.trigger('unselectfiles', { files: [ file.hash ] });
  22322. },
  22323. reqOpen = null,
  22324. reqInfo = null,
  22325. dialogs = fm.getUI().children('.' + self.dialogClass + ':visible');
  22326. if (dialogNode.is(':hidden')) {
  22327. dialogs = dialogs.add(dialogNode);
  22328. }
  22329. dialogs.removeClass(clsEditing).fadeOut();
  22330. fm.enable();
  22331. if (fm.searchStatus.state < 2 && phash !== fm.cwd().hash) {
  22332. reqOpen = fm.exec('open', [phash], {thash: phash});
  22333. } else if (!fm.file(phash)) {
  22334. reqInfo = fm.request({cmd: 'info', targets: [phash]});
  22335. }
  22336. $.when([reqOpen, reqInfo]).done(function() {
  22337. if (reqInfo) {
  22338. fm.one('infodone', function() {
  22339. fm.file(phash)? make() : fail('errFolderNotFound');
  22340. });
  22341. } else {
  22342. reqOpen? fm.one('cwdrender', make) : make();
  22343. }
  22344. }).fail(fail);
  22345. },
  22346. changed = function() {
  22347. var dfd = $.Deferred(),
  22348. res, tm;
  22349. if (!_loaded) {
  22350. return dfd.resolve(false);
  22351. }
  22352. ta.editor && ta.editor.save(ta[0], ta.editor.instance);
  22353. res = getContent();
  22354. if (res && res.promise) {
  22355. tm = setTimeout(function() {
  22356. fm.notify({
  22357. type : 'chkcontent',
  22358. cnt : 1,
  22359. hideCnt: true,
  22360. cancel : function() {
  22361. res.reject();
  22362. }
  22363. });
  22364. }, 100);
  22365. res.always(function() {
  22366. tm && clearTimeout(tm);
  22367. fm.notify({ type : 'chkcontent', cnt: -1 });
  22368. }).done(function(d) {
  22369. dfd.resolve(old !== d);
  22370. }).fail(function(err) {
  22371. dfd.resolve(err || (old === undefined? false : true));
  22372. });
  22373. } else {
  22374. dfd.resolve(old !== res);
  22375. }
  22376. return dfd;
  22377. },
  22378. opts = {
  22379. title : fm.escape(file.name),
  22380. width : getDlgWidth(),
  22381. height : getDlgHeight(),
  22382. buttons : {},
  22383. cssClass : clsEditing,
  22384. maxWidth : 'window',
  22385. maxHeight : 'window',
  22386. allowMinimize : true,
  22387. allowMaximize : true,
  22388. openMaximized : editorMaximized() || (editor && editor.info && editor.info.openMaximized),
  22389. btnHoverFocus : false,
  22390. closeOnEscape : false,
  22391. propagationEvents : ['mousemove', 'mouseup', 'click'],
  22392. minimize : function() {
  22393. var conf;
  22394. if (ta.editor && dialogNode.closest('.ui-dialog').is(':hidden')) {
  22395. conf = ta.editor.confObj;
  22396. if (conf.info && conf.info.syncInterval) {
  22397. fileSync(file.hash);
  22398. }
  22399. }
  22400. },
  22401. close : function() {
  22402. var close = function() {
  22403. var conf;
  22404. dfrd.resolve();
  22405. if (ta.editor) {
  22406. ta.editor.close(ta[0], ta.editor.instance);
  22407. conf = ta.editor.confObj;
  22408. if (conf.info && conf.info.syncInterval) {
  22409. fileSync(file.hash);
  22410. }
  22411. }
  22412. ta.elfinderdialog('destroy');
  22413. },
  22414. onlySaveAs = (typeof saveAsFile.name !== 'undefined'),
  22415. accept = onlySaveAs? {
  22416. label : 'btnSaveAs',
  22417. callback : function() {
  22418. requestAnimationFrame(saveAs);
  22419. }
  22420. } : {
  22421. label : 'btnSaveClose',
  22422. callback : function() {
  22423. save().done(function() {
  22424. close();
  22425. });
  22426. }
  22427. };
  22428. changed().done(function(change) {
  22429. var msgs = ['confirmNotSave'];
  22430. if (change) {
  22431. if (typeof change === 'string') {
  22432. msgs.unshift(change);
  22433. }
  22434. fm.confirm({
  22435. title : self.title,
  22436. text : msgs,
  22437. accept : accept,
  22438. cancel : {
  22439. label : 'btnClose',
  22440. callback : close
  22441. },
  22442. buttons : onlySaveAs? null : [{
  22443. label : 'btnSaveAs',
  22444. callback : function() {
  22445. requestAnimationFrame(saveAs);
  22446. }
  22447. }]
  22448. });
  22449. } else {
  22450. close();
  22451. }
  22452. });
  22453. },
  22454. open : function() {
  22455. var loadRes, conf, interval;
  22456. ta.initEditArea.call(ta, id, file, content, fm);
  22457. if (ta.editor) {
  22458. loadRes = ta.editor.load(ta[0]) || null;
  22459. if (loadRes && loadRes.done) {
  22460. loadRes.always(function() {
  22461. _loaded = true;
  22462. }).done(function(instance) {
  22463. ta.editor.instance = instance;
  22464. ta.editor.focus(ta[0], ta.editor.instance);
  22465. setOld(getContent());
  22466. requestAnimationFrame(function() {
  22467. dialogNode.trigger('resize');
  22468. });
  22469. }).fail(function(error) {
  22470. error && fm.error(error);
  22471. ta.elfinderdialog('destroy');
  22472. return;
  22473. }).always(makeToasts);
  22474. } else {
  22475. _loaded = true;
  22476. if (loadRes && (typeof loadRes === 'string' || Array.isArray(loadRes))) {
  22477. fm.error(loadRes);
  22478. ta.elfinderdialog('destroy');
  22479. return;
  22480. }
  22481. ta.editor.instance = loadRes;
  22482. ta.editor.focus(ta[0], ta.editor.instance);
  22483. setOld(getContent());
  22484. requestAnimationFrame(function() {
  22485. dialogNode.trigger('resize');
  22486. });
  22487. makeToasts();
  22488. }
  22489. conf = ta.editor.confObj;
  22490. if (conf.info && conf.info.syncInterval) {
  22491. if (interval = parseInt(conf.info.syncInterval)) {
  22492. setTimeout(function() {
  22493. autoSync(interval);
  22494. }, interval);
  22495. }
  22496. }
  22497. } else {
  22498. _loaded = true;
  22499. setOld(getContent());
  22500. }
  22501. },
  22502. resize : function(e, data) {
  22503. ta.editor && ta.editor.resize(ta[0], ta.editor.instance, e, data || {});
  22504. }
  22505. },
  22506. getContent = function() {
  22507. var res = ta.getContent.call(ta, ta[0]);
  22508. if (res === undefined || res === false || res === null) {
  22509. res = $.Deferred().reject();
  22510. }
  22511. return res;
  22512. },
  22513. setOld = function(res) {
  22514. if (res && res.promise) {
  22515. res.done(function(d) {
  22516. old = d;
  22517. });
  22518. } else {
  22519. old = res;
  22520. }
  22521. },
  22522. autoSync = function(interval) {
  22523. if (dialogNode.is(':visible')) {
  22524. fileSync(file.hash);
  22525. setTimeout(function() {
  22526. autoSync(interval);
  22527. }, interval);
  22528. }
  22529. },
  22530. stateChange = function() {
  22531. if (selEncoding) {
  22532. changed().done(function(change) {
  22533. if (change) {
  22534. selEncoding.attr('title', fm.i18n('saveAsEncoding')).addClass('elfinder-edit-changed');
  22535. } else {
  22536. selEncoding.attr('title', fm.i18n('openAsEncoding')).removeClass('elfinder-edit-changed');
  22537. }
  22538. });
  22539. }
  22540. },
  22541. saveAsFile = {},
  22542. ta, old, dialogNode, selEncoding, extEditor, maxW, syncInterval;
  22543. if (editor) {
  22544. if (editor.html) {
  22545. ta = $(editor.html);
  22546. }
  22547. extEditor = {
  22548. init : editor.init || null,
  22549. load : editor.load,
  22550. getContent : editor.getContent || null,
  22551. save : editor.save,
  22552. beforeclose : typeof editor.beforeclose == 'function' ? editor.beforeclose : void 0,
  22553. close : typeof editor.close == 'function' ? editor.close : function() {},
  22554. focus : typeof editor.focus == 'function' ? editor.focus : function() {},
  22555. resize : typeof editor.resize == 'function' ? editor.resize : function() {},
  22556. instance : null,
  22557. doSave : saveon,
  22558. doCancel : cancel,
  22559. doClose : savecl,
  22560. file : file,
  22561. fm : fm,
  22562. confObj : editor,
  22563. trigger : function(evName, data) {
  22564. fm.trigger('editEditor' + evName, Object.assign({}, editor.info || {}, data));
  22565. }
  22566. };
  22567. }
  22568. if (!ta) {
  22569. if (!fm.mimeIsText(file.mime)) {
  22570. return dfrd.reject('errEditorNotFound');
  22571. }
  22572. (function() {
  22573. ta = $('<textarea class="elfinder-file-edit" rows="20" id="'+id+'-ta"></textarea>')
  22574. .on('input propertychange', stateChange);
  22575. if (!editor || !editor.info || editor.info.useTextAreaEvent) {
  22576. ta.on('keydown', function(e) {
  22577. var code = e.keyCode,
  22578. value, start;
  22579. e.stopPropagation();
  22580. if (code == $.ui.keyCode.TAB) {
  22581. e.preventDefault();
  22582. // insert tab on tab press
  22583. if (this.setSelectionRange) {
  22584. value = this.value;
  22585. start = this.selectionStart;
  22586. this.value = value.substr(0, start) + "\t" + value.substr(this.selectionEnd);
  22587. start += 1;
  22588. this.setSelectionRange(start, start);
  22589. }
  22590. }
  22591. if (e.ctrlKey || e.metaKey) {
  22592. // close on ctrl+w/q
  22593. if (code == 'Q'.charCodeAt(0) || code == 'W'.charCodeAt(0)) {
  22594. e.preventDefault();
  22595. cancel();
  22596. }
  22597. if (code == 'S'.charCodeAt(0)) {
  22598. e.preventDefault();
  22599. saveon();
  22600. }
  22601. }
  22602. })
  22603. .on('mouseenter', function(){this.focus();});
  22604. }
  22605. ta.initEditArea = function(id, file, content) {
  22606. // ta.hide() for performance tune. Need ta.show() in `load()` if use textarea node.
  22607. ta.hide().val(content);
  22608. this._setupSelEncoding(content);
  22609. };
  22610. })();
  22611. }
  22612. // extended function to setup selector of encoding for text editor
  22613. ta._setupSelEncoding = function(content) {
  22614. var heads = (encoding && encoding !== 'unknown')? [{value: encoding}] : [],
  22615. wfake = $('<select></select>').hide(),
  22616. setSelW = function(init) {
  22617. init && wfake.appendTo(selEncoding.parent());
  22618. wfake.empty().append($('<option></option>').text(selEncoding.val()));
  22619. selEncoding.width(wfake.width());
  22620. };
  22621. if (content === '' || ! encoding || encoding !== 'UTF-8') {
  22622. heads.push({value: 'UTF-8'});
  22623. }
  22624. selEncoding = getEncSelect(heads).on('touchstart', function(e) {
  22625. // for touch punch event handler
  22626. e.stopPropagation();
  22627. }).on('change', function() {
  22628. // reload to change encoding if not edited
  22629. changed().done(function(change) {
  22630. if (! change && getContent() !== '') {
  22631. cancel();
  22632. edit(file, selEncoding.val(), editor).fail(function(err) { err && fm.error(err); });
  22633. }
  22634. });
  22635. setSelW();
  22636. }).on('mouseover', stateChange);
  22637. ta.parent().next().prepend($('<div class="ui-dialog-buttonset elfinder-edit-extras"></div>').append(selEncoding));
  22638. setSelW(true);
  22639. };
  22640. ta.data('hash', file.hash);
  22641. if (extEditor) {
  22642. ta.editor = extEditor;
  22643. if (typeof extEditor.beforeclose === 'function') {
  22644. opts.beforeclose = function() {
  22645. return extEditor.beforeclose(ta[0], extEditor.instance);
  22646. };
  22647. }
  22648. if (typeof extEditor.init === 'function') {
  22649. ta.initEditArea = extEditor.init;
  22650. }
  22651. if (typeof extEditor.getContent === 'function') {
  22652. ta.getContent = extEditor.getContent;
  22653. }
  22654. }
  22655. if (! ta.initEditArea) {
  22656. ta.initEditArea = function() {};
  22657. }
  22658. if (! ta.getContent) {
  22659. ta.getContent = function() {
  22660. return rtrim(ta.val());
  22661. };
  22662. }
  22663. if (!editor || !editor.info || !editor.info.preventGet) {
  22664. opts.buttons[fm.i18n('btnSave')] = saveon;
  22665. opts.buttons[fm.i18n('btnSaveClose')] = savecl;
  22666. opts.buttons[fm.i18n('btnSaveAs')] = saveAs;
  22667. opts.buttons[fm.i18n('btnCancel')] = cancel;
  22668. }
  22669. if (editor && typeof editor.prepare === 'function') {
  22670. editor.prepare(ta, opts, file);
  22671. }
  22672. dialogNode = self.fmDialog(ta, opts)
  22673. .attr('id', id)
  22674. .on('keydown keyup keypress', function(e) {
  22675. e.stopPropagation();
  22676. })
  22677. .css({ overflow: 'hidden', minHeight: '7em' })
  22678. .addClass('elfinder-edit-editor')
  22679. .closest('.ui-dialog')
  22680. .on('changeType', function(e, data) {
  22681. if (data.extention && data.mime) {
  22682. var ext = data.extention,
  22683. mime = data.mime,
  22684. btnSet = $(this).children('.ui-dialog-buttonpane').children('.ui-dialog-buttonset');
  22685. btnSet.children('.elfinder-btncnt-0,.elfinder-btncnt-1').hide();
  22686. saveAsFile.name = fm.splitFileExtention(file.name)[0] + '.' + data.extention;
  22687. saveAsFile.mime = data.mime;
  22688. if (!data.keepEditor) {
  22689. btnSet.children('.elfinder-btncnt-2').trigger('click');
  22690. }
  22691. }
  22692. });
  22693. // care to viewport scale change with mobile devices
  22694. maxW = (fm.options.dialogContained? fm.getUI() : $(window)).width();
  22695. (dialogNode.width() > maxW) && dialogNode.width(maxW);
  22696. return dfrd.promise();
  22697. },
  22698. /**
  22699. * Get file content and
  22700. * open dialog with textarea to edit file content
  22701. *
  22702. * @param String file hash
  22703. * @return jQuery.Deferred
  22704. **/
  22705. edit = function(file, convert, editor) {
  22706. var hash = file.hash,
  22707. opts = fm.options,
  22708. dfrd = $.Deferred(),
  22709. id = 'edit-'+fm.namespace+'-'+file.hash,
  22710. d = fm.getUI().find('#'+id),
  22711. conv = !convert? 0 : convert,
  22712. noContent = false,
  22713. req, error, res;
  22714. if (d.length) {
  22715. d.elfinderdialog('toTop');
  22716. return dfrd.resolve();
  22717. }
  22718. if (!file.read || (!file.write && (!editor.info || !editor.info.converter))) {
  22719. error = ['errOpen', file.name, 'errPerm'];
  22720. return dfrd.reject(error);
  22721. }
  22722. if (editor && editor.info) {
  22723. if (typeof editor.info.edit === 'function') {
  22724. res = editor.info.edit.call(fm, file, editor);
  22725. if (res.promise) {
  22726. res.done(function() {
  22727. dfrd.resolve();
  22728. }).fail(function(error) {
  22729. dfrd.reject(error);
  22730. });
  22731. } else {
  22732. res? dfrd.resolve() : dfrd.reject();
  22733. }
  22734. return dfrd;
  22735. }
  22736. noContent = editor.info.preventGet || editor.info.noContent;
  22737. if (editor.info.urlAsContent || noContent) {
  22738. req = $.Deferred();
  22739. if (editor.info.urlAsContent) {
  22740. fm.url(hash, { async: true, onetime: true, temporary: true }).done(function(url) {
  22741. req.resolve({content: url});
  22742. });
  22743. } else {
  22744. req.resolve({});
  22745. }
  22746. } else {
  22747. if (conv) {
  22748. file.encoding = conv;
  22749. fm.cache(file, 'change');
  22750. }
  22751. req = fm.request({
  22752. data : {cmd : 'get', target : hash, conv : conv, _t : file.ts},
  22753. options : {type: 'get', cache : true},
  22754. notify : {type : 'file', cnt : 1},
  22755. preventDefault : true
  22756. });
  22757. }
  22758. req.done(function(data) {
  22759. var selEncoding, reg, m, res;
  22760. if (data.doconv) {
  22761. fm.confirm({
  22762. title : self.title,
  22763. text : data.doconv === 'unknown'? 'confirmNonUTF8' : 'confirmConvUTF8',
  22764. accept : {
  22765. label : 'btnConv',
  22766. callback : function() {
  22767. dfrd = edit(file, selEncoding.val(), editor);
  22768. }
  22769. },
  22770. cancel : {
  22771. label : 'btnCancel',
  22772. callback : function() { dfrd.reject(); }
  22773. },
  22774. optionsCallback : function(options) {
  22775. options.create = function() {
  22776. var base = $('<div class="elfinder-dialog-confirm-encoding"></div>'),
  22777. head = {value: data.doconv},
  22778. detected;
  22779. if (data.doconv === 'unknown') {
  22780. head.caption = '-';
  22781. }
  22782. selEncoding = getEncSelect([head]);
  22783. $(this).next().find('.ui-dialog-buttonset')
  22784. .prepend(base.append($('<label>'+fm.i18n('encoding')+' </label>').append(selEncoding)));
  22785. };
  22786. }
  22787. });
  22788. } else {
  22789. if (!noContent && fm.mimeIsText(file.mime)) {
  22790. reg = new RegExp('^(data:'+file.mime.replace(/([.+])/g, '\\$1')+';base64,)', 'i');
  22791. if (!editor.info.dataScheme) {
  22792. if (window.atob && (m = data.content.match(reg))) {
  22793. data.content = atob(data.content.substr(m[1].length));
  22794. }
  22795. } else {
  22796. if (window.btoa && !data.content.match(reg)) {
  22797. data.content = 'data:'+file.mime+';base64,'+btoa(data.content);
  22798. }
  22799. }
  22800. }
  22801. dialog(id, file, data.content, data.encoding, editor, data.toasts)
  22802. .done(function(data) {
  22803. dfrd.resolve(data);
  22804. })
  22805. .progress(function(encoding, newHash, data, saveDfd) {
  22806. var ta = this;
  22807. if (newHash) {
  22808. hash = newHash;
  22809. }
  22810. fm.request({
  22811. options : {type : 'post'},
  22812. data : {
  22813. cmd : 'put',
  22814. target : hash,
  22815. encoding : encoding || data.encoding,
  22816. content : data
  22817. },
  22818. notify : {type : 'save', cnt : 1},
  22819. syncOnFail : true,
  22820. preventFail : true,
  22821. navigate : {
  22822. target : 'changed',
  22823. toast : {
  22824. inbuffer : {msg: fm.i18n(['complete', fm.i18n('btnSave')])}
  22825. }
  22826. }
  22827. })
  22828. .fail(function(error) {
  22829. dfrd.reject(error);
  22830. saveDfd.reject();
  22831. })
  22832. .done(function(data) {
  22833. requestAnimationFrame(function(){
  22834. ta.trigger('focus');
  22835. ta.editor && ta.editor.focus(ta[0], ta.editor.instance);
  22836. });
  22837. saveDfd.resolve();
  22838. });
  22839. })
  22840. .fail(function(error) {
  22841. dfrd.reject(error);
  22842. });
  22843. }
  22844. })
  22845. .fail(function(error) {
  22846. var err = fm.parseError(error);
  22847. err = Array.isArray(err)? err[0] : err;
  22848. if (file.encoding) {
  22849. file.encoding = '';
  22850. fm.cache(file, 'change');
  22851. }
  22852. (err !== 'errConvUTF8') && fm.sync();
  22853. dfrd.reject(error);
  22854. });
  22855. }
  22856. return dfrd.promise();
  22857. },
  22858. /**
  22859. * Current editors of selected files
  22860. *
  22861. * @type Object
  22862. */
  22863. editors = {},
  22864. /**
  22865. * Fallback editor (Simple text editor)
  22866. *
  22867. * @type Object
  22868. */
  22869. fallbackEditor = {
  22870. // Simple Text (basic textarea editor)
  22871. info : {
  22872. id : 'textarea',
  22873. name : 'TextArea',
  22874. useTextAreaEvent : true
  22875. },
  22876. load : function(textarea) {
  22877. // trigger event 'editEditorPrepare'
  22878. this.trigger('Prepare', {
  22879. node: textarea,
  22880. editorObj: void(0),
  22881. instance: void(0),
  22882. opts: {}
  22883. });
  22884. textarea.setSelectionRange && textarea.setSelectionRange(0, 0);
  22885. $(textarea).trigger('focus').show();
  22886. },
  22887. save : function(){}
  22888. },
  22889. /**
  22890. * Set current editors
  22891. *
  22892. * @param Object file object
  22893. * @param Number cnt count of selected items
  22894. * @return Void
  22895. */
  22896. setEditors = function(file, cnt) {
  22897. var mimeMatch = function(fileMime, editorMimes){
  22898. if (!editorMimes) {
  22899. return fm.mimeIsText(fileMime);
  22900. } else {
  22901. if (editorMimes[0] === '*' || $.inArray(fileMime, editorMimes) !== -1) {
  22902. return true;
  22903. }
  22904. var i, l;
  22905. l = editorMimes.length;
  22906. for (i = 0; i < l; i++) {
  22907. if (fileMime.indexOf(editorMimes[i]) === 0) {
  22908. return true;
  22909. }
  22910. }
  22911. return false;
  22912. }
  22913. },
  22914. extMatch = function(fileName, editorExts){
  22915. if (!editorExts || !editorExts.length) {
  22916. return true;
  22917. }
  22918. var ext = fileName.replace(/^.+\.([^.]+)|(.+)$/, '$1$2').toLowerCase(),
  22919. i, l;
  22920. l = editorExts.length;
  22921. for (i = 0; i < l; i++) {
  22922. if (ext === editorExts[i].toLowerCase()) {
  22923. return true;
  22924. }
  22925. }
  22926. return false;
  22927. },
  22928. optEditors = self.options.editors || [],
  22929. cwdWrite = fm.cwd().write;
  22930. stored = fm.storage('storedEditors') || {};
  22931. editors = {};
  22932. if (!optEditors.length) {
  22933. optEditors = [fallbackEditor];
  22934. }
  22935. $.each(optEditors, function(i, editor) {
  22936. var name;
  22937. if ((cnt === 1 || !editor.info.single)
  22938. && ((!editor.info || !editor.info.converter)? file.write : cwdWrite)
  22939. && (file.size > 0 || (!editor.info.converter && editor.info.canMakeEmpty !== false && fm.mimesCanMakeEmpty[file.mime]))
  22940. && (!editor.info.maxSize || file.size <= editor.info.maxSize)
  22941. && mimeMatch(file.mime, editor.mimes || null)
  22942. && extMatch(file.name, editor.exts || null)
  22943. && typeof editor.load == 'function'
  22944. && typeof editor.save == 'function') {
  22945. name = editor.info.name? editor.info.name : ('Editor ' + i);
  22946. editor.id = editor.info.id? editor.info.id : ('editor' + i),
  22947. editor.name = name;
  22948. editor.i18n = fm.i18n(name);
  22949. editors[editor.id] = editor;
  22950. }
  22951. });
  22952. return Object.keys(editors).length? true : false;
  22953. },
  22954. store = function(mime, editor) {
  22955. if (mime && editor) {
  22956. if (!$.isPlainObject(stored)) {
  22957. stored = {};
  22958. }
  22959. stored[mime] = editor.id;
  22960. fm.storage('storedEditors', stored);
  22961. fm.trigger('selectfiles', {files : fm.selected()});
  22962. }
  22963. },
  22964. useStoredEditor = function() {
  22965. var d = fm.storage('useStoredEditor');
  22966. return d? (d > 0) : self.options.useStoredEditor;
  22967. },
  22968. editorMaximized = function() {
  22969. var d = fm.storage('editorMaximized');
  22970. return d? (d > 0) : self.options.editorMaximized;
  22971. },
  22972. getSubMenuRaw = function(files, callback) {
  22973. var subMenuRaw = [];
  22974. $.each(editors, function(id, ed) {
  22975. subMenuRaw.push(
  22976. {
  22977. label : fm.escape(ed.i18n),
  22978. icon : ed.info && ed.info.icon? ed.info.icon : 'edit',
  22979. options : { iconImg: ed.info && ed.info.iconImg? fm.baseUrl + ed.info.iconImg : void(0) },
  22980. callback : function() {
  22981. store(files[0].mime, ed);
  22982. callback && callback.call(ed);
  22983. }
  22984. }
  22985. );
  22986. });
  22987. return subMenuRaw;
  22988. },
  22989. getStoreId = function(name) {
  22990. // for compatibility to previous version
  22991. return name.toLowerCase().replace(/ +/g, '');
  22992. },
  22993. getStoredEditor = function(mime) {
  22994. var name = stored[mime];
  22995. return name && Object.keys(editors).length? editors[getStoreId(name)] : void(0);
  22996. },
  22997. infoRequest = function() {
  22998. },
  22999. stored;
  23000. // make public method
  23001. this.getEncSelect = getEncSelect;
  23002. this.shortcuts = [{
  23003. pattern : 'ctrl+e'
  23004. }];
  23005. this.init = function() {
  23006. var self = this,
  23007. fm = this.fm,
  23008. opts = this.options,
  23009. cmdChecks = [],
  23010. ccData, dfd;
  23011. this.onlyMimes = this.options.mimes || [];
  23012. fm.one('open', function() {
  23013. // editors setup
  23014. if (opts.editors && Array.isArray(opts.editors)) {
  23015. fm.trigger('canMakeEmptyFile', {mimes: Object.keys(fm.storage('mkfileTextMimes') || {}).concat(opts.makeTextMimes || ['text/plain'])});
  23016. $.each(opts.editors, function(i, editor) {
  23017. if (editor.info && editor.info.cmdCheck) {
  23018. cmdChecks.push(editor.info.cmdCheck);
  23019. }
  23020. });
  23021. if (cmdChecks.length) {
  23022. if (fm.api >= 2.1030) {
  23023. dfd = fm.request({
  23024. data : {
  23025. cmd: 'editor',
  23026. name: cmdChecks,
  23027. method: 'enabled'
  23028. },
  23029. preventDefault : true
  23030. }).done(function(d) {
  23031. ccData = d;
  23032. }).fail(function() {
  23033. ccData = {};
  23034. });
  23035. } else {
  23036. ccData = {};
  23037. dfd = $.Deferred().resolve();
  23038. }
  23039. } else {
  23040. dfd = $.Deferred().resolve();
  23041. }
  23042. dfd.always(function() {
  23043. if (ccData) {
  23044. opts.editors = $.grep(opts.editors, function(e) {
  23045. if (e.info && e.info.cmdCheck) {
  23046. return ccData[e.info.cmdCheck]? true : false;
  23047. } else {
  23048. return true;
  23049. }
  23050. });
  23051. }
  23052. $.each(opts.editors, function(i, editor) {
  23053. if (editor.setup && typeof editor.setup === 'function') {
  23054. editor.setup.call(editor, opts, fm);
  23055. }
  23056. if (!editor.disabled) {
  23057. if (editor.mimes && Array.isArray(editor.mimes)) {
  23058. mimesSingle = mimesSingle.concat(editor.mimes);
  23059. if (!editor.info || !editor.info.single) {
  23060. mimes = mimes.concat(editor.mimes);
  23061. }
  23062. }
  23063. if (!allowAll && editor.mimes && editor.mimes[0] === '*') {
  23064. allowAll = true;
  23065. }
  23066. if (!editor.info) {
  23067. editor.info = {};
  23068. }
  23069. if (editor.info.integrate) {
  23070. fm.trigger('helpIntegration', Object.assign({cmd: 'edit'}, editor.info.integrate));
  23071. }
  23072. if (editor.info.canMakeEmpty) {
  23073. fm.trigger('canMakeEmptyFile', {mimes: Array.isArray(editor.info.canMakeEmpty)? editor.info.canMakeEmpty : editor.mimes});
  23074. }
  23075. }
  23076. });
  23077. mimesSingle = ($.uniqueSort || $.unique)(mimesSingle);
  23078. mimes = ($.uniqueSort || $.unique)(mimes);
  23079. opts.editors = $.grep(opts.editors, function(e) {
  23080. return e.disabled? false : true;
  23081. });
  23082. });
  23083. }
  23084. })
  23085. .bind('select', function() {
  23086. editors = null;
  23087. })
  23088. .bind('contextmenucreate', function(e) {
  23089. var file, editor,
  23090. single = function(editor) {
  23091. var title = self.title;
  23092. fm.one('contextmenucreatedone', function() {
  23093. self.title = title;
  23094. });
  23095. self.title = fm.escape(editor.i18n);
  23096. if (editor.info && editor.info.iconImg) {
  23097. self.contextmenuOpts = {
  23098. iconImg: fm.baseUrl + editor.info.iconImg
  23099. };
  23100. }
  23101. delete self.variants;
  23102. };
  23103. self.contextmenuOpts = void(0);
  23104. if (e.data.type === 'files' && self.enabled()) {
  23105. file = fm.file(e.data.targets[0]);
  23106. if (setEditors(file, e.data.targets.length)) {
  23107. if (Object.keys(editors).length > 1) {
  23108. if (!useStoredEditor() || !(editor = getStoredEditor(file.mime))) {
  23109. delete self.extra;
  23110. self.variants = [];
  23111. $.each(editors, function(id, editor) {
  23112. self.variants.push([{ editor: editor }, editor.i18n, editor.info && editor.info.iconImg? fm.baseUrl + editor.info.iconImg : 'edit']);
  23113. });
  23114. } else {
  23115. single(editor);
  23116. self.extra = {
  23117. icon: 'menu',
  23118. node: $('<span></span>')
  23119. .attr({title: fm.i18n('select')})
  23120. .on('click touchstart', function(e){
  23121. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  23122. return;
  23123. }
  23124. var node = $(this);
  23125. e.stopPropagation();
  23126. e.preventDefault();
  23127. fm.trigger('contextmenu', {
  23128. raw: getSubMenuRaw(fm.selectedFiles(), function() {
  23129. var hashes = fm.selected();
  23130. fm.exec('edit', hashes, {editor: this});
  23131. fm.trigger('selectfiles', {files : hashes});
  23132. }),
  23133. x: node.offset().left,
  23134. y: node.offset().top
  23135. });
  23136. })
  23137. };
  23138. }
  23139. } else {
  23140. single(editors[Object.keys(editors)[0]]);
  23141. delete self.extra;
  23142. }
  23143. }
  23144. }
  23145. })
  23146. .bind('canMakeEmptyFile', function(e) {
  23147. if (e.data && e.data.resetTexts) {
  23148. var defs = fm.arrayFlip(self.options.makeTextMimes || ['text/plain']),
  23149. hides = self.getMkfileHides();
  23150. $.each((fm.storage('mkfileTextMimes') || {}), function(mime, type) {
  23151. if (!defs[mime]) {
  23152. delete fm.mimesCanMakeEmpty[mime];
  23153. delete hides[mime];
  23154. }
  23155. });
  23156. fm.storage('mkfileTextMimes', null);
  23157. if (Object.keys(hides).length) {
  23158. fm.storage('mkfileHides', hides);
  23159. } else {
  23160. fm.storage('mkfileHides', null);
  23161. }
  23162. }
  23163. });
  23164. };
  23165. this.getstate = function(select) {
  23166. var sel = this.files(select),
  23167. cnt = sel.length;
  23168. return cnt && filter(sel).length == cnt ? 0 : -1;
  23169. };
  23170. this.exec = function(select, opts) {
  23171. var fm = this.fm,
  23172. files = filter(this.files(select)),
  23173. hashes = $.map(files, function(f) { return f.hash; }),
  23174. list = [],
  23175. editor = opts && opts.editor? opts.editor : null,
  23176. node = $(opts && opts._currentNode? opts._currentNode : fm.cwdHash2Elm(hashes[0])),
  23177. getEditor = function() {
  23178. var dfd = $.Deferred(),
  23179. storedId;
  23180. if (!editor && Object.keys(editors).length > 1) {
  23181. if (useStoredEditor() && (editor = getStoredEditor(files[0].mime))) {
  23182. return dfd.resolve(editor);
  23183. }
  23184. fm.trigger('contextmenu', {
  23185. raw: getSubMenuRaw(files, function() {
  23186. dfd.resolve(this);
  23187. }),
  23188. x: node.offset().left,
  23189. y: node.offset().top + 22,
  23190. opened: function() {
  23191. fm.one('closecontextmenu',function() {
  23192. requestAnimationFrame(function() {
  23193. if (dfd.state() === 'pending') {
  23194. dfd.reject();
  23195. }
  23196. });
  23197. });
  23198. }
  23199. });
  23200. fm.trigger('selectfiles', {files : hashes});
  23201. return dfd;
  23202. } else {
  23203. Object.keys(editors).length > 1 && editor && store(files[0].mime, editor);
  23204. return dfd.resolve(editor? editor : (Object.keys(editors).length? editors[Object.keys(editors)[0]] : null));
  23205. }
  23206. },
  23207. dfrd = $.Deferred(),
  23208. file;
  23209. if (editors === null) {
  23210. setEditors(files[0], hashes.length);
  23211. }
  23212. if (!node.length) {
  23213. node = fm.getUI('cwd');
  23214. }
  23215. getEditor().done(function(editor) {
  23216. while ((file = files.shift())) {
  23217. list.push(edit(file, (file.encoding || void(0)), editor).fail(function(error) {
  23218. error && fm.error(error);
  23219. }));
  23220. }
  23221. if (list.length) {
  23222. $.when.apply(null, list).done(function() {
  23223. dfrd.resolve();
  23224. }).fail(function() {
  23225. dfrd.reject();
  23226. });
  23227. } else {
  23228. dfrd.reject();
  23229. }
  23230. }).fail(function() {
  23231. dfrd.reject();
  23232. });
  23233. return dfrd;
  23234. };
  23235. this.getMkfileHides = function() {
  23236. return fm.storage('mkfileHides') || fm.arrayFlip(self.options.mkfileHideMimes || []);
  23237. };
  23238. };
  23239. /*
  23240. * File: /js/commands/empty.js
  23241. */
  23242. /**
  23243. * @class elFinder command "empty".
  23244. * Empty the folder
  23245. *
  23246. * @type elFinder.command
  23247. * @author Naoki Sawada
  23248. */
  23249. elFinder.prototype.commands.empty = function() {
  23250. var self, fm,
  23251. selFiles = function(select) {
  23252. var sel = self.files(select);
  23253. if (!sel.length) {
  23254. sel = [ fm.cwd() ];
  23255. }
  23256. return sel;
  23257. };
  23258. this.linkedCmds = ['rm'];
  23259. this.init = function() {
  23260. // lazy assign to make possible to become superclass
  23261. self = this;
  23262. fm = this.fm;
  23263. };
  23264. this.getstate = function(select) {
  23265. var sel = selFiles(select),
  23266. cnt;
  23267. cnt = sel.length;
  23268. return $.grep(sel, function(f) { return f.read && f.write && f.mime === 'directory' ? true : false; }).length == cnt ? 0 : -1;
  23269. };
  23270. this.exec = function(hashes) {
  23271. var dirs = selFiles(hashes),
  23272. cnt = dirs.length,
  23273. dfrd = $.Deferred()
  23274. .done(function() {
  23275. var data = {changed: {}};
  23276. fm.toast({msg: fm.i18n(['"'+success.join('", ')+'"', 'complete', fm.i18n('cmdempty')])});
  23277. $.each(dirs, function(i, dir) {
  23278. data.changed[dir.hash] = dir;
  23279. });
  23280. fm.change(data);
  23281. })
  23282. .always(function() {
  23283. var cwd = fm.cwd().hash;
  23284. fm.trigger('selectfiles', {files: $.map(dirs, function(d) { return cwd === d.phash? d.hash : null; })});
  23285. }),
  23286. success = [],
  23287. done = function(res) {
  23288. if (typeof res === 'number') {
  23289. success.push(dirs[res].name);
  23290. delete dirs[res].dirs;
  23291. } else {
  23292. res && fm.error(res);
  23293. }
  23294. (--cnt < 1) && dfrd[success.length? 'resolve' : 'reject']();
  23295. };
  23296. $.each(dirs, function(i, dir) {
  23297. var tm;
  23298. if (!(dir.write && dir.mime === 'directory')) {
  23299. done(['errEmpty', dir.name, 'errPerm']);
  23300. return null;
  23301. }
  23302. if (!fm.isCommandEnabled('rm', dir.hash)) {
  23303. done(['errCmdNoSupport', '"rm"']);
  23304. return null;
  23305. }
  23306. tm = setTimeout(function() {
  23307. fm.notify({type : 'search', cnt : 1, hideCnt : cnt > 1? false : true});
  23308. }, fm.notifyDelay);
  23309. fm.request({
  23310. data : {cmd : 'open', target : dir.hash},
  23311. preventDefault : true,
  23312. asNotOpen : true
  23313. }).done(function(data) {
  23314. var targets = [];
  23315. tm && clearTimeout(tm);
  23316. if (fm.ui.notify.children('.elfinder-notify-search').length) {
  23317. fm.notify({type : 'search', cnt : -1, hideCnt : cnt > 1? false : true});
  23318. }
  23319. if (data && data.files && data.files.length) {
  23320. if (data.files.length > fm.maxTargets) {
  23321. done(['errEmpty', dir.name, 'errMaxTargets', fm.maxTargets]);
  23322. } else {
  23323. fm.updateCache(data);
  23324. $.each(data.files, function(i, f) {
  23325. if (!f.write || f.locked) {
  23326. done(['errEmpty', dir.name, 'errRm', f.name, 'errPerm']);
  23327. targets = [];
  23328. return false;
  23329. }
  23330. targets.push(f.hash);
  23331. });
  23332. if (targets.length) {
  23333. fm.exec('rm', targets, { _userAction : true, addTexts : [ fm.i18n('folderToEmpty', dir.name) ] })
  23334. .fail(function(error) {
  23335. fm.trigger('unselectfiles', {files: fm.selected()});
  23336. done(fm.parseError(error) || '');
  23337. })
  23338. .done(function() { done(i); });
  23339. }
  23340. }
  23341. } else {
  23342. fm.toast({ mode: 'warning', msg: fm.i18n('filderIsEmpty', dir.name)});
  23343. done('');
  23344. }
  23345. }).fail(function(error) {
  23346. done(fm.parseError(error) || '');
  23347. });
  23348. });
  23349. return dfrd;
  23350. };
  23351. };
  23352. /*
  23353. * File: /js/commands/extract.js
  23354. */
  23355. /**
  23356. * @class elFinder command "extract"
  23357. * Extract files from archive
  23358. *
  23359. * @author Dmitry (dio) Levashov
  23360. **/
  23361. elFinder.prototype.commands.extract = function() {
  23362. var self = this,
  23363. fm = self.fm,
  23364. mimes = [],
  23365. filter = function(files) {
  23366. return $.grep(files, function(file) {
  23367. return file.read && $.inArray(file.mime, mimes) !== -1 ? true : false;
  23368. });
  23369. };
  23370. this.variants = [];
  23371. this.disableOnSearch = true;
  23372. // Update mimes list on open/reload
  23373. fm.bind('open reload', function() {
  23374. mimes = fm.option('archivers')['extract'] || [];
  23375. if (fm.api > 2) {
  23376. self.variants = [[{makedir: true}, fm.i18n('cmdmkdir')], [{}, fm.i18n('btnCwd')]];
  23377. } else {
  23378. self.variants = [[{}, fm.i18n('btnCwd')]];
  23379. }
  23380. self.change();
  23381. });
  23382. this.getstate = function(select) {
  23383. var sel = this.files(select),
  23384. cnt = sel.length;
  23385. return cnt && this.fm.cwd().write && filter(sel).length == cnt ? 0 : -1;
  23386. };
  23387. this.exec = function(hashes, opts) {
  23388. var files = this.files(hashes),
  23389. dfrd = $.Deferred(),
  23390. cnt = files.length,
  23391. makedir = opts && opts.makedir ? 1 : 0,
  23392. i, error,
  23393. decision;
  23394. var overwriteAll = false;
  23395. var omitAll = false;
  23396. var mkdirAll = 0;
  23397. var names = $.map(fm.files(hashes), function(file) { return file.name; });
  23398. var map = {};
  23399. $.grep(fm.files(hashes), function(file) {
  23400. map[file.name] = file;
  23401. return false;
  23402. });
  23403. var decide = function(decision) {
  23404. switch (decision) {
  23405. case 'overwrite_all' :
  23406. overwriteAll = true;
  23407. break;
  23408. case 'omit_all':
  23409. omitAll = true;
  23410. break;
  23411. }
  23412. };
  23413. var unpack = function(file) {
  23414. if (!(file.read && fm.file(file.phash).write)) {
  23415. error = ['errExtract', file.name, 'errPerm'];
  23416. fm.error(error);
  23417. dfrd.reject(error);
  23418. } else if ($.inArray(file.mime, mimes) === -1) {
  23419. error = ['errExtract', file.name, 'errNoArchive'];
  23420. fm.error(error);
  23421. dfrd.reject(error);
  23422. } else {
  23423. fm.request({
  23424. data:{cmd:'extract', target:file.hash, makedir:makedir},
  23425. notify:{type:'extract', cnt:1},
  23426. syncOnFail:true,
  23427. navigate:{
  23428. toast : makedir? {
  23429. incwd : {msg: fm.i18n(['complete', fm.i18n('cmdextract')]), action: {cmd: 'open', msg: 'cmdopen'}},
  23430. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmdextract')]), action: {cmd: 'open', msg: 'cmdopen'}}
  23431. } : {
  23432. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmdextract')])}
  23433. }
  23434. }
  23435. })
  23436. .fail(function (error) {
  23437. if (dfrd.state() != 'rejected') {
  23438. dfrd.reject(error);
  23439. }
  23440. })
  23441. .done(function () {
  23442. });
  23443. }
  23444. };
  23445. var confirm = function(files, index) {
  23446. var file = files[index],
  23447. name = fm.splitFileExtention(file.name)[0],
  23448. existed = ($.inArray(name, names) >= 0),
  23449. next = function(){
  23450. if((index+1) < cnt) {
  23451. confirm(files, index+1);
  23452. } else {
  23453. dfrd.resolve();
  23454. }
  23455. };
  23456. if (!makedir && existed && map[name].mime != 'directory') {
  23457. fm.confirm(
  23458. {
  23459. title : fm.i18n('ntfextract'),
  23460. text : ['errExists', name, 'confirmRepl'],
  23461. accept:{
  23462. label : 'btnYes',
  23463. callback:function (all) {
  23464. decision = all ? 'overwrite_all' : 'overwrite';
  23465. decide(decision);
  23466. if(!overwriteAll && !omitAll) {
  23467. if('overwrite' == decision) {
  23468. unpack(file);
  23469. }
  23470. if((index+1) < cnt) {
  23471. confirm(files, index+1);
  23472. } else {
  23473. dfrd.resolve();
  23474. }
  23475. } else if(overwriteAll) {
  23476. for (i = index; i < cnt; i++) {
  23477. unpack(files[i]);
  23478. }
  23479. dfrd.resolve();
  23480. }
  23481. }
  23482. },
  23483. reject : {
  23484. label : 'btnNo',
  23485. callback:function (all) {
  23486. decision = all ? 'omit_all' : 'omit';
  23487. decide(decision);
  23488. if(!overwriteAll && !omitAll && (index+1) < cnt) {
  23489. confirm(files, index+1);
  23490. } else if (omitAll) {
  23491. dfrd.resolve();
  23492. }
  23493. }
  23494. },
  23495. cancel : {
  23496. label : 'btnCancel',
  23497. callback:function () {
  23498. dfrd.resolve();
  23499. }
  23500. },
  23501. all : ((index+1) < cnt)
  23502. }
  23503. );
  23504. } else if (!makedir) {
  23505. if (mkdirAll == 0) {
  23506. fm.confirm({
  23507. title : fm.i18n('cmdextract'),
  23508. text : [fm.i18n('cmdextract')+' "'+file.name+'"', 'confirmRepl'],
  23509. accept:{
  23510. label : 'btnYes',
  23511. callback:function (all) {
  23512. all && (mkdirAll = 1);
  23513. unpack(file);
  23514. next();
  23515. }
  23516. },
  23517. reject : {
  23518. label : 'btnNo',
  23519. callback:function (all) {
  23520. all && (mkdirAll = -1);
  23521. next();
  23522. }
  23523. },
  23524. cancel : {
  23525. label : 'btnCancel',
  23526. callback:function () {
  23527. dfrd.resolve();
  23528. }
  23529. },
  23530. all : ((index+1) < cnt)
  23531. });
  23532. } else {
  23533. (mkdirAll > 0) && unpack(file);
  23534. next();
  23535. }
  23536. } else {
  23537. unpack(file);
  23538. next();
  23539. }
  23540. };
  23541. if (!(this.enabled() && cnt && mimes.length)) {
  23542. return dfrd.reject();
  23543. }
  23544. if(cnt > 0) {
  23545. confirm(files, 0);
  23546. }
  23547. return dfrd;
  23548. };
  23549. };
  23550. /*
  23551. * File: /js/commands/forward.js
  23552. */
  23553. /**
  23554. * @class elFinder command "forward"
  23555. * Open next visited folder
  23556. *
  23557. * @author Dmitry (dio) Levashov
  23558. **/
  23559. (elFinder.prototype.commands.forward = function() {
  23560. this.alwaysEnabled = true;
  23561. this.updateOnSelect = true;
  23562. this.shortcuts = [{
  23563. pattern : 'ctrl+right'
  23564. }];
  23565. this.getstate = function() {
  23566. return this.fm.history.canForward() ? 0 : -1;
  23567. };
  23568. this.exec = function() {
  23569. return this.fm.history.forward();
  23570. };
  23571. }).prototype = { forceLoad : true }; // this is required command
  23572. /*
  23573. * File: /js/commands/fullscreen.js
  23574. */
  23575. /**
  23576. * @class elFinder command "fullscreen"
  23577. * elFinder node to full scrren mode
  23578. *
  23579. * @author Naoki Sawada
  23580. **/
  23581. elFinder.prototype.commands.fullscreen = function() {
  23582. var self = this,
  23583. fm = this.fm,
  23584. update = function(e, data) {
  23585. e.preventDefault();
  23586. e.stopPropagation();
  23587. if (data && data.fullscreen) {
  23588. self.update(void(0), (data.fullscreen === 'on'));
  23589. }
  23590. };
  23591. this.alwaysEnabled = true;
  23592. this.updateOnSelect = false;
  23593. this.syncTitleOnChange = true;
  23594. this.value = false;
  23595. this.options = {
  23596. ui : 'fullscreenbutton'
  23597. };
  23598. this.getstate = function() {
  23599. return 0;
  23600. };
  23601. this.exec = function() {
  23602. var node = fm.getUI().get(0),
  23603. full = (node === fm.toggleFullscreen(node));
  23604. self.title = fm.i18n(full ? 'reinstate' : 'cmdfullscreen');
  23605. self.update(void(0), full);
  23606. return $.Deferred().resolve();
  23607. };
  23608. fm.bind('init', function() {
  23609. fm.getUI().off('resize.' + fm.namespace, update).on('resize.' + fm.namespace, update);
  23610. });
  23611. };
  23612. /*
  23613. * File: /js/commands/getfile.js
  23614. */
  23615. /**
  23616. * @class elFinder command "getfile".
  23617. * Return selected files info into outer callback.
  23618. * For use elFinder with wysiwyg editors etc.
  23619. *
  23620. * @author Dmitry (dio) Levashov, dio@std42.ru
  23621. **/
  23622. (elFinder.prototype.commands.getfile = function() {
  23623. var self = this,
  23624. fm = this.fm,
  23625. filter = function(files) {
  23626. var o = self.options;
  23627. files = $.grep(files, function(file) {
  23628. return (file.mime != 'directory' || o.folders) && file.read ? true : false;
  23629. });
  23630. return o.multiple || files.length == 1 ? files : [];
  23631. };
  23632. this.alwaysEnabled = true;
  23633. this.callback = fm.options.getFileCallback;
  23634. this._disabled = typeof(this.callback) == 'function';
  23635. this.getstate = function(select) {
  23636. var sel = this.files(select),
  23637. cnt = sel.length;
  23638. return this.callback && cnt && filter(sel).length == cnt ? 0 : -1;
  23639. };
  23640. this.exec = function(hashes) {
  23641. var fm = this.fm,
  23642. opts = this.options,
  23643. files = this.files(hashes),
  23644. cnt = files.length,
  23645. url = fm.option('url'),
  23646. tmb = fm.option('tmbUrl'),
  23647. dfrd = $.Deferred()
  23648. .done(function(data) {
  23649. var res,
  23650. done = function() {
  23651. if (opts.oncomplete == 'close') {
  23652. fm.hide();
  23653. } else if (opts.oncomplete == 'destroy') {
  23654. fm.destroy();
  23655. }
  23656. },
  23657. fail = function(error) {
  23658. if (opts.onerror == 'close') {
  23659. fm.hide();
  23660. } else if (opts.onerror == 'destroy') {
  23661. fm.destroy();
  23662. } else {
  23663. error && fm.error(error);
  23664. }
  23665. };
  23666. fm.trigger('getfile', {files : data});
  23667. try {
  23668. res = self.callback(data, fm);
  23669. } catch(e) {
  23670. fail(['Error in `getFileCallback`.', e.message]);
  23671. return;
  23672. }
  23673. if (typeof res === 'object' && typeof res.done === 'function') {
  23674. res.done(done).fail(fail);
  23675. } else {
  23676. done();
  23677. }
  23678. }),
  23679. result = function(file) {
  23680. return opts.onlyURL
  23681. ? opts.multiple ? $.map(files, function(f) { return f.url; }) : files[0].url
  23682. : opts.multiple ? files : files[0];
  23683. },
  23684. req = [],
  23685. i, file, dim;
  23686. for (i = 0; i < cnt; i++) {
  23687. file = files[i];
  23688. if (file.mime == 'directory' && !opts.folders) {
  23689. return dfrd.reject();
  23690. }
  23691. file.baseUrl = url;
  23692. if (file.url == '1') {
  23693. req.push(fm.request({
  23694. data : {cmd : 'url', target : file.hash},
  23695. notify : {type : 'url', cnt : 1, hideCnt : true},
  23696. preventDefault : true
  23697. })
  23698. .done(function(data) {
  23699. if (data.url) {
  23700. var rfile = fm.file(this.hash);
  23701. rfile.url = this.url = data.url;
  23702. }
  23703. }.bind(file)));
  23704. } else {
  23705. file.url = fm.url(file.hash);
  23706. }
  23707. if (! opts.onlyURL) {
  23708. if (opts.getPath) {
  23709. file.path = fm.path(file.hash);
  23710. if (file.path === '' && file.phash) {
  23711. // get parents
  23712. (function() {
  23713. var dfd = $.Deferred();
  23714. req.push(dfd);
  23715. fm.path(file.hash, false, {})
  23716. .done(function(path) {
  23717. file.path = path;
  23718. })
  23719. .fail(function() {
  23720. file.path = '';
  23721. })
  23722. .always(function() {
  23723. dfd.resolve();
  23724. });
  23725. })();
  23726. }
  23727. }
  23728. if (file.tmb && file.tmb != 1) {
  23729. file.tmb = tmb + file.tmb;
  23730. }
  23731. if (!file.width && !file.height) {
  23732. if (file.dim) {
  23733. dim = file.dim.split('x');
  23734. file.width = dim[0];
  23735. file.height = dim[1];
  23736. } else if (opts.getImgSize && file.mime.indexOf('image') !== -1) {
  23737. req.push(fm.request({
  23738. data : {cmd : 'dim', target : file.hash},
  23739. notify : {type : 'dim', cnt : 1, hideCnt : true},
  23740. preventDefault : true
  23741. })
  23742. .done(function(data) {
  23743. if (data.dim) {
  23744. var dim = data.dim.split('x');
  23745. var rfile = fm.file(this.hash);
  23746. rfile.width = this.width = dim[0];
  23747. rfile.height = this.height = dim[1];
  23748. }
  23749. }.bind(file)));
  23750. }
  23751. }
  23752. }
  23753. }
  23754. if (req.length) {
  23755. $.when.apply(null, req).always(function() {
  23756. dfrd.resolve(result(files));
  23757. });
  23758. return dfrd;
  23759. }
  23760. return dfrd.resolve(result(files));
  23761. };
  23762. }).prototype = { forceLoad : true }; // this is required command
  23763. /*
  23764. * File: /js/commands/help.js
  23765. */
  23766. /**
  23767. * @class elFinder command "help"
  23768. * "About" dialog
  23769. *
  23770. * @author Dmitry (dio) Levashov
  23771. **/
  23772. (elFinder.prototype.commands.help = function() {
  23773. var fm = this.fm,
  23774. self = this,
  23775. linktpl = '<div class="elfinder-help-link"> <a href="{url}">{link}</a></div>',
  23776. linktpltgt = '<div class="elfinder-help-link"> <a href="{url}" target="_blank">{link}</a></div>',
  23777. atpl = '<div class="elfinder-help-team"><div>{author}</div>{work}</div>',
  23778. url = /\{url\}/,
  23779. link = /\{link\}/,
  23780. author = /\{author\}/,
  23781. work = /\{work\}/,
  23782. r = 'replace',
  23783. prim = 'ui-priority-primary',
  23784. sec = 'ui-priority-secondary',
  23785. lic = 'elfinder-help-license',
  23786. tab = '<li class="' + fm.res('class', 'tabstab') + ' elfinder-help-tab-{id}"><a href="#'+fm.namespace+'-help-{id}" class="ui-tabs-anchor">{title}</a></li>',
  23787. html = ['<div class="ui-tabs ui-widget ui-widget-content ui-corner-all elfinder-help">',
  23788. '<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-top">'],
  23789. stpl = '<div class="elfinder-help-shortcut"><div class="elfinder-help-shortcut-pattern">{pattern}</div> {descrip}</div>',
  23790. sep = '<div class="elfinder-help-separator"></div>',
  23791. selfUrl = $('base').length? document.location.href.replace(/#.*$/, '') : '',
  23792. clTabActive = fm.res('class', 'tabsactive'),
  23793. getTheme = function() {
  23794. var src;
  23795. if (fm.theme && fm.theme.author) {
  23796. src = atpl[r]('elfinder-help-team', 'elfinder-help-team elfinder-help-term-theme')[r](author, fm.i18n(fm.theme.author) + (fm.theme.email? ' &lt;'+fm.theme.email+'&gt;' : ''))[r](work, fm.i18n('theme') + ' ('+fm.i18n(fm.theme.name)+')');
  23797. } else {
  23798. src = '<div class="elfinder-help-team elfinder-help-term-theme" style="display:none"></div>';
  23799. }
  23800. return src;
  23801. },
  23802. about = function() {
  23803. html.push('<div id="'+fm.namespace+'-help-about" class="ui-tabs-panel ui-widget-content ui-corner-bottom"><div class="elfinder-help-logo"></div>');
  23804. html.push('<h3>elFinder</h3>');
  23805. html.push('<div class="'+prim+'">'+fm.i18n('webfm')+'</div>');
  23806. html.push('<div class="'+sec+'">'+fm.i18n('ver')+': '+fm.version+'</div>');
  23807. html.push('<div class="'+sec+'">'+fm.i18n('protocolver')+': <span class="apiver"></span></div>');
  23808. html.push('<div class="'+sec+'">jQuery/jQuery UI: '+$().jquery+'/'+$.ui.version+'</div>');
  23809. html.push(sep);
  23810. html.push(linktpltgt[r](url, 'https://studio-42.github.io/elFinder/')[r](link, fm.i18n('homepage')));
  23811. html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder/wiki')[r](link, fm.i18n('docs')));
  23812. html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder')[r](link, fm.i18n('github')));
  23813. //html.push(linktpltgt[r](url, 'http://twitter.com/elrte_elfinder')[r](link, fm.i18n('twitter')));
  23814. html.push(sep);
  23815. html.push('<div class="'+prim+'">'+fm.i18n('team')+'</div>');
  23816. html.push(atpl[r](author, 'Dmitry "dio" Levashov &lt;dio@std42.ru&gt;')[r](work, fm.i18n('chiefdev')));
  23817. html.push(atpl[r](author, 'Naoki Sawada &lt;hypweb+elfinder@gmail.com&gt;')[r](work, fm.i18n('developer')));
  23818. html.push(atpl[r](author, 'Troex Nevelin &lt;troex@fury.scancode.ru&gt;')[r](work, fm.i18n('maintainer')));
  23819. html.push(atpl[r](author, 'Alexey Sukhotin &lt;strogg@yandex.ru&gt;')[r](work, fm.i18n('contributor')));
  23820. if (fm.i18[fm.lang].translator) {
  23821. $.each(fm.i18[fm.lang].translator.split(', '), function() {
  23822. html.push(atpl[r](author, $.trim(this))[r](work, fm.i18n('translator')+' ('+fm.i18[fm.lang].language+')'));
  23823. });
  23824. }
  23825. html.push(getTheme());
  23826. html.push(sep);
  23827. html.push('<div class="'+lic+'">'+fm.i18n('icons')+': Pixelmixer, <a href="http://p.yusukekamiyamane.com" target="_blank">Fugue</a>, <a href="https://icons8.com" target="_blank">Icons8</a></div>');
  23828. html.push(sep);
  23829. html.push('<div class="'+lic+'">Licence: 3-clauses BSD Licence</div>');
  23830. html.push('<div class="'+lic+'">Copyright © 2009-2020, Studio 42</div>');
  23831. html.push('<div class="'+lic+'">„ …'+fm.i18n('dontforget')+' ”</div>');
  23832. html.push('</div>');
  23833. },
  23834. shortcuts = function() {
  23835. var sh = fm.shortcuts();
  23836. // shortcuts tab
  23837. html.push('<div id="'+fm.namespace+'-help-shortcuts" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  23838. if (sh.length) {
  23839. html.push('<div class="ui-widget-content elfinder-help-shortcuts">');
  23840. $.each(sh, function(i, s) {
  23841. html.push(stpl.replace(/\{pattern\}/, s[0]).replace(/\{descrip\}/, s[1]));
  23842. });
  23843. html.push('</div>');
  23844. } else {
  23845. html.push('<div class="elfinder-help-disabled">'+fm.i18n('shortcutsof')+'</div>');
  23846. }
  23847. html.push('</div>');
  23848. },
  23849. help = function() {
  23850. // help tab
  23851. html.push('<div id="'+fm.namespace+'-help-help" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  23852. html.push('<a href="https://github.com/Studio-42/elFinder/wiki" target="_blank" class="elfinder-dont-panic"><span>DON\'T PANIC</span></a>');
  23853. html.push('</div>');
  23854. // end help
  23855. },
  23856. useInteg = false,
  23857. integrations = function() {
  23858. useInteg = true;
  23859. html.push('<div id="'+fm.namespace+'-help-integrations" class="ui-tabs-panel ui-widget-content ui-corner-bottom"></div>');
  23860. },
  23861. useDebug = false,
  23862. debug = function() {
  23863. useDebug = true;
  23864. // debug tab
  23865. html.push('<div id="'+fm.namespace+'-help-debug" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  23866. html.push('<div class="ui-widget-content elfinder-help-debug"><ul></ul></div>');
  23867. html.push('</div>');
  23868. // end debug
  23869. },
  23870. debugRender = function() {
  23871. var render = function(elm, obj) {
  23872. $.each(obj, function(k, v) {
  23873. elm.append($('<dt></dt>').text(k));
  23874. if (typeof v === 'undefined') {
  23875. elm.append($('<dd></dd>').append($('<span></span>').text('undfined')));
  23876. } else if (typeof v === 'object' && !v) {
  23877. elm.append($('<dd></dd>').append($('<span></span>').text('null')));
  23878. } else if (typeof v === 'object' && ($.isPlainObject(v) || v.length)) {
  23879. elm.append( $('<dd></dd>').append(render($('<dl></dl>'), v)));
  23880. } else {
  23881. elm.append($('<dd></dd>').append($('<span></span>').text((v && typeof v === 'object')? '[]' : (v? v : '""'))));
  23882. }
  23883. });
  23884. return elm;
  23885. },
  23886. cnt = debugUL.children('li').length,
  23887. targetL, target, tabId,
  23888. info, lastUL, lastDIV;
  23889. if (self.debug.options || self.debug.debug) {
  23890. if (cnt >= 5) {
  23891. lastUL = debugUL.children('li:last');
  23892. lastDIV = debugDIV.children('div:last');
  23893. if (lastDIV.is(':hidden')) {
  23894. lastUL.remove();
  23895. lastDIV.remove();
  23896. } else {
  23897. lastUL.prev().remove();
  23898. lastDIV.prev().remove();
  23899. }
  23900. }
  23901. tabId = fm.namespace + '-help-debug-' + (+new Date());
  23902. targetL = $('<li></li>').html('<a href="'+selfUrl+'#'+tabId+'">'+self.debug.debug.cmd+'</a>').prependTo(debugUL);
  23903. target = $('<div id="'+tabId+'"></div>').data('debug', self.debug);
  23904. targetL.on('click.debugrender', function() {
  23905. var debug = target.data('debug');
  23906. target.removeData('debug');
  23907. if (debug) {
  23908. target.hide();
  23909. if (debug.debug) {
  23910. info = $('<fieldset>').append($('<legend></legend>').text('debug'), render($('<dl></dl>'), debug.debug));
  23911. target.append(info);
  23912. }
  23913. if (debug.options) {
  23914. info = $('<fieldset>').append($('<legend></legend>').text('options'), render($('<dl></dl>'), debug.options));
  23915. target.append(info);
  23916. }
  23917. target.show();
  23918. }
  23919. targetL.off('click.debugrender');
  23920. });
  23921. debugUL.after(target);
  23922. opened && debugDIV.tabs('refresh');
  23923. }
  23924. },
  23925. content = '',
  23926. opened, tabInteg, integDIV, tabDebug, debugDIV, debugUL;
  23927. this.alwaysEnabled = true;
  23928. this.updateOnSelect = false;
  23929. this.state = -1;
  23930. this.shortcuts = [{
  23931. pattern : 'f1',
  23932. description : this.title
  23933. }];
  23934. fm.bind('load', function() {
  23935. var parts = self.options.view || ['about', 'shortcuts', 'help', 'integrations', 'debug'],
  23936. i, helpSource, tabBase, tabNav, tabs, delta;
  23937. // remove 'preference' tab, it moved to command 'preference'
  23938. if ((i = $.inArray('preference', parts)) !== -1) {
  23939. parts.splice(i, 1);
  23940. }
  23941. // debug tab require jQueryUI Tabs Widget
  23942. if (! $.fn.tabs) {
  23943. if ((i = $.inArray(parts, 'debug')) !== -1) {
  23944. parts.splice(i, 1);
  23945. }
  23946. }
  23947. $.each(parts, function(i, title) {
  23948. html.push(tab[r](/\{id\}/g, title)[r](/\{title\}/, fm.i18n(title)));
  23949. });
  23950. html.push('</ul>');
  23951. $.inArray('about', parts) !== -1 && about();
  23952. $.inArray('shortcuts', parts) !== -1 && shortcuts();
  23953. if ($.inArray('help', parts) !== -1) {
  23954. helpSource = fm.i18nBaseUrl + 'help/%s.html.js';
  23955. help();
  23956. }
  23957. $.inArray('integrations', parts) !== -1 && integrations();
  23958. $.inArray('debug', parts) !== -1 && debug();
  23959. html.push('</div>');
  23960. content = $(html.join(''));
  23961. content.find('.ui-tabs-nav li')
  23962. .on('mouseenter mouseleave', function(e) {
  23963. $(this).toggleClass('ui-state-hover', e.type === 'mouseenter');
  23964. })
  23965. .on('focus blur', 'a', function(e) {
  23966. $(e.delegateTarget).toggleClass('ui-state-focus', e.type === 'focusin');
  23967. })
  23968. .children()
  23969. .on('click', function(e) {
  23970. var link = $(this);
  23971. e.preventDefault();
  23972. e.stopPropagation();
  23973. link.parent().addClass(clTabActive).siblings().removeClass(clTabActive);
  23974. content.children('.ui-tabs-panel').hide().filter(link.attr('href')).show();
  23975. })
  23976. .filter(':first').trigger('click');
  23977. if (useInteg) {
  23978. tabInteg = content.find('.elfinder-help-tab-integrations').hide();
  23979. integDIV = content.find('#'+fm.namespace+'-help-integrations').hide().append($('<div class="elfinder-help-integrations-desc"></div>').html(fm.i18n('integrationWith')));
  23980. fm.bind('helpIntegration', function(e) {
  23981. var ul = integDIV.children('ul:first'),
  23982. data, elm, cmdUL, cmdCls;
  23983. if (e.data) {
  23984. if ($.isPlainObject(e.data)) {
  23985. data = Object.assign({
  23986. link: '',
  23987. title: '',
  23988. banner: ''
  23989. }, e.data);
  23990. if (data.title || data.link) {
  23991. if (!data.title) {
  23992. data.title = data.link;
  23993. }
  23994. if (data.link) {
  23995. elm = $('<a></a>').attr('href', data.link).attr('target', '_blank').text(data.title);
  23996. } else {
  23997. elm = $('<span></span>').text(data.title);
  23998. }
  23999. if (data.banner) {
  24000. elm = $('<span></span>').append($('<img/>').attr(data.banner), elm);
  24001. }
  24002. }
  24003. } else {
  24004. elm = $(e.data);
  24005. elm.filter('a').each(function() {
  24006. var tgt = $(this);
  24007. if (!tgt.attr('target')) {
  24008. tgt.attr('target', '_blank');;
  24009. }
  24010. });
  24011. }
  24012. if (elm) {
  24013. tabInteg.show();
  24014. if (!ul.length) {
  24015. ul = $('<ul class="elfinder-help-integrations"></ul>').appendTo(integDIV);
  24016. }
  24017. if (data && data.cmd) {
  24018. cmdCls = 'elfinder-help-integration-' + data.cmd;
  24019. cmdUL = ul.find('ul.' + cmdCls);
  24020. if (!cmdUL.length) {
  24021. cmdUL = $('<ul class="'+cmdCls+'"></ul>');
  24022. ul.append($('<li></li>').append($('<span></span>').html(fm.i18n('cmd'+data.cmd))).append(cmdUL));
  24023. }
  24024. elm = cmdUL.append($('<li></li>').append(elm));
  24025. } else {
  24026. ul.append($('<li></li>').append(elm));
  24027. }
  24028. }
  24029. }
  24030. }).bind('themechange', function() {
  24031. content.find('div.elfinder-help-term-theme').replaceWith(getTheme());
  24032. });
  24033. }
  24034. // debug
  24035. if (useDebug) {
  24036. tabDebug = content.find('.elfinder-help-tab-debug').hide();
  24037. debugDIV = content.find('#'+fm.namespace+'-help-debug').children('div:first');
  24038. debugUL = debugDIV.children('ul:first').on('click', function(e) {
  24039. e.preventDefault();
  24040. e.stopPropagation();
  24041. });
  24042. self.debug = {};
  24043. fm.bind('backenddebug', function(e) {
  24044. // CAUTION: DO NOT TOUCH `e.data`
  24045. if (useDebug && e.data && e.data.debug) {
  24046. self.debug = { options : e.data.options, debug : Object.assign({ cmd : fm.currentReqCmd }, e.data.debug) };
  24047. if (self.dialog) {
  24048. debugRender();
  24049. }
  24050. }
  24051. });
  24052. }
  24053. content.find('#'+fm.namespace+'-help-about').find('.apiver').text(fm.api);
  24054. self.dialog = self.fmDialog(content, {
  24055. title : self.title,
  24056. width : 530,
  24057. maxWidth: 'window',
  24058. maxHeight: 'window',
  24059. autoOpen : false,
  24060. destroyOnClose : false,
  24061. close : function() {
  24062. if (useDebug) {
  24063. tabDebug.hide();
  24064. debugDIV.tabs('destroy');
  24065. }
  24066. opened = false;
  24067. }
  24068. })
  24069. .on('click', function(e) {
  24070. e.stopPropagation();
  24071. })
  24072. .css({
  24073. overflow: 'hidden'
  24074. });
  24075. tabBase = self.dialog.children('.ui-tabs');
  24076. tabNav = tabBase.children('.ui-tabs-nav:first');
  24077. tabs = tabBase.children('.ui-tabs-panel');
  24078. delta = self.dialog.outerHeight(true) - self.dialog.height();
  24079. self.dialog.closest('.ui-dialog').on('resize', function() {
  24080. tabs.height(self.dialog.height() - delta - tabNav.outerHeight(true) - 20);
  24081. });
  24082. if (helpSource) {
  24083. self.dialog.one('initContents', function() {
  24084. $.ajax({
  24085. url: self.options.helpSource? self.options.helpSource : helpSource.replace('%s', fm.lang),
  24086. dataType: 'html'
  24087. }).done(function(source) {
  24088. $('#'+fm.namespace+'-help-help').html(source);
  24089. }).fail(function() {
  24090. $.ajax({
  24091. url: helpSource.replace('%s', 'en'),
  24092. dataType: 'html'
  24093. }).done(function(source) {
  24094. $('#'+fm.namespace+'-help-help').html(source);
  24095. });
  24096. });
  24097. });
  24098. }
  24099. self.state = 0;
  24100. fm.trigger('helpBuilded', self.dialog);
  24101. }).one('open', function() {
  24102. var debug = false;
  24103. fm.one('backenddebug', function() {
  24104. debug =true;
  24105. }).one('opendone', function() {
  24106. requestAnimationFrame(function() {
  24107. if (! debug && useDebug) {
  24108. useDebug = false;
  24109. tabDebug.hide();
  24110. debugDIV.hide();
  24111. debugUL.hide();
  24112. }
  24113. });
  24114. });
  24115. });
  24116. this.getstate = function() {
  24117. return 0;
  24118. };
  24119. this.exec = function(sel, opts) {
  24120. var tab = opts? opts.tab : void(0),
  24121. debugShow = function() {
  24122. if (useDebug) {
  24123. debugDIV.tabs();
  24124. debugUL.find('a:first').trigger('click');
  24125. tabDebug.show();
  24126. opened = true;
  24127. }
  24128. };
  24129. debugShow();
  24130. this.dialog.trigger('initContents').elfinderdialog('open').find((tab? '.elfinder-help-tab-'+tab : '.ui-tabs-nav li') + ' a:first').trigger('click');
  24131. return $.Deferred().resolve();
  24132. };
  24133. }).prototype = { forceLoad : true }; // this is required command
  24134. /*
  24135. * File: /js/commands/hidden.js
  24136. */
  24137. /**
  24138. * @class elFinder command "hidden"
  24139. * Always hidden command for uiCmdMap
  24140. *
  24141. * @author Naoki Sawada
  24142. **/
  24143. elFinder.prototype.commands.hidden = function() {
  24144. this.hidden = true;
  24145. this.updateOnSelect = false;
  24146. this.getstate = function() {
  24147. return -1;
  24148. };
  24149. };
  24150. /*
  24151. * File: /js/commands/hide.js
  24152. */
  24153. /**
  24154. * @class elFinder command "hide".
  24155. * folders/files to hide as personal setting.
  24156. *
  24157. * @type elFinder.command
  24158. * @author Naoki Sawada
  24159. */
  24160. elFinder.prototype.commands.hide = function() {
  24161. var self = this,
  24162. nameCache = {},
  24163. hideData, hideCnt, cMenuType, sOrigin;
  24164. this.syncTitleOnChange = true;
  24165. this.shortcuts = [{
  24166. pattern : 'ctrl+shift+dot',
  24167. description : this.fm.i18n('toggleHidden')
  24168. }];
  24169. this.init = function() {
  24170. var fm = this.fm;
  24171. hideData = fm.storage('hide') || {items: {}};
  24172. hideCnt = Object.keys(hideData.items).length;
  24173. this.title = fm.i18n(hideData.show? 'hideHidden' : 'showHidden');
  24174. self.update(void(0), self.title);
  24175. };
  24176. this.fm.bind('select contextmenucreate closecontextmenu', function(e, fm) {
  24177. var sel = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected();
  24178. if (e.type === 'select' && e.data) {
  24179. sOrigin = e.data.origin;
  24180. } else if (e.type === 'contextmenucreate') {
  24181. cMenuType = e.data.type;
  24182. }
  24183. if (!sel.length || (((e.type !== 'contextmenucreate' && sOrigin !== 'navbar') || cMenuType === 'cwd') && sel[0] === fm.cwd().hash)) {
  24184. self.title = fm.i18n(hideData.show? 'hideHidden' : 'showHidden');
  24185. } else {
  24186. self.title = fm.i18n('cmdhide');
  24187. }
  24188. if (e.type !== 'closecontextmenu') {
  24189. self.update(cMenuType === 'cwd'? (hideCnt? 0 : -1) : void(0), self.title);
  24190. } else {
  24191. cMenuType = '';
  24192. requestAnimationFrame(function() {
  24193. self.update(void(0), self.title);
  24194. });
  24195. }
  24196. });
  24197. this.getstate = function(sel) {
  24198. return (this.fm.cookieEnabled && cMenuType !== 'cwd' && (sel || this.fm.selected()).length) || hideCnt? 0 : -1;
  24199. };
  24200. this.exec = function(hashes, opts) {
  24201. var fm = this.fm,
  24202. dfrd = $.Deferred()
  24203. .done(function() {
  24204. fm.trigger('hide', {items: items, opts: opts});
  24205. })
  24206. .fail(function(error) {
  24207. fm.error(error);
  24208. }),
  24209. o = opts || {},
  24210. items = o.targets? o.targets : (hashes || fm.selected()),
  24211. added = [],
  24212. removed = [],
  24213. notifyto, files, res;
  24214. hideData = fm.storage('hide') || {};
  24215. if (!$.isPlainObject(hideData)) {
  24216. hideData = {};
  24217. }
  24218. if (!$.isPlainObject(hideData.items)) {
  24219. hideData.items = {};
  24220. }
  24221. if (opts._currentType === 'shortcut' || !items.length || (opts._currentType !== 'navbar' && sOrigin !=='navbar' && items[0] === fm.cwd().hash)) {
  24222. if (hideData.show) {
  24223. o.hide = true;
  24224. } else if (Object.keys(hideData.items).length) {
  24225. o.show = true;
  24226. }
  24227. }
  24228. if (o.reset) {
  24229. o.show = true;
  24230. hideCnt = 0;
  24231. }
  24232. if (o.show || o.hide) {
  24233. if (o.show) {
  24234. hideData.show = true;
  24235. } else {
  24236. delete hideData.show;
  24237. }
  24238. if (o.show) {
  24239. fm.storage('hide', o.reset? null : hideData);
  24240. self.title = fm.i18n('hideHidden');
  24241. self.update(o.reset? -1 : void(0), self.title);
  24242. $.each(hideData.items, function(h) {
  24243. var f = fm.file(h, true);
  24244. if (f && (fm.searchStatus.state || !f.phash || fm.file(f.phash))) {
  24245. added.push(f);
  24246. }
  24247. });
  24248. if (added.length) {
  24249. fm.updateCache({added: added});
  24250. fm.add({added: added});
  24251. }
  24252. if (o.reset) {
  24253. hideData = {items: {}};
  24254. }
  24255. return dfrd.resolve();
  24256. }
  24257. items = Object.keys(hideData.items);
  24258. }
  24259. if (items.length) {
  24260. $.each(items, function(i, h) {
  24261. var f;
  24262. if (!hideData.items[h]) {
  24263. f = fm.file(h);
  24264. if (f) {
  24265. nameCache[h] = f.i18 || f.name;
  24266. }
  24267. hideData.items[h] = nameCache[h]? nameCache[h] : h;
  24268. }
  24269. });
  24270. hideCnt = Object.keys(hideData.items).length;
  24271. files = this.files(items);
  24272. fm.storage('hide', hideData);
  24273. fm.remove({removed: items});
  24274. if (hideData.show) {
  24275. this.exec(void(0), {hide: true});
  24276. }
  24277. if (!o.hide) {
  24278. res = {};
  24279. res.undo = {
  24280. cmd : 'hide',
  24281. callback : function() {
  24282. var nData = fm.storage('hide');
  24283. if (nData) {
  24284. $.each(items, function(i, h) {
  24285. delete nData.items[h];
  24286. });
  24287. hideCnt = Object.keys(nData.items).length;
  24288. fm.storage('hide', nData);
  24289. fm.trigger('hide', {items: items, opts: {}});
  24290. self.update(hideCnt? 0 : -1);
  24291. }
  24292. fm.updateCache({added: files});
  24293. fm.add({added: files});
  24294. }
  24295. };
  24296. res.redo = {
  24297. cmd : 'hide',
  24298. callback : function() {
  24299. return fm.exec('hide', void(0), {targets: items});
  24300. }
  24301. };
  24302. }
  24303. }
  24304. return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(res);
  24305. };
  24306. };
  24307. /*
  24308. * File: /js/commands/home.js
  24309. */
  24310. (elFinder.prototype.commands.home = function() {
  24311. this.title = 'Home';
  24312. this.alwaysEnabled = true;
  24313. this.updateOnSelect = false;
  24314. this.shortcuts = [{
  24315. pattern : 'ctrl+home ctrl+shift+up',
  24316. description : 'Home'
  24317. }];
  24318. this.getstate = function() {
  24319. var root = this.fm.root(),
  24320. cwd = this.fm.cwd().hash;
  24321. return root && cwd && root != cwd ? 0: -1;
  24322. };
  24323. this.exec = function() {
  24324. return this.fm.exec('open', this.fm.root());
  24325. };
  24326. }).prototype = { forceLoad : true }; // this is required command
  24327. /*
  24328. * File: /js/commands/info.js
  24329. */
  24330. /**
  24331. * @class elFinder command "info".
  24332. * Display dialog with file properties.
  24333. *
  24334. * @author Dmitry (dio) Levashov, dio@std42.ru
  24335. **/
  24336. (elFinder.prototype.commands.info = function() {
  24337. var m = 'msg',
  24338. fm = this.fm,
  24339. spclass = 'elfinder-spinner',
  24340. btnclass = 'elfinder-info-button',
  24341. msg = {
  24342. calc : fm.i18n('calc'),
  24343. size : fm.i18n('size'),
  24344. unknown : fm.i18n('unknown'),
  24345. path : fm.i18n('path'),
  24346. aliasfor : fm.i18n('aliasfor'),
  24347. modify : fm.i18n('modify'),
  24348. perms : fm.i18n('perms'),
  24349. locked : fm.i18n('locked'),
  24350. dim : fm.i18n('dim'),
  24351. kind : fm.i18n('kind'),
  24352. files : fm.i18n('files'),
  24353. folders : fm.i18n('folders'),
  24354. roots : fm.i18n('volumeRoots'),
  24355. items : fm.i18n('items'),
  24356. yes : fm.i18n('yes'),
  24357. no : fm.i18n('no'),
  24358. link : fm.i18n('link'),
  24359. owner : fm.i18n('owner'),
  24360. group : fm.i18n('group'),
  24361. perm : fm.i18n('perm'),
  24362. getlink : fm.i18n('getLink')
  24363. },
  24364. applyZWSP = function(str, remove) {
  24365. if (remove) {
  24366. return str.replace(/\u200B/g, '');
  24367. } else {
  24368. return str.replace(/(\/|\\)/g, "$1\u200B");
  24369. }
  24370. };
  24371. this.items = ['size', 'aliasfor', 'path', 'link', 'dim', 'modify', 'perms', 'locked', 'owner', 'group', 'perm'];
  24372. if (this.options.custom && Object.keys(this.options.custom).length) {
  24373. $.each(this.options.custom, function(name, details) {
  24374. details.label && this.items.push(details.label);
  24375. });
  24376. }
  24377. this.tpl = {
  24378. main : '<div class="ui-helper-clearfix elfinder-info-title {dirclass}"><span class="elfinder-cwd-icon {class} ui-corner-all"{style}></span>{title}</div><table class="elfinder-info-tb">{content}</table>',
  24379. itemTitle : '<strong>{name}</strong><span class="elfinder-info-kind">{kind}</span>',
  24380. groupTitle : '<strong>{items}: {num}</strong>',
  24381. row : '<tr><td class="elfinder-info-label">{label} : </td><td class="{class}">{value}</td></tr>',
  24382. spinner : '<span>{text}</span> <span class="'+spclass+' '+spclass+'-{name}"></span>'
  24383. };
  24384. this.alwaysEnabled = true;
  24385. this.updateOnSelect = false;
  24386. this.shortcuts = [{
  24387. pattern : 'ctrl+i'
  24388. }];
  24389. this.init = function() {
  24390. $.each(msg, function(k, v) {
  24391. msg[k] = fm.i18n(v);
  24392. });
  24393. };
  24394. this.getstate = function() {
  24395. return 0;
  24396. };
  24397. this.exec = function(hashes) {
  24398. var files = this.files(hashes);
  24399. if (! files.length) {
  24400. files = this.files([ this.fm.cwd().hash ]);
  24401. }
  24402. var self = this,
  24403. fm = this.fm,
  24404. o = this.options,
  24405. tpl = this.tpl,
  24406. row = tpl.row,
  24407. cnt = files.length,
  24408. content = [],
  24409. view = tpl.main,
  24410. l = '{label}',
  24411. v = '{value}',
  24412. reqs = [],
  24413. reqDfrd = null,
  24414. opts = {
  24415. title : fm.i18n('selectionInfo'),
  24416. width : 'auto',
  24417. close : function() {
  24418. $(this).elfinderdialog('destroy');
  24419. if (reqDfrd && reqDfrd.state() === 'pending') {
  24420. reqDfrd.reject();
  24421. }
  24422. $.grep(reqs, function(r) {
  24423. r && r.state() === 'pending' && r.reject();
  24424. });
  24425. }
  24426. },
  24427. count = [],
  24428. replSpinner = function(msg, name, className) {
  24429. dialog.find('.'+spclass+'-'+name).parent().html(msg).addClass(className || '');
  24430. },
  24431. id = fm.namespace+'-info-'+$.map(files, function(f) { return f.hash; }).join('-'),
  24432. dialog = fm.getUI().find('#'+id),
  24433. customActions = [],
  24434. style = '',
  24435. hashClass = 'elfinder-font-mono elfinder-info-hash',
  24436. getHashAlgorisms = [],
  24437. ndialog = fm.ui.notify,
  24438. size, tmb, file, title, dcnt, rdcnt, path, hideItems, hashProg;
  24439. if (ndialog.is(':hidden') && ndialog.children('.elfinder-notify').length) {
  24440. ndialog.elfinderdialog('open').height('auto');
  24441. }
  24442. if (!cnt) {
  24443. return $.Deferred().reject();
  24444. }
  24445. if (dialog.length) {
  24446. dialog.elfinderdialog('toTop');
  24447. return $.Deferred().resolve();
  24448. }
  24449. hideItems = fm.storage('infohides') || fm.arrayFlip(o.hideItems, true);
  24450. if (cnt === 1) {
  24451. file = files[0];
  24452. if (file.icon) {
  24453. style = ' '+fm.getIconStyle(file);
  24454. }
  24455. view = view.replace('{dirclass}', file.csscls? fm.escape(file.csscls) : '').replace('{class}', fm.mime2class(file.mime)).replace('{style}', style);
  24456. title = tpl.itemTitle.replace('{name}', fm.escape(file.i18 || file.name)).replace('{kind}', '<span title="'+fm.escape(file.mime)+'">'+fm.mime2kind(file)+'</span>');
  24457. tmb = fm.tmb(file);
  24458. if (!file.read) {
  24459. size = msg.unknown;
  24460. } else if (file.mime != 'directory' || file.alias) {
  24461. size = fm.formatSize(file.size);
  24462. } else {
  24463. size = tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'size');
  24464. count.push(file.hash);
  24465. }
  24466. !hideItems.size && content.push(row.replace(l, msg.size).replace(v, size));
  24467. !hideItems.aleasfor && file.alias && content.push(row.replace(l, msg.aliasfor).replace(v, file.alias));
  24468. if (!hideItems.path) {
  24469. if (path = fm.path(file.hash, true)) {
  24470. content.push(row.replace(l, msg.path).replace(v, applyZWSP(fm.escape(path))).replace('{class}', 'elfinder-info-path'));
  24471. } else {
  24472. content.push(row.replace(l, msg.path).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'path')).replace('{class}', 'elfinder-info-path'));
  24473. reqs.push(fm.path(file.hash, true, {notify: null})
  24474. .fail(function() {
  24475. replSpinner(msg.unknown, 'path');
  24476. })
  24477. .done(function(path) {
  24478. replSpinner(applyZWSP(path), 'path');
  24479. }));
  24480. }
  24481. }
  24482. if (!hideItems.link && file.read) {
  24483. var href,
  24484. name_esc = fm.escape(file.name);
  24485. if (file.url == '1') {
  24486. content.push(row.replace(l, msg.link).replace(v, '<button class="'+btnclass+' '+spclass+'-url">'+msg.getlink+'</button>'));
  24487. } else {
  24488. if (file.url) {
  24489. href = file.url;
  24490. } else if (file.mime === 'directory') {
  24491. if (o.nullUrlDirLinkSelf && file.url === null) {
  24492. var loc = window.location;
  24493. href = loc.pathname + loc.search + '#elf_' + file.hash;
  24494. } else if (file.url !== '' && fm.option('url', (!fm.isRoot(file) && file.phash) || file.hash)) {
  24495. href = fm.url(file.hash);
  24496. }
  24497. } else {
  24498. href = fm.url(file.hash);
  24499. }
  24500. href && content.push(row.replace(l, msg.link).replace(v, '<a href="'+href+'" target="_blank">'+name_esc+'</a>'));
  24501. }
  24502. }
  24503. if (!hideItems.dim) {
  24504. if (file.dim) { // old api
  24505. content.push(row.replace(l, msg.dim).replace(v, file.dim));
  24506. } else if (file.mime.indexOf('image') !== -1) {
  24507. if (file.width && file.height) {
  24508. content.push(row.replace(l, msg.dim).replace(v, file.width+'x'+file.height));
  24509. } else if (file.size && file.size !== '0') {
  24510. content.push(row.replace(l, msg.dim).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'dim')));
  24511. reqs.push(fm.request({
  24512. data : {cmd : 'dim', target : file.hash},
  24513. preventDefault : true
  24514. })
  24515. .fail(function() {
  24516. replSpinner(msg.unknown, 'dim');
  24517. })
  24518. .done(function(data) {
  24519. replSpinner(data.dim || msg.unknown, 'dim');
  24520. if (data.dim) {
  24521. var dim = data.dim.split('x');
  24522. var rfile = fm.file(file.hash);
  24523. rfile.width = dim[0];
  24524. rfile.height = dim[1];
  24525. }
  24526. }));
  24527. }
  24528. }
  24529. }
  24530. !hideItems.modify && content.push(row.replace(l, msg.modify).replace(v, fm.formatDate(file)));
  24531. !hideItems.perms && content.push(row.replace(l, msg.perms).replace(v, fm.formatPermissions(file)));
  24532. !hideItems.locked && content.push(row.replace(l, msg.locked).replace(v, file.locked ? msg.yes : msg.no));
  24533. !hideItems.owner && file.owner && content.push(row.replace(l, msg.owner).replace(v, file.owner));
  24534. !hideItems.group && file.group && content.push(row.replace(l, msg.group).replace(v, file.group));
  24535. !hideItems.perm && file.perm && content.push(row.replace(l, msg.perm).replace(v, fm.formatFileMode(file.perm)));
  24536. // Get MD5, SHA hashes
  24537. if (window.ArrayBuffer && (fm.options.cdns.sparkmd5 || fm.options.cdns.jssha) && file.mime !== 'directory' && file.size > 0 && (!o.showHashMaxsize || file.size <= o.showHashMaxsize)) {
  24538. getHashAlgorisms = [];
  24539. $.each(fm.storage('hashchekcer') || o.showHashAlgorisms, function(i, n) {
  24540. if (!file[n]) {
  24541. content.push(row.replace(l, fm.i18n(n)).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', n)));
  24542. getHashAlgorisms.push(n);
  24543. } else {
  24544. content.push(row.replace(l, fm.i18n(n)).replace(v, file[n]).replace('{class}', hashClass));
  24545. }
  24546. });
  24547. if (getHashAlgorisms.length) {
  24548. hashProg = $('<div class="elfinder-quicklook-info-progress"></div>');
  24549. reqs.push(
  24550. fm.getContentsHashes(file.hash, getHashAlgorisms, o.showHashOpts, { progressBar : hashProg }).progress(function(hashes) {
  24551. $.each(getHashAlgorisms, function(i, n) {
  24552. if (hashes[n]) {
  24553. replSpinner(hashes[n], n, hashClass);
  24554. }
  24555. });
  24556. }).always(function() {
  24557. $.each(getHashAlgorisms, function(i, n) {
  24558. replSpinner(msg.unknown, n);
  24559. });
  24560. })
  24561. );
  24562. }
  24563. }
  24564. // Add custom info fields
  24565. if (o.custom) {
  24566. $.each(o.custom, function(name, details) {
  24567. if (
  24568. !hideItems[details.label]
  24569. &&
  24570. (!details.mimes || $.grep(details.mimes, function(m){return (file.mime === m || file.mime.indexOf(m+'/') === 0)? true : false;}).length)
  24571. &&
  24572. (!details.hashRegex || file.hash.match(details.hashRegex))
  24573. ) {
  24574. // Add to the content
  24575. content.push(row.replace(l, fm.i18n(details.label)).replace(v , details.tpl.replace('{id}', id)));
  24576. // Register the action
  24577. if (details.action && (typeof details.action == 'function')) {
  24578. customActions.push(details.action);
  24579. }
  24580. }
  24581. });
  24582. }
  24583. } else {
  24584. view = view.replace('{class}', 'elfinder-cwd-icon-group');
  24585. title = tpl.groupTitle.replace('{items}', msg.items).replace('{num}', cnt);
  24586. dcnt = $.grep(files, function(f) { return f.mime == 'directory' ? true : false ; }).length;
  24587. if (!dcnt) {
  24588. size = 0;
  24589. $.each(files, function(h, f) {
  24590. var s = parseInt(f.size);
  24591. if (s >= 0 && size >= 0) {
  24592. size += s;
  24593. } else {
  24594. size = 'unknown';
  24595. }
  24596. });
  24597. content.push(row.replace(l, msg.kind).replace(v, msg.files));
  24598. !hideItems.size && content.push(row.replace(l, msg.size).replace(v, fm.formatSize(size)));
  24599. } else {
  24600. rdcnt = $.grep(files, function(f) { return f.mime === 'directory' && (! f.phash || f.isroot)? true : false ; }).length;
  24601. dcnt -= rdcnt;
  24602. content.push(row.replace(l, msg.kind).replace(v, (rdcnt === cnt || dcnt === cnt)? msg[rdcnt? 'roots' : 'folders'] : $.map({roots: rdcnt, folders: dcnt, files: cnt - rdcnt - dcnt}, function(c, t) { return c? msg[t]+' '+c : null; }).join(', ')));
  24603. !hideItems.size && content.push(row.replace(l, msg.size).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'size')));
  24604. count = $.map(files, function(f) { return f.hash; });
  24605. }
  24606. }
  24607. view = view.replace('{title}', title).replace('{content}', content.join('').replace(/{class}/g, ''));
  24608. dialog = self.fmDialog(view, opts);
  24609. dialog.attr('id', id).one('mousedown', '.elfinder-info-path', function() {
  24610. $(this).html(applyZWSP($(this).html(), true));
  24611. });
  24612. if (getHashAlgorisms.length) {
  24613. hashProg.appendTo(dialog.find('.'+spclass+'-'+getHashAlgorisms[0]).parent());
  24614. }
  24615. if (fm.UA.Mobile && $.fn.tooltip) {
  24616. dialog.children('.ui-dialog-content .elfinder-info-title').tooltip({
  24617. classes: {
  24618. 'ui-tooltip': 'elfinder-ui-tooltip ui-widget-shadow'
  24619. },
  24620. tooltipClass: 'elfinder-ui-tooltip ui-widget-shadow',
  24621. track: true
  24622. });
  24623. }
  24624. if (file && file.url == '1') {
  24625. dialog.on('click', '.'+spclass+'-url', function(){
  24626. $(this).parent().html(tpl.spinner.replace('{text}', fm.i18n('ntfurl')).replace('{name}', 'url'));
  24627. fm.request({
  24628. data : {cmd : 'url', target : file.hash},
  24629. preventDefault : true
  24630. })
  24631. .fail(function() {
  24632. replSpinner(name_esc, 'url');
  24633. })
  24634. .done(function(data) {
  24635. if (data.url) {
  24636. replSpinner('<a href="'+data.url+'" target="_blank">'+name_esc+'</a>' || name_esc, 'url');
  24637. var rfile = fm.file(file.hash);
  24638. rfile.url = data.url;
  24639. } else {
  24640. replSpinner(name_esc, 'url');
  24641. }
  24642. });
  24643. });
  24644. }
  24645. // load thumbnail
  24646. if (tmb) {
  24647. $('<img/>')
  24648. .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  24649. .attr('src', tmb.url);
  24650. }
  24651. // send request to count total size
  24652. if (count.length) {
  24653. reqDfrd = fm.getSize(count).done(function(data) {
  24654. replSpinner(data.formated, 'size');
  24655. }).fail(function() {
  24656. replSpinner(msg.unknown, 'size');
  24657. });
  24658. }
  24659. // call custom actions
  24660. if (customActions.length) {
  24661. $.each(customActions, function(i, action) {
  24662. try {
  24663. action(file, fm, dialog);
  24664. } catch(e) {
  24665. fm.debug('error', e);
  24666. }
  24667. });
  24668. }
  24669. return $.Deferred().resolve();
  24670. };
  24671. }).prototype = { forceLoad : true }; // this is required command
  24672. /*
  24673. * File: /js/commands/mkdir.js
  24674. */
  24675. /**
  24676. * @class elFinder command "mkdir"
  24677. * Create new folder
  24678. *
  24679. * @author Dmitry (dio) Levashov
  24680. **/
  24681. elFinder.prototype.commands.mkdir = function() {
  24682. var fm = this.fm,
  24683. self = this,
  24684. curOrg;
  24685. this.value = '';
  24686. this.disableOnSearch = true;
  24687. this.updateOnSelect = false;
  24688. this.syncTitleOnChange = true;
  24689. this.mime = 'directory';
  24690. this.prefix = 'untitled folder';
  24691. this.exec = function(select, cOpts) {
  24692. var onCwd;
  24693. if (select && select.length && cOpts && cOpts._currentType && cOpts._currentType === 'navbar') {
  24694. this.origin = cOpts._currentType;
  24695. this.data = {
  24696. target: select[0]
  24697. };
  24698. } else {
  24699. onCwd = fm.cwd().hash === select[0];
  24700. this.origin = curOrg && !onCwd? curOrg : 'cwd';
  24701. delete this.data;
  24702. }
  24703. if (! select && ! this.options.intoNewFolderToolbtn) {
  24704. fm.getUI('cwd').trigger('unselectall');
  24705. }
  24706. //this.move = (!onCwd && curOrg !== 'navbar' && fm.selected().length)? true : false;
  24707. this.move = this.value === fm.i18n('cmdmkdirin');
  24708. return $.proxy(fm.res('mixin', 'make'), self)();
  24709. };
  24710. this.shortcuts = [{
  24711. pattern : 'ctrl+shift+n'
  24712. }];
  24713. this.init = function() {
  24714. if (this.options.intoNewFolderToolbtn) {
  24715. this.syncTitleOnChange = true;
  24716. }
  24717. };
  24718. fm.bind('select contextmenucreate closecontextmenu', function(e) {
  24719. var sel = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected();
  24720. self.className = 'mkdir';
  24721. curOrg = e.data && sel.length? (e.data.origin || e.data.type || '') : '';
  24722. if (!self.options.intoNewFolderToolbtn && curOrg === '') {
  24723. curOrg = 'cwd';
  24724. }
  24725. if (sel.length && curOrg !== 'navbar' && curOrg !== 'cwd' && fm.cwd().hash !== sel[0]) {
  24726. self.title = fm.i18n('cmdmkdirin');
  24727. self.className += ' elfinder-button-icon-mkdirin';
  24728. } else {
  24729. self.title = fm.i18n('cmdmkdir');
  24730. }
  24731. if (e.type !== 'closecontextmenu') {
  24732. self.update(void(0), self.title);
  24733. } else {
  24734. requestAnimationFrame(function() {
  24735. self.update(void(0), self.title);
  24736. });
  24737. }
  24738. });
  24739. this.getstate = function(select) {
  24740. var cwd = fm.cwd(),
  24741. sel = (curOrg === 'navbar' || (select && select[0] !== cwd.hash))? this.files(select || fm.selected()) : [],
  24742. cnt = sel.length;
  24743. if (curOrg === 'navbar') {
  24744. return cnt && sel[0].write && sel[0].read? 0 : -1;
  24745. } else {
  24746. return cwd.write && (!cnt || $.grep(sel, function(f) { return f.read && ! f.locked? true : false; }).length == cnt)? 0 : -1;
  24747. }
  24748. };
  24749. };
  24750. /*
  24751. * File: /js/commands/mkfile.js
  24752. */
  24753. /**
  24754. * @class elFinder command "mkfile"
  24755. * Create new empty file
  24756. *
  24757. * @author Dmitry (dio) Levashov
  24758. **/
  24759. elFinder.prototype.commands.mkfile = function() {
  24760. var self = this;
  24761. this.disableOnSearch = true;
  24762. this.updateOnSelect = false;
  24763. this.mime = 'text/plain';
  24764. this.prefix = 'untitled file.txt';
  24765. this.variants = [];
  24766. this.getTypeName = function(mime, type) {
  24767. var fm = self.fm,
  24768. name;
  24769. if (name = fm.messages['kind' + fm.kinds[mime]]) {
  24770. name = fm.i18n(['extentiontype', type.toUpperCase(), name]);
  24771. } else {
  24772. name = fm.i18n(['extentionfile', type.toUpperCase()]);
  24773. }
  24774. return name;
  24775. };
  24776. this.fm.bind('open reload canMakeEmptyFile', function() {
  24777. var fm = self.fm,
  24778. hides = fm.getCommand('edit').getMkfileHides();
  24779. self.variants = [];
  24780. if (fm.mimesCanMakeEmpty) {
  24781. $.each(fm.mimesCanMakeEmpty, function(mime, type) {
  24782. type && !hides[mime] && fm.uploadMimeCheck(mime) && self.variants.push([mime, self.getTypeName(mime, type)]);
  24783. });
  24784. }
  24785. self.change();
  24786. });
  24787. this.getstate = function() {
  24788. return this.fm.cwd().write ? 0 : -1;
  24789. };
  24790. this.exec = function(_dum, mime) {
  24791. var fm = self.fm,
  24792. type, err;
  24793. if (type = fm.mimesCanMakeEmpty[mime]) {
  24794. if (fm.uploadMimeCheck(mime)) {
  24795. this.mime = mime;
  24796. this.prefix = fm.i18n(['untitled file', type]);
  24797. return $.proxy(fm.res('mixin', 'make'), self)();
  24798. }
  24799. err = ['errMkfile', self.getTypeName(mime, type)];
  24800. }
  24801. return $.Deferred().reject(err);
  24802. };
  24803. };
  24804. /*
  24805. * File: /js/commands/netmount.js
  24806. */
  24807. /**
  24808. * @class elFinder command "netmount"
  24809. * Mount network volume with user credentials.
  24810. *
  24811. * @author Dmitry (dio) Levashov
  24812. **/
  24813. elFinder.prototype.commands.netmount = function() {
  24814. var self = this,
  24815. hasMenus = false,
  24816. content;
  24817. this.alwaysEnabled = true;
  24818. this.updateOnSelect = false;
  24819. this.drivers = [];
  24820. this.handlers = {
  24821. load : function() {
  24822. var fm = self.fm;
  24823. if (fm.cookieEnabled) {
  24824. fm.one('open', function() {
  24825. self.drivers = fm.netDrivers;
  24826. if (self.drivers.length) {
  24827. $.each(self.drivers, function() {
  24828. var d = self.options[this];
  24829. if (d) {
  24830. hasMenus = true;
  24831. if (d.integrateInfo) {
  24832. fm.trigger('helpIntegration', Object.assign({cmd: 'netmount'}, d.integrateInfo));
  24833. }
  24834. }
  24835. });
  24836. }
  24837. });
  24838. }
  24839. }
  24840. };
  24841. this.getstate = function() {
  24842. return hasMenus ? 0 : -1;
  24843. };
  24844. this.exec = function() {
  24845. var fm = self.fm,
  24846. dfrd = $.Deferred(),
  24847. o = self.options,
  24848. create = function() {
  24849. var winFocus = function() {
  24850. inputs.protocol.trigger('change', 'winfocus');
  24851. },
  24852. inputs = {
  24853. protocol : $('<select></select>')
  24854. .on('change', function(e, data){
  24855. var protocol = this.value;
  24856. content.find('.elfinder-netmount-tr').hide();
  24857. content.find('.elfinder-netmount-tr-'+protocol).show();
  24858. dialogNode && dialogNode.children('.ui-dialog-buttonpane:first').find('button').show();
  24859. if (typeof o[protocol].select == 'function') {
  24860. o[protocol].select(fm, e, data);
  24861. }
  24862. })
  24863. .addClass('ui-corner-all')
  24864. },
  24865. opts = {
  24866. title : fm.i18n('netMountDialogTitle'),
  24867. resizable : true,
  24868. modal : true,
  24869. destroyOnClose : false,
  24870. open : function() {
  24871. $(window).on('focus.'+fm.namespace, winFocus);
  24872. inputs.protocol.trigger('change');
  24873. },
  24874. close : function() {
  24875. dfrd.state() == 'pending' && dfrd.reject();
  24876. $(window).off('focus.'+fm.namespace, winFocus);
  24877. },
  24878. buttons : {}
  24879. },
  24880. doMount = function() {
  24881. var protocol = inputs.protocol.val(),
  24882. data = {cmd : 'netmount', protocol: protocol},
  24883. cur = o[protocol],
  24884. mnt2res;
  24885. $.each(content.find('input.elfinder-netmount-inputs-'+protocol), function(name, input) {
  24886. var val, elm;
  24887. elm = $(input);
  24888. if (elm.is(':radio,:checkbox')) {
  24889. if (elm.is(':checked')) {
  24890. val = $.trim(elm.val());
  24891. }
  24892. } else {
  24893. val = $.trim(elm.val());
  24894. }
  24895. if (val) {
  24896. data[input.name] = val;
  24897. }
  24898. });
  24899. if (!data.host) {
  24900. return fm.trigger('error', {error : 'errNetMountHostReq', opts : {modal: true}});
  24901. }
  24902. if (data.mnt2res) {
  24903. mnt2res = true;
  24904. }
  24905. fm.request({data : data, notify : {type : 'netmount', cnt : 1, hideCnt : true}})
  24906. .done(function(data) {
  24907. var pdir;
  24908. if (data.added && data.added.length) {
  24909. mnt2res && inputs.protocol.trigger('change', 'reset');
  24910. if (data.added[0].phash) {
  24911. if (pdir = fm.file(data.added[0].phash)) {
  24912. if (! pdir.dirs) {
  24913. pdir.dirs = 1;
  24914. fm.change({ changed: [ pdir ] });
  24915. }
  24916. }
  24917. }
  24918. fm.one('netmountdone', function() {
  24919. fm.exec('open', data.added[0].hash);
  24920. });
  24921. }
  24922. dfrd.resolve();
  24923. })
  24924. .fail(function(error) {
  24925. if (cur.fail && typeof cur.fail == 'function') {
  24926. cur.fail(fm, fm.parseError(error));
  24927. }
  24928. dfrd.reject(error);
  24929. });
  24930. self.dialog.elfinderdialog('close');
  24931. },
  24932. form = $('<form autocomplete="off"></form>').on('keydown', 'input', function(e) {
  24933. var comp = true,
  24934. next;
  24935. if (e.keyCode === $.ui.keyCode.ENTER) {
  24936. $.each(form.find('input:visible:not(.elfinder-input-optional)'), function() {
  24937. if ($(this).val() === '') {
  24938. comp = false;
  24939. next = $(this);
  24940. return false;
  24941. }
  24942. });
  24943. if (comp) {
  24944. doMount();
  24945. } else {
  24946. next.trigger('focus');
  24947. }
  24948. }
  24949. }),
  24950. hidden = $('<div></div>'),
  24951. dialog;
  24952. content = $('<table class="elfinder-info-tb elfinder-netmount-tb"></table>')
  24953. .append($('<tr></tr>').append($('<td>'+fm.i18n('protocol')+'</td>')).append($('<td></td>').append(inputs.protocol)));
  24954. $.each(self.drivers, function(i, protocol) {
  24955. if (o[protocol]) {
  24956. inputs.protocol.append('<option value="'+protocol+'">'+fm.i18n(o[protocol].name || protocol)+'</option>');
  24957. $.each(o[protocol].inputs, function(name, input) {
  24958. input.attr('name', name);
  24959. if (input.attr('type') != 'hidden') {
  24960. input.addClass('ui-corner-all elfinder-netmount-inputs-'+protocol);
  24961. content.append($('<tr></tr>').addClass('elfinder-netmount-tr elfinder-netmount-tr-'+protocol).append($('<td>'+fm.i18n(name)+'</td>')).append($('<td></td>').append(input)));
  24962. } else {
  24963. input.addClass('elfinder-netmount-inputs-'+protocol);
  24964. hidden.append(input);
  24965. }
  24966. });
  24967. o[protocol].protocol = inputs.protocol;
  24968. }
  24969. });
  24970. content.append(hidden);
  24971. content.find('.elfinder-netmount-tr').hide();
  24972. content.find('.elfinder-netmount-tr-' + self.drivers[0]).show();
  24973. opts.buttons[fm.i18n('btnMount')] = doMount;
  24974. opts.buttons[fm.i18n('btnCancel')] = function() {
  24975. self.dialog.elfinderdialog('close');
  24976. };
  24977. content.find('select,input').addClass('elfinder-tabstop');
  24978. dialog = self.fmDialog(form.append(content), opts).ready(function() {
  24979. inputs.protocol.trigger('change');
  24980. dialog.elfinderdialog('posInit');
  24981. });
  24982. dialogNode = dialog.closest('.ui-dialog');
  24983. return dialog;
  24984. },
  24985. dialogNode;
  24986. if (!self.dialog) {
  24987. self.dialog = create();
  24988. } else {
  24989. self.dialog.elfinderdialog('open');
  24990. }
  24991. return dfrd.promise();
  24992. };
  24993. self.fm.bind('netmount', function(e) {
  24994. var d = e.data || null,
  24995. o = self.options,
  24996. done = function() {
  24997. if (o[d.protocol] && typeof o[d.protocol].done == 'function') {
  24998. o[d.protocol].done(self.fm, d);
  24999. content.find('select,input').addClass('elfinder-tabstop');
  25000. self.dialog.elfinderdialog('tabstopsInit');
  25001. }
  25002. };
  25003. if (d && d.protocol) {
  25004. if (d.mode && d.mode === 'redirect') {
  25005. // To support of third-party cookie blocking (ITP) on CORS
  25006. // On iOS and iPadOS 13.4 and Safari 13.1 on macOS, the session cannot be continued when redirecting OAuth in CORS mode
  25007. self.fm.request({
  25008. data : {cmd : 'netmount', protocol : d.protocol, host: d.host, user : 'init', pass : 'return', options: d.options},
  25009. preventDefault : true
  25010. }).done(function(data) {
  25011. d = JSON.parse(data.body);
  25012. done();
  25013. });
  25014. } else {
  25015. done();
  25016. }
  25017. }
  25018. });
  25019. };
  25020. elFinder.prototype.commands.netunmount = function() {
  25021. var self = this;
  25022. this.alwaysEnabled = true;
  25023. this.updateOnSelect = false;
  25024. this.drivers = [];
  25025. this.handlers = {
  25026. load : function() {
  25027. this.drivers = this.fm.netDrivers;
  25028. }
  25029. };
  25030. this.getstate = function(sel) {
  25031. var fm = this.fm,
  25032. file;
  25033. return !!sel && this.drivers.length && !this._disabled && (file = fm.file(sel[0])) && file.netkey ? 0 : -1;
  25034. };
  25035. this.exec = function(hashes) {
  25036. var self = this,
  25037. fm = this.fm,
  25038. dfrd = $.Deferred()
  25039. .fail(function(error) {
  25040. error && fm.error(error);
  25041. }),
  25042. drive = fm.file(hashes[0]),
  25043. childrenRoots = function(hash) {
  25044. var roots = [],
  25045. work;
  25046. if (fm.leafRoots) {
  25047. work = [];
  25048. $.each(fm.leafRoots, function(phash, hashes) {
  25049. var parents = fm.parents(phash),
  25050. idx, deep;
  25051. if ((idx = $.inArray(hash, parents)) !== -1) {
  25052. idx = parents.length - idx;
  25053. $.each(hashes, function(i, h) {
  25054. work.push({i: idx, hash: h});
  25055. });
  25056. }
  25057. });
  25058. if (work.length) {
  25059. work.sort(function(a, b) { return a.i < b.i; });
  25060. $.each(work, function(i, o) {
  25061. roots.push(o.hash);
  25062. });
  25063. }
  25064. }
  25065. return roots;
  25066. };
  25067. if (this._disabled) {
  25068. return dfrd.reject();
  25069. }
  25070. if (dfrd.state() == 'pending') {
  25071. fm.confirm({
  25072. title : self.title,
  25073. text : fm.i18n('confirmUnmount', drive.name),
  25074. accept : {
  25075. label : 'btnUnmount',
  25076. callback : function() {
  25077. var target = drive.hash,
  25078. roots = childrenRoots(target),
  25079. requests = [],
  25080. removed = [],
  25081. doUmount = function() {
  25082. $.when(requests).done(function() {
  25083. fm.request({
  25084. data : {cmd : 'netmount', protocol : 'netunmount', host: drive.netkey, user : target, pass : 'dum'},
  25085. notify : {type : 'netunmount', cnt : 1, hideCnt : true},
  25086. preventFail : true
  25087. })
  25088. .fail(function(error) {
  25089. dfrd.reject(error);
  25090. })
  25091. .done(function(data) {
  25092. drive.volumeid && delete fm.volumeExpires[drive.volumeid];
  25093. dfrd.resolve();
  25094. });
  25095. }).fail(function(error) {
  25096. if (removed.length) {
  25097. fm.remove({ removed: removed });
  25098. }
  25099. dfrd.reject(error);
  25100. });
  25101. };
  25102. if (roots.length) {
  25103. fm.confirm({
  25104. title : self.title,
  25105. text : (function() {
  25106. var msgs = ['unmountChildren'];
  25107. $.each(roots, function(i, hash) {
  25108. msgs.push([fm.file(hash).name]);
  25109. });
  25110. return msgs;
  25111. })(),
  25112. accept : {
  25113. label : 'btnUnmount',
  25114. callback : function() {
  25115. $.each(roots, function(i, hash) {
  25116. var d = fm.file(hash);
  25117. if (d.netkey) {
  25118. requests.push(fm.request({
  25119. data : {cmd : 'netmount', protocol : 'netunmount', host: d.netkey, user : d.hash, pass : 'dum'},
  25120. notify : {type : 'netunmount', cnt : 1, hideCnt : true},
  25121. preventDefault : true
  25122. }).done(function(data) {
  25123. if (data.removed) {
  25124. d.volumeid && delete fm.volumeExpires[d.volumeid];
  25125. removed = removed.concat(data.removed);
  25126. }
  25127. }));
  25128. }
  25129. });
  25130. doUmount();
  25131. }
  25132. },
  25133. cancel : {
  25134. label : 'btnCancel',
  25135. callback : function() {
  25136. dfrd.reject();
  25137. }
  25138. }
  25139. });
  25140. } else {
  25141. requests = null;
  25142. doUmount();
  25143. }
  25144. }
  25145. },
  25146. cancel : {
  25147. label : 'btnCancel',
  25148. callback : function() { dfrd.reject(); }
  25149. }
  25150. });
  25151. }
  25152. return dfrd;
  25153. };
  25154. };
  25155. /*
  25156. * File: /js/commands/open.js
  25157. */
  25158. /**
  25159. * @class elFinder command "open"
  25160. * Enter folder or open files in new windows
  25161. *
  25162. * @author Dmitry (dio) Levashov
  25163. **/
  25164. (elFinder.prototype.commands.open = function() {
  25165. var fm = this.fm,
  25166. self = this;
  25167. this.alwaysEnabled = true;
  25168. this.noChangeDirOnRemovedCwd = true;
  25169. this._handlers = {
  25170. dblclick : function(e) {
  25171. var arg = e.data && e.data.file? [ e.data.file ]: void(0);
  25172. if (self.getstate(arg) === 0) {
  25173. e.preventDefault();
  25174. fm.exec('open', arg);
  25175. }
  25176. },
  25177. 'select enable disable reload' : function(e) { this.update(e.type == 'disable' ? -1 : void(0)); }
  25178. };
  25179. this.shortcuts = [{
  25180. pattern : 'ctrl+down numpad_enter'+(fm.OS != 'mac' && ' enter')
  25181. }];
  25182. this.getstate = function(select) {
  25183. var sel = this.files(select),
  25184. cnt = sel.length;
  25185. return cnt == 1
  25186. ? (sel[0].read ? 0 : -1)
  25187. : (cnt && !fm.UA.Mobile) ? ($.grep(sel, function(file) { return file.mime == 'directory' || ! file.read ? false : true;}).length == cnt ? 0 : -1) : -1;
  25188. };
  25189. this.exec = function(hashes, cOpts) {
  25190. var dfrd = $.Deferred().fail(function(error) { error && fm.error(error); }),
  25191. files = this.files(hashes),
  25192. cnt = files.length,
  25193. thash = (typeof cOpts == 'object')? cOpts.thash : false,
  25194. opts = this.options,
  25195. into = opts.into || 'window',
  25196. file, url, s, w, imgW, imgH, winW, winH, reg, link, html5dl, inline,
  25197. selAct, cmd;
  25198. if (!cnt && !thash) {
  25199. {
  25200. return dfrd.reject();
  25201. }
  25202. }
  25203. // open folder
  25204. if (thash || (cnt == 1 && (file = files[0]) && file.mime == 'directory')) {
  25205. if (!thash && file && !file.read) {
  25206. return dfrd.reject(['errOpen', file.name, 'errPerm']);
  25207. } else {
  25208. if (fm.keyState.ctrlKey && (fm.keyState.shiftKey || typeof fm.options.getFileCallback !== 'function')) {
  25209. if (fm.getCommand('opennew')) {
  25210. return fm.exec('opennew', [thash? thash : file.hash]);
  25211. }
  25212. }
  25213. return fm.request({
  25214. data : {cmd : 'open', target : thash || file.hash},
  25215. notify : {type : 'open', cnt : 1, hideCnt : true},
  25216. syncOnFail : true,
  25217. lazy : false
  25218. });
  25219. }
  25220. }
  25221. files = $.grep(files, function(file) { return file.mime != 'directory' ? true : false; });
  25222. // nothing to open or files and folders selected - do nothing
  25223. if (cnt != files.length) {
  25224. return dfrd.reject();
  25225. }
  25226. var doOpen = function() {
  25227. var openCB = function(url) {
  25228. var link = $('<a>').hide().appendTo($('body'));
  25229. if (fm.UA.Mobile || !inline) {
  25230. if (html5dl) {
  25231. if (!inline) {
  25232. link.attr('download', file.name);
  25233. } else {
  25234. link.attr('target', '_blank');
  25235. }
  25236. link.attr('href', url).get(0).click();
  25237. } else {
  25238. wnd = window.open(url);
  25239. if (!wnd) {
  25240. return dfrd.reject('errPopup');
  25241. }
  25242. }
  25243. } else {
  25244. getOnly = (typeof opts.method === 'string' && opts.method.toLowerCase() === 'get');
  25245. if (!getOnly
  25246. && url.indexOf(fm.options.url) === 0
  25247. && fm.customData
  25248. && Object.keys(fm.customData).length
  25249. // Since playback by POST request can not be done in Chrome, media allows GET request
  25250. && !file.mime.match(/^(?:video|audio)/)
  25251. ) {
  25252. // Send request as 'POST' method to hide custom data at location bar
  25253. url = '';
  25254. }
  25255. if (into === 'window') {
  25256. // set window size for image if set
  25257. imgW = winW = Math.round(2 * screen.availWidth / 3);
  25258. imgH = winH = Math.round(2 * screen.availHeight / 3);
  25259. if (parseInt(file.width) && parseInt(file.height)) {
  25260. imgW = parseInt(file.width);
  25261. imgH = parseInt(file.height);
  25262. } else if (file.dim) {
  25263. s = file.dim.split('x');
  25264. imgW = parseInt(s[0]);
  25265. imgH = parseInt(s[1]);
  25266. }
  25267. if (winW >= imgW && winH >= imgH) {
  25268. winW = imgW;
  25269. winH = imgH;
  25270. } else {
  25271. if ((imgW - winW) > (imgH - winH)) {
  25272. winH = Math.round(imgH * (winW / imgW));
  25273. } else {
  25274. winW = Math.round(imgW * (winH / imgH));
  25275. }
  25276. }
  25277. w = 'width='+winW+',height='+winH;
  25278. wnd = window.open(url, target, w + ',top=50,left=50,scrollbars=yes,resizable=yes,titlebar=no');
  25279. } else {
  25280. if (into === 'tabs') {
  25281. target = file.hash;
  25282. }
  25283. wnd = window.open('about:blank', target);
  25284. }
  25285. if (!wnd) {
  25286. return dfrd.reject('errPopup');
  25287. }
  25288. if (url === '') {
  25289. var form = document.createElement("form");
  25290. form.action = fm.options.url;
  25291. form.method = 'POST';
  25292. form.target = target;
  25293. form.style.display = 'none';
  25294. var params = Object.assign({}, fm.customData, {
  25295. cmd: 'file',
  25296. target: file.hash,
  25297. _t: file.ts || parseInt(+new Date()/1000)
  25298. });
  25299. $.each(params, function(key, val)
  25300. {
  25301. var input = document.createElement("input");
  25302. input.name = key;
  25303. input.value = val;
  25304. form.appendChild(input);
  25305. });
  25306. document.body.appendChild(form);
  25307. form.submit();
  25308. } else if (into !== 'window') {
  25309. wnd.location = url;
  25310. }
  25311. $(wnd).trigger('focus');
  25312. }
  25313. link.remove();
  25314. },
  25315. wnd, target, getOnly;
  25316. try {
  25317. reg = new RegExp(fm.option('dispInlineRegex'), 'i');
  25318. } catch(e) {
  25319. reg = false;
  25320. }
  25321. // open files
  25322. html5dl = (typeof $('<a>').get(0).download === 'string');
  25323. cnt = files.length;
  25324. while (cnt--) {
  25325. target = 'elf_open_window';
  25326. file = files[cnt];
  25327. if (!file.read) {
  25328. return dfrd.reject(['errOpen', file.name, 'errPerm']);
  25329. }
  25330. inline = (reg && file.mime.match(reg));
  25331. fm.openUrl(file.hash, !inline, openCB);
  25332. }
  25333. return dfrd.resolve(hashes);
  25334. };
  25335. if (cnt > 1) {
  25336. fm.confirm({
  25337. title: 'openMulti',
  25338. text : ['openMultiConfirm', cnt + ''],
  25339. accept : {
  25340. label : 'cmdopen',
  25341. callback : function() { doOpen(); }
  25342. },
  25343. cancel : {
  25344. label : 'btnCancel',
  25345. callback : function() {
  25346. dfrd.reject();
  25347. }
  25348. },
  25349. buttons : (fm.getCommand('zipdl') && fm.isCommandEnabled('zipdl', fm.cwd().hash))? [
  25350. {
  25351. label : 'cmddownload',
  25352. callback : function() {
  25353. fm.exec('download', hashes);
  25354. dfrd.reject();
  25355. }
  25356. }
  25357. ] : []
  25358. });
  25359. } else {
  25360. selAct = fm.storage('selectAction') || opts.selectAction;
  25361. if (selAct) {
  25362. $.each(selAct.split('/'), function() {
  25363. var cmdName = this.valueOf();
  25364. if (cmdName !== 'open' && (cmd = fm.getCommand(cmdName)) && cmd.enabled()) {
  25365. return false;
  25366. }
  25367. cmd = null;
  25368. });
  25369. if (cmd) {
  25370. return fm.exec(cmd.name);
  25371. }
  25372. }
  25373. doOpen();
  25374. }
  25375. return dfrd;
  25376. };
  25377. }).prototype = { forceLoad : true }; // this is required command
  25378. /*
  25379. * File: /js/commands/opendir.js
  25380. */
  25381. /**
  25382. * @class elFinder command "opendir"
  25383. * Enter parent folder
  25384. *
  25385. * @author Naoki Sawada
  25386. **/
  25387. elFinder.prototype.commands.opendir = function() {
  25388. this.alwaysEnabled = true;
  25389. this.getstate = function() {
  25390. var sel = this.fm.selected(),
  25391. cnt = sel.length,
  25392. wz;
  25393. if (cnt !== 1) {
  25394. return -1;
  25395. }
  25396. wz = this.fm.getUI('workzone');
  25397. return wz.hasClass('elfinder-search-result')? 0 : -1;
  25398. };
  25399. this.exec = function(hashes) {
  25400. var fm = this.fm,
  25401. dfrd = $.Deferred(),
  25402. files = this.files(hashes),
  25403. cnt = files.length,
  25404. hash, pcheck = null;
  25405. if (!cnt || !files[0].phash) {
  25406. return dfrd.reject();
  25407. }
  25408. hash = files[0].phash;
  25409. fm.trigger('searchend', { noupdate: true });
  25410. fm.request({
  25411. data : {cmd : 'open', target : hash},
  25412. notify : {type : 'open', cnt : 1, hideCnt : true},
  25413. syncOnFail : false
  25414. });
  25415. return dfrd;
  25416. };
  25417. };
  25418. /*
  25419. * File: /js/commands/opennew.js
  25420. */
  25421. /**
  25422. * @class elFinder command "opennew"
  25423. * Open folder in new window
  25424. *
  25425. * @author Naoki Sawada
  25426. **/
  25427. elFinder.prototype.commands.opennew = function() {
  25428. var fm = this.fm;
  25429. this.shortcuts = [{
  25430. pattern : (typeof(fm.options.getFileCallback) === 'function'? 'shift+' : '') + 'ctrl+enter'
  25431. }];
  25432. this.getstate = function(select) {
  25433. var sel = this.files(select),
  25434. cnt = sel.length;
  25435. return cnt === 1
  25436. ? (sel[0].mime === 'directory' && sel[0].read? 0 : -1)
  25437. : -1;
  25438. };
  25439. this.exec = function(hashes) {
  25440. var dfrd = $.Deferred(),
  25441. files = this.files(hashes),
  25442. cnt = files.length,
  25443. opts = this.options,
  25444. file, loc, url, win;
  25445. // open folder to new tab (window)
  25446. if (cnt === 1 && (file = files[0]) && file.mime === 'directory') {
  25447. loc = window.location;
  25448. if (opts.url) {
  25449. url = opts.url;
  25450. } else {
  25451. url = loc.pathname;
  25452. }
  25453. if (opts.useOriginQuery) {
  25454. if (!url.match(/\?/)) {
  25455. url += loc.search;
  25456. } else if (loc.search) {
  25457. url += '&' + loc.search.substr(1);
  25458. }
  25459. }
  25460. url += '#elf_' + file.hash;
  25461. win = window.open(url, '_blank');
  25462. setTimeout(function() {
  25463. win.focus();
  25464. }, 1000);
  25465. return dfrd.resolve();
  25466. } else {
  25467. return dfrd.reject();
  25468. }
  25469. };
  25470. };
  25471. /*
  25472. * File: /js/commands/paste.js
  25473. */
  25474. /**
  25475. * @class elFinder command "paste"
  25476. * Paste filesfrom clipboard into directory.
  25477. * If files pasted in its parent directory - files duplicates will created
  25478. *
  25479. * @author Dmitry (dio) Levashov
  25480. **/
  25481. elFinder.prototype.commands.paste = function() {
  25482. this.updateOnSelect = false;
  25483. this.handlers = {
  25484. changeclipboard : function() { this.update(); }
  25485. };
  25486. this.shortcuts = [{
  25487. pattern : 'ctrl+v shift+insert'
  25488. }];
  25489. this.getstate = function(dst) {
  25490. if (this._disabled) {
  25491. return -1;
  25492. }
  25493. if (dst) {
  25494. if (Array.isArray(dst)) {
  25495. if (dst.length != 1) {
  25496. return -1;
  25497. }
  25498. dst = this.fm.file(dst[0]);
  25499. }
  25500. } else {
  25501. dst = this.fm.cwd();
  25502. }
  25503. return this.fm.clipboard().length && dst.mime == 'directory' && dst.write ? 0 : -1;
  25504. };
  25505. this.exec = function(select, cOpts) {
  25506. var self = this,
  25507. fm = self.fm,
  25508. opts = cOpts || {},
  25509. dst = select ? this.files(select)[0] : fm.cwd(),
  25510. files = fm.clipboard(),
  25511. cnt = files.length,
  25512. cut = cnt ? files[0].cut : false,
  25513. cmd = opts._cmd? opts._cmd : (cut? 'move' : 'copy'),
  25514. error = 'err' + cmd.charAt(0).toUpperCase() + cmd.substr(1),
  25515. fpaste = [],
  25516. fcopy = [],
  25517. dfrd = $.Deferred()
  25518. .fail(function(error) {
  25519. error && fm.error(error);
  25520. })
  25521. .always(function() {
  25522. fm.unlockfiles({files : $.map(files, function(f) { return f.hash; })});
  25523. }),
  25524. copy = function(files) {
  25525. return files.length && fm._commands.duplicate
  25526. ? fm.exec('duplicate', files)
  25527. : $.Deferred().resolve();
  25528. },
  25529. paste = function(files) {
  25530. var dfrd = $.Deferred(),
  25531. existed = [],
  25532. hashes = {},
  25533. intersect = function(files, names) {
  25534. var ret = [],
  25535. i = files.length;
  25536. while (i--) {
  25537. $.inArray(files[i].name, names) !== -1 && ret.unshift(i);
  25538. }
  25539. return ret;
  25540. },
  25541. confirm = function(ndx) {
  25542. var i = existed[ndx],
  25543. file = files[i],
  25544. last = ndx == existed.length-1;
  25545. if (!file) {
  25546. return;
  25547. }
  25548. fm.confirm({
  25549. title : fm.i18n(cmd + 'Files'),
  25550. text : ['errExists', file.name, cmd === 'restore'? 'confirmRest' : 'confirmRepl'],
  25551. all : !last,
  25552. accept : {
  25553. label : 'btnYes',
  25554. callback : function(all) {
  25555. !last && !all
  25556. ? confirm(++ndx)
  25557. : paste(files);
  25558. }
  25559. },
  25560. reject : {
  25561. label : 'btnNo',
  25562. callback : function(all) {
  25563. var i;
  25564. if (all) {
  25565. i = existed.length;
  25566. while (ndx < i--) {
  25567. files[existed[i]].remove = true;
  25568. }
  25569. } else {
  25570. files[existed[ndx]].remove = true;
  25571. }
  25572. !last && !all
  25573. ? confirm(++ndx)
  25574. : paste(files);
  25575. }
  25576. },
  25577. cancel : {
  25578. label : 'btnCancel',
  25579. callback : function() {
  25580. dfrd.resolve();
  25581. }
  25582. },
  25583. buttons : [
  25584. {
  25585. label : 'btnBackup',
  25586. callback : function(all) {
  25587. var i;
  25588. if (all) {
  25589. i = existed.length;
  25590. while (ndx < i--) {
  25591. files[existed[i]].rename = true;
  25592. }
  25593. } else {
  25594. files[existed[ndx]].rename = true;
  25595. }
  25596. !last && !all
  25597. ? confirm(++ndx)
  25598. : paste(files);
  25599. }
  25600. }
  25601. ]
  25602. });
  25603. },
  25604. valid = function(names) {
  25605. var exists = {}, existedArr;
  25606. if (names) {
  25607. if (Array.isArray(names)) {
  25608. if (names.length) {
  25609. if (typeof names[0] == 'string') {
  25610. // elFinder <= 2.1.6 command `is` results
  25611. existed = intersect(files, names);
  25612. } else {
  25613. $.each(names, function(i, v) {
  25614. exists[v.name] = v.hash;
  25615. });
  25616. existed = intersect(files, $.map(exists, function(h, n) { return n; }));
  25617. $.each(files, function(i, file) {
  25618. if (exists[file.name]) {
  25619. hashes[exists[file.name]] = file.name;
  25620. }
  25621. });
  25622. }
  25623. }
  25624. } else {
  25625. existedArr = [];
  25626. existed = $.map(names, function(n) {
  25627. if (typeof n === 'string') {
  25628. return n;
  25629. } else {
  25630. // support to >=2.1.11 plugin Normalizer, Sanitizer
  25631. existedArr = existedArr.concat(n);
  25632. return false;
  25633. }
  25634. });
  25635. if (existedArr.length) {
  25636. existed = existed.concat(existedArr);
  25637. }
  25638. existed = intersect(files, existed);
  25639. hashes = names;
  25640. }
  25641. }
  25642. existed.length ? confirm(0) : paste(files);
  25643. },
  25644. paste = function(selFiles) {
  25645. var renames = [],
  25646. files = $.grep(selFiles, function(file) {
  25647. if (file.rename) {
  25648. renames.push(file.name);
  25649. }
  25650. return !file.remove ? true : false;
  25651. }),
  25652. cnt = files.length,
  25653. groups = {},
  25654. args = [],
  25655. targets, reqData;
  25656. if (!cnt) {
  25657. return dfrd.resolve();
  25658. }
  25659. targets = $.map(files, function(f) { return f.hash; });
  25660. reqData = {cmd : 'paste', dst : dst.hash, targets : targets, cut : cut ? 1 : 0, renames : renames, hashes : hashes, suffix : fm.options.backupSuffix};
  25661. if (fm.api < 2.1) {
  25662. reqData.src = files[0].phash;
  25663. }
  25664. fm.request({
  25665. data : reqData,
  25666. notify : {type : cmd, cnt : cnt},
  25667. cancel : true,
  25668. navigate : {
  25669. toast : opts.noToast? {} : {
  25670. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmd' + cmd)]), action: {
  25671. cmd: 'open',
  25672. msg: 'cmdopendir',
  25673. data: [dst.hash],
  25674. done: 'select',
  25675. cwdNot: dst.hash
  25676. }}
  25677. }
  25678. }
  25679. })
  25680. .done(function(data) {
  25681. var dsts = {},
  25682. added = data.added && data.added.length? data.added : null;
  25683. if (cut && added) {
  25684. // undo/redo
  25685. $.each(files, function(i, f) {
  25686. var phash = f.phash,
  25687. srcHash = function(name) {
  25688. var hash;
  25689. $.each(added, function(i, f) {
  25690. if (f.name === name) {
  25691. hash = f.hash;
  25692. return false;
  25693. }
  25694. });
  25695. return hash;
  25696. },
  25697. shash = srcHash(f.name);
  25698. if (shash) {
  25699. if (dsts[phash]) {
  25700. dsts[phash].push(shash);
  25701. } else {
  25702. dsts[phash] = [ shash ];
  25703. }
  25704. }
  25705. });
  25706. if (Object.keys(dsts).length) {
  25707. data.undo = {
  25708. cmd : 'move',
  25709. callback : function() {
  25710. var reqs = [];
  25711. $.each(dsts, function(dst, targets) {
  25712. reqs.push(fm.request({
  25713. data : {cmd : 'paste', dst : dst, targets : targets, cut : 1},
  25714. notify : {type : 'undo', cnt : targets.length}
  25715. }));
  25716. });
  25717. return $.when.apply(null, reqs);
  25718. }
  25719. };
  25720. data.redo = {
  25721. cmd : 'move',
  25722. callback : function() {
  25723. return fm.request({
  25724. data : reqData,
  25725. notify : {type : 'redo', cnt : cnt}
  25726. });
  25727. }
  25728. };
  25729. }
  25730. }
  25731. dfrd.resolve(data);
  25732. })
  25733. .fail(function(flg) {
  25734. dfrd.reject();
  25735. if (flg === 0) {
  25736. // canceling
  25737. fm.sync();
  25738. }
  25739. })
  25740. .always(function() {
  25741. fm.unlockfiles({files : files});
  25742. });
  25743. },
  25744. internames;
  25745. if (!fm.isCommandEnabled(self.name, dst.hash) || !files.length) {
  25746. return dfrd.resolve();
  25747. }
  25748. if (fm.oldAPI) {
  25749. paste(files);
  25750. } else {
  25751. if (!fm.option('copyOverwrite', dst.hash)) {
  25752. paste(files);
  25753. } else {
  25754. internames = $.map(files, function(f) { return f.name; });
  25755. dst.hash == fm.cwd().hash
  25756. ? valid($.map(fm.files(), function(file) { return file.phash == dst.hash ? {hash: file.hash, name: file.name} : null; }))
  25757. : fm.request({
  25758. data : {cmd : 'ls', target : dst.hash, intersect : internames},
  25759. notify : {type : 'prepare', cnt : 1, hideCnt : true},
  25760. preventFail : true
  25761. })
  25762. .always(function(data) {
  25763. valid(data.list);
  25764. });
  25765. }
  25766. }
  25767. return dfrd;
  25768. },
  25769. parents, fparents, cutDfrd;
  25770. if (!cnt || !dst || dst.mime != 'directory') {
  25771. return dfrd.reject();
  25772. }
  25773. if (!dst.write) {
  25774. return dfrd.reject([error, files[0].name, 'errPerm']);
  25775. }
  25776. parents = fm.parents(dst.hash);
  25777. $.each(files, function(i, file) {
  25778. if (!file.read) {
  25779. return !dfrd.reject([error, file.name, 'errPerm']);
  25780. }
  25781. if (cut && file.locked) {
  25782. return !dfrd.reject(['errLocked', file.name]);
  25783. }
  25784. if ($.inArray(file.hash, parents) !== -1) {
  25785. return !dfrd.reject(['errCopyInItself', file.name]);
  25786. }
  25787. if (file.mime && file.mime !== 'directory' && ! fm.uploadMimeCheck(file.mime, dst.hash)) {
  25788. return !dfrd.reject([error, file.name, 'errUploadMime']);
  25789. }
  25790. fparents = fm.parents(file.hash);
  25791. fparents.pop();
  25792. if ($.inArray(dst.hash, fparents) !== -1) {
  25793. if ($.grep(fparents, function(h) { var d = fm.file(h); return d.phash == dst.hash && d.name == file.name ? true : false; }).length) {
  25794. return !dfrd.reject(['errReplByChild', file.name]);
  25795. }
  25796. }
  25797. if (file.phash == dst.hash) {
  25798. fcopy.push(file.hash);
  25799. } else {
  25800. fpaste.push({
  25801. hash : file.hash,
  25802. phash : file.phash,
  25803. name : file.name
  25804. });
  25805. }
  25806. });
  25807. if (dfrd.state() === 'rejected') {
  25808. return dfrd;
  25809. }
  25810. cutDfrd = $.Deferred();
  25811. if (cut && self.options.moveConfirm) {
  25812. fm.confirm({
  25813. title : 'moveFiles',
  25814. text : fm.i18n('confirmMove', dst.i18 || dst.name),
  25815. accept : {
  25816. label : 'btnYes',
  25817. callback : function() {
  25818. cutDfrd.resolve();
  25819. }
  25820. },
  25821. cancel : {
  25822. label : 'btnCancel',
  25823. callback : function() {
  25824. cutDfrd.reject();
  25825. }
  25826. }
  25827. });
  25828. } else {
  25829. cutDfrd.resolve();
  25830. }
  25831. cutDfrd.done(function() {
  25832. $.when(
  25833. copy(fcopy),
  25834. paste(fpaste)
  25835. )
  25836. .done(function(cr, pr) {
  25837. dfrd.resolve(pr && pr.undo? pr : void(0));
  25838. })
  25839. .fail(function() {
  25840. dfrd.reject();
  25841. })
  25842. .always(function() {
  25843. cut && fm.clipboard([]);
  25844. });
  25845. }).fail(function() {
  25846. dfrd.reject();
  25847. });
  25848. return dfrd;
  25849. };
  25850. };
  25851. /*
  25852. * File: /js/commands/places.js
  25853. */
  25854. /**
  25855. * @class elFinder command "places"
  25856. * Regist to Places
  25857. *
  25858. * @author Naoki Sawada
  25859. **/
  25860. elFinder.prototype.commands.places = function() {
  25861. var self = this,
  25862. fm = this.fm,
  25863. filter = function(hashes) {
  25864. return $.grep(self.files(hashes), function(f) { return f.mime == 'directory' ? true : false; });
  25865. },
  25866. places = null;
  25867. this.getstate = function(select) {
  25868. var sel = this.hashes(select),
  25869. cnt = sel.length;
  25870. return places && cnt && cnt == filter(sel).length ? 0 : -1;
  25871. };
  25872. this.exec = function(hashes) {
  25873. var files = this.files(hashes);
  25874. places.trigger('regist', [ files ]);
  25875. return $.Deferred().resolve();
  25876. };
  25877. fm.one('load', function(){
  25878. places = fm.ui.places;
  25879. });
  25880. };
  25881. /*
  25882. * File: /js/commands/preference.js
  25883. */
  25884. /**
  25885. * @class elFinder command "preference"
  25886. * "Preference" dialog
  25887. *
  25888. * @author Naoki Sawada
  25889. **/
  25890. elFinder.prototype.commands.preference = function() {
  25891. var self = this,
  25892. fm = this.fm,
  25893. r = 'replace',
  25894. tab = '<li class="' + fm.res('class', 'tabstab') + ' elfinder-preference-tab-{id}"><a href="#'+fm.namespace+'-preference-{id}" id="'+fm.namespace+'-preference-tab-{id}" class="ui-tabs-anchor {class}">{title}</a></li>',
  25895. base = $('<div class="ui-tabs ui-widget ui-widget-content ui-corner-all elfinder-preference">'),
  25896. ul = $('<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-top">'),
  25897. tabs = $('<div class="elfinder-preference-tabs ui-tabs-panel ui-widget-content ui-corner-bottom"></div>'),
  25898. sep = '<div class="elfinder-preference-separator"></div>',
  25899. selfUrl = $('base').length? document.location.href.replace(/#.*$/, '') : '',
  25900. selectTab = function(tab) {
  25901. $('#'+fm.namespace+'-preference-tab-'+tab).trigger('mouseover').trigger('click');
  25902. openTab = tab;
  25903. },
  25904. clTabActive = fm.res('class', 'tabsactive'),
  25905. build = function() {
  25906. var cats = self.options.categories || {
  25907. 'language' : ['language'],
  25908. 'theme' : ['theme'],
  25909. 'toolbar' : ['toolbarPref'],
  25910. 'workspace' : ['iconSize','columnPref', 'selectAction', 'makefileTypes', 'useStoredEditor', 'editorMaximized', 'useFullscreen', 'showHidden'],
  25911. 'dialog' : ['autoFocusDialog'],
  25912. 'selectionInfo' : ['infoItems', 'hashChecker'],
  25913. 'reset' : ['clearBrowserData'],
  25914. 'all' : true
  25915. },
  25916. forms = self.options.prefs || ['language', 'theme', 'toolbarPref', 'iconSize', 'columnPref', 'selectAction', 'makefileTypes', 'useStoredEditor', 'editorMaximized', 'useFullscreen', 'showHidden', 'infoItems', 'hashChecker', 'autoFocusDialog', 'clearBrowserData'];
  25917. if (!fm.cookieEnabled) {
  25918. delete cats.language;
  25919. }
  25920. forms = fm.arrayFlip(forms, true);
  25921. if (fm.options.getFileCallback) {
  25922. delete forms.selectAction;
  25923. }
  25924. if (!fm.UA.Fullscreen) {
  25925. delete forms.useFullscreen;
  25926. }
  25927. forms.language && (forms.language = (function() {
  25928. var langSel = $('<select></select>').on('change', function() {
  25929. var lang = $(this).val();
  25930. fm.storage('lang', lang);
  25931. $('#'+fm.id).elfinder('reload');
  25932. }),
  25933. optTags = [],
  25934. langs = self.options.langs || {
  25935. ar: 'العربية',
  25936. bg: 'Български',
  25937. ca: 'Català',
  25938. cs: 'Čeština',
  25939. da: 'Dansk',
  25940. de: 'Deutsch',
  25941. el: 'Ελληνικά',
  25942. en: 'English',
  25943. es: 'Español',
  25944. fa: 'فارسی',
  25945. fo: 'Føroyskt',
  25946. fr: 'Français',
  25947. fr_CA: 'Français (Canada)',
  25948. he: 'עברית',
  25949. hr: 'Hrvatski',
  25950. hu: 'Magyar',
  25951. id: 'Bahasa Indonesia',
  25952. it: 'Italiano',
  25953. ja: '日本語',
  25954. ko: '한국어',
  25955. nl: 'Nederlands',
  25956. no: 'Norsk',
  25957. pl: 'Polski',
  25958. pt_BR: 'Português',
  25959. ro: 'Română',
  25960. ru: 'Pусский',
  25961. si: 'සිංහල',
  25962. sk: 'Slovenčina',
  25963. sl: 'Slovenščina',
  25964. sr: 'Srpski',
  25965. sv: 'Svenska',
  25966. tr: 'Türkçe',
  25967. ug_CN: 'ئۇيغۇرچە',
  25968. uk: 'Український',
  25969. vi: 'Tiếng Việt',
  25970. zh_CN: '简体中文',
  25971. zh_TW: '正體中文'
  25972. };
  25973. if (!fm.cookieEnabled) {
  25974. return $();
  25975. }
  25976. $.each(langs, function(lang, name) {
  25977. optTags.push('<option value="'+lang+'">'+name+'</option>');
  25978. });
  25979. return langSel.append(optTags.join('')).val(fm.lang);
  25980. })());
  25981. forms.theme && (forms.theme = (function() {
  25982. var cnt = fm.options.themes? Object.keys(fm.options.themes).length : 0;
  25983. if (cnt === 0 || (cnt === 1 && fm.options.themes.default)) {
  25984. return null;
  25985. }
  25986. var themeSel = $('<select></select>').on('change', function() {
  25987. var theme = $(this).val();
  25988. fm.changeTheme(theme).storage('theme', theme);
  25989. }),
  25990. optTags = [],
  25991. tpl = {
  25992. image: '<img class="elfinder-preference-theme elfinder-preference-theme-image" src="$2" />',
  25993. link: '<a href="$1" target="_blank" title="$3">$2</a>',
  25994. data: '<dt>$1</dt><dd><span class="elfinder-preference-theme elfinder-preference-theme-$0">$2</span></dd>'
  25995. },
  25996. items = ['image', 'description', 'author', 'email', 'license'],
  25997. render = function(key, data) {
  25998. },
  25999. defBtn = $('<button class="ui-button ui-corner-all ui-widget elfinder-preference-theme-default"></button>').text(fm.i18n('default')).on('click', function(e) {
  26000. themeSel.val('default').trigger('change');
  26001. }),
  26002. list = $('<div class="elfinder-reference-hide-taball"></div>').on('click', 'button', function() {
  26003. var val = $(this).data('themeid');
  26004. themeSel.val(val).trigger('change');
  26005. });
  26006. if (!fm.options.themes.default) {
  26007. themeSel.append('<option value="default">'+fm.i18n('default')+'</option>');
  26008. }
  26009. $.each(fm.options.themes, function(id, val) {
  26010. var opt = $('<option class="elfinder-theme-option-'+id+'" value="'+id+'">'+fm.i18n(id)+'</option>'),
  26011. dsc = $('<fieldset class="ui-widget ui-widget-content ui-corner-all elfinder-theme-list-'+id+'"><legend>'+fm.i18n(id)+'</legend><div><span class="elfinder-spinner"></span></div></fieldset>'),
  26012. tm;
  26013. themeSel.append(opt);
  26014. list.append(dsc);
  26015. tm = setTimeout(function() {
  26016. dsc.find('span.elfinder-spinner').replaceWith(fm.i18n(['errRead', id]));
  26017. }, 10000);
  26018. fm.getTheme(id).always(function() {
  26019. tm && clearTimeout(tm);
  26020. }).done(function(data) {
  26021. var link, val = $(), dl = $('<dl></dl>');
  26022. link = data.link? tpl.link.replace(/\$1/g, data.link).replace(/\$3/g, fm.i18n('website')) : '$2';
  26023. if (data.name) {
  26024. opt.html(fm.i18n(data.name));
  26025. }
  26026. dsc.children('legend').html(link.replace(/\$2/g, fm.i18n(data.name) || id));
  26027. $.each(items, function(i, key) {
  26028. var t = tpl[key] || tpl.data,
  26029. elm;
  26030. if (data[key]) {
  26031. elm = t.replace(/\$0/g, fm.escape(key)).replace(/\$1/g, fm.i18n(key)).replace(/\$2/g, fm.i18n(data[key]));
  26032. if (key === 'image' && data.link) {
  26033. elm = $(elm).on('click', function() {
  26034. themeSel.val(id).trigger('change');
  26035. }).attr('title', fm.i18n('select'));
  26036. }
  26037. dl.append(elm);
  26038. }
  26039. });
  26040. val = val.add(dl);
  26041. val = val.add($('<div class="elfinder-preference-theme-btn"></div>').append($('<button class="ui-button ui-corner-all ui-widget"></button>').data('themeid', id).html(fm.i18n('select'))));
  26042. dsc.find('span.elfinder-spinner').replaceWith(val);
  26043. }).fail(function() {
  26044. dsc.find('span.elfinder-spinner').replaceWith(fm.i18n(['errRead', id]));
  26045. });
  26046. });
  26047. return $('<div></div>').append(themeSel.val(fm.theme && fm.theme.id? fm.theme.id : 'default'), defBtn, list);
  26048. })());
  26049. forms.toolbarPref && (forms.toolbarPref = (function() {
  26050. var pnls = $.map(fm.options.uiOptions.toolbar, function(v) {
  26051. return $.isArray(v)? v : null;
  26052. }),
  26053. tags = [],
  26054. hides = fm.storage('toolbarhides') || {};
  26055. $.each(pnls, function() {
  26056. var cmd = this,
  26057. name = fm.i18n('cmd'+cmd);
  26058. if (name === 'cmd'+cmd) {
  26059. name = fm.i18n(cmd);
  26060. }
  26061. tags.push('<span class="elfinder-preference-toolbar-item"><label><input type="checkbox" value="'+cmd+'" '+(hides[cmd]? '' : 'checked')+'/>'+name+'</label></span>');
  26062. });
  26063. return $(tags.join(' ')).on('change', 'input', function() {
  26064. var v = $(this).val(),
  26065. o = $(this).is(':checked');
  26066. if (!o && !hides[v]) {
  26067. hides[v] = true;
  26068. } else if (o && hides[v]) {
  26069. delete hides[v];
  26070. }
  26071. fm.storage('toolbarhides', hides);
  26072. fm.trigger('toolbarpref');
  26073. });
  26074. })());
  26075. forms.iconSize && (forms.iconSize = (function() {
  26076. var max = fm.options.uiOptions.cwd.iconsView.sizeMax || 3,
  26077. size = fm.storage('iconsize') || fm.options.uiOptions.cwd.iconsView.size || 0,
  26078. sld = $('<div class="touch-punch"></div>').slider({
  26079. classes: {
  26080. 'ui-slider-handle': 'elfinder-tabstop',
  26081. },
  26082. value: size,
  26083. max: max,
  26084. slide: function(e, ui) {
  26085. fm.getUI('cwd').trigger('iconpref', {size: ui.value});
  26086. },
  26087. change: function(e, ui) {
  26088. fm.storage('iconsize', ui.value);
  26089. }
  26090. });
  26091. fm.getUI('cwd').on('iconpref', function(e, data) {
  26092. sld.slider('option', 'value', data.size);
  26093. });
  26094. return sld;
  26095. })());
  26096. forms.columnPref && (forms.columnPref = (function() {
  26097. var cols = fm.options.uiOptions.cwd.listView.columns,
  26098. tags = [],
  26099. hides = fm.storage('columnhides') || {};
  26100. $.each(cols, function() {
  26101. var key = this,
  26102. name = fm.getColumnName(key);
  26103. tags.push('<span class="elfinder-preference-column-item"><label><input type="checkbox" value="'+key+'" '+(hides[key]? '' : 'checked')+'/>'+name+'</label></span>');
  26104. });
  26105. return $(tags.join(' ')).on('change', 'input', function() {
  26106. var v = $(this).val(),
  26107. o = $(this).is(':checked');
  26108. if (!o && !hides[v]) {
  26109. hides[v] = true;
  26110. } else if (o && hides[v]) {
  26111. delete hides[v];
  26112. }
  26113. fm.storage('columnhides', hides);
  26114. fm.trigger('columnpref', { repaint: true });
  26115. });
  26116. })());
  26117. forms.selectAction && (forms.selectAction = (function() {
  26118. var actSel = $('<select></select>').on('change', function() {
  26119. var act = $(this).val();
  26120. fm.storage('selectAction', act === 'default'? null : act);
  26121. }),
  26122. optTags = [],
  26123. acts = self.options.selectActions,
  26124. defAct = fm.getCommand('open').options.selectAction || 'open';
  26125. if ($.inArray(defAct, acts) === -1) {
  26126. acts.unshift(defAct);
  26127. }
  26128. $.each(acts, function(i, act) {
  26129. var names = $.map(act.split('/'), function(cmd) {
  26130. var name = fm.i18n('cmd'+cmd);
  26131. if (name === 'cmd'+cmd) {
  26132. name = fm.i18n(cmd);
  26133. }
  26134. return name;
  26135. });
  26136. optTags.push('<option value="'+act+'">'+names.join('/')+'</option>');
  26137. });
  26138. return actSel.append(optTags.join('')).val(fm.storage('selectAction') || defAct);
  26139. })());
  26140. forms.makefileTypes && (forms.makefileTypes = (function() {
  26141. var hides = fm.getCommand('edit').getMkfileHides(),
  26142. getTag = function() {
  26143. var tags = [];
  26144. // re-assign hides
  26145. hides = fm.getCommand('edit').getMkfileHides();
  26146. $.each(fm.mimesCanMakeEmpty, function(mime, type) {
  26147. var name = fm.getCommand('mkfile').getTypeName(mime, type);
  26148. tags.push('<span class="elfinder-preference-column-item" title="'+fm.escape(name)+'"><label><input type="checkbox" value="'+mime+'" '+(hides[mime]? '' : 'checked')+'/>'+type+'</label></span>');
  26149. });
  26150. return tags.join(' ');
  26151. },
  26152. elm = $('<div></div>').on('change', 'input', function() {
  26153. var v = $(this).val(),
  26154. o = $(this).is(':checked');
  26155. if (!o && !hides[v]) {
  26156. hides[v] = true;
  26157. } else if (o && hides[v]) {
  26158. delete hides[v];
  26159. }
  26160. fm.storage('mkfileHides', hides);
  26161. fm.trigger('canMakeEmptyFile');
  26162. }).append(getTag()),
  26163. add = $('<div></div>').append(
  26164. $('<input type="text" placeholder="'+fm.i18n('typeOfTextfile')+'"/>').on('keydown', function(e) {
  26165. (e.keyCode === $.ui.keyCode.ENTER) && $(this).next().trigger('click');
  26166. }),
  26167. $('<button class="ui-button"></button>').html(fm.i18n('add')).on('click', function() {
  26168. var input = $(this).prev(),
  26169. val = input.val(),
  26170. uiToast = fm.getUI('toast'),
  26171. err = function() {
  26172. uiToast.appendTo(input.closest('.ui-dialog'));
  26173. fm.toast({
  26174. msg: fm.i18n('errUsupportType'),
  26175. mode: 'warning',
  26176. onHidden: function() {
  26177. uiToast.children().length === 1 && uiToast.appendTo(fm.getUI());
  26178. }
  26179. });
  26180. input.trigger('focus');
  26181. return false;
  26182. },
  26183. tmpMimes;
  26184. if (!val.match(/\//)) {
  26185. val = fm.arrayFlip(fm.mimeTypes)[val];
  26186. if (!val) {
  26187. return err();
  26188. }
  26189. input.val(val);
  26190. }
  26191. if (!fm.mimeIsText(val) || !fm.mimeTypes[val]) {
  26192. return err();
  26193. }
  26194. fm.trigger('canMakeEmptyFile', {mimes: [val], unshift: true});
  26195. tmpMimes = {};
  26196. tmpMimes[val] = fm.mimeTypes[val];
  26197. fm.storage('mkfileTextMimes', Object.assign(tmpMimes, fm.storage('mkfileTextMimes') || {}));
  26198. input.val('');
  26199. uiToast.appendTo(input.closest('.ui-dialog'));
  26200. fm.toast({
  26201. msg: fm.i18n(['complete', val + ' (' + tmpMimes[val] + ')']),
  26202. onHidden: function() {
  26203. uiToast.children().length === 1 && uiToast.appendTo(fm.getUI());
  26204. }
  26205. });
  26206. }),
  26207. $('<button class="ui-button"></button>').html(fm.i18n('reset')).on('click', function() {
  26208. fm.one('canMakeEmptyFile', {done: function() {
  26209. elm.empty().append(getTag());
  26210. }});
  26211. fm.trigger('canMakeEmptyFile', {resetTexts: true});
  26212. })
  26213. ),
  26214. tm;
  26215. fm.bind('canMakeEmptyFile', {done: function(e) {
  26216. if (e.data && e.data.mimes && e.data.mimes.length) {
  26217. elm.empty().append(getTag());
  26218. }
  26219. }});
  26220. return $('<div></div>').append(elm, add);
  26221. })());
  26222. forms.useStoredEditor && (forms.useStoredEditor = $('<input type="checkbox"/>').prop('checked', (function() {
  26223. var s = fm.storage('useStoredEditor');
  26224. return s? (s > 0) : fm.options.commandsOptions.edit.useStoredEditor;
  26225. })()).on('change', function(e) {
  26226. fm.storage('useStoredEditor', $(this).is(':checked')? 1 : -1);
  26227. }));
  26228. forms.editorMaximized && (forms.editorMaximized = $('<input type="checkbox"/>').prop('checked', (function() {
  26229. var s = fm.storage('editorMaximized');
  26230. return s? (s > 0) : fm.options.commandsOptions.edit.editorMaximized;
  26231. })()).on('change', function(e) {
  26232. fm.storage('editorMaximized', $(this).is(':checked')? 1 : -1);
  26233. }));
  26234. forms.useFullscreen && (forms.useFullscreen = $('<input type="checkbox"/>').prop('checked', (function() {
  26235. var s = fm.storage('useFullscreen');
  26236. return s? (s > 0) : fm.options.commandsOptions.fullscreen.mode === 'screen';
  26237. })()).on('change', function(e) {
  26238. fm.storage('useFullscreen', $(this).is(':checked')? 1 : -1);
  26239. }));
  26240. if (forms.showHidden) {
  26241. (function() {
  26242. var setTitle = function() {
  26243. var s = fm.storage('hide'),
  26244. t = [],
  26245. v;
  26246. if (s && s.items) {
  26247. $.each(s.items, function(h, n) {
  26248. t.push(fm.escape(n));
  26249. });
  26250. }
  26251. elms.prop('disabled', !t.length)[t.length? 'removeClass' : 'addClass']('ui-state-disabled');
  26252. v = t.length? t.join('\n') : '';
  26253. forms.showHidden.attr('title',v);
  26254. useTooltip && forms.showHidden.tooltip('option', 'content', v.replace(/\n/g, '<br>')).tooltip('close');
  26255. },
  26256. chk = $('<input type="checkbox"/>').prop('checked', (function() {
  26257. var s = fm.storage('hide');
  26258. return s && s.show;
  26259. })()).on('change', function(e) {
  26260. var o = {};
  26261. o[$(this).is(':checked')? 'show' : 'hide'] = true;
  26262. fm.exec('hide', void(0), o);
  26263. }),
  26264. btn = $('<button class="ui-button ui-corner-all ui-widget"></button>').append(fm.i18n('reset')).on('click', function() {
  26265. fm.exec('hide', void(0), {reset: true});
  26266. $(this).parent().find('input:first').prop('checked', false);
  26267. setTitle();
  26268. }),
  26269. elms = $().add(chk).add(btn),
  26270. useTooltip;
  26271. forms.showHidden = $('<div></div>').append(chk, btn);
  26272. fm.bind('hide', function(e) {
  26273. var d = e.data;
  26274. if (!d.opts || (!d.opts.show && !d.opts.hide)) {
  26275. setTitle();
  26276. }
  26277. });
  26278. if (fm.UA.Mobile && $.fn.tooltip) {
  26279. useTooltip = true;
  26280. forms.showHidden.tooltip({
  26281. classes: {
  26282. 'ui-tooltip': 'elfinder-ui-tooltip ui-widget-shadow'
  26283. },
  26284. tooltipClass: 'elfinder-ui-tooltip ui-widget-shadow',
  26285. track: true
  26286. }).css('user-select', 'none');
  26287. btn.css('user-select', 'none');
  26288. }
  26289. setTitle();
  26290. })();
  26291. }
  26292. forms.infoItems && (forms.infoItems = (function() {
  26293. var items = fm.getCommand('info').items,
  26294. tags = [],
  26295. hides = fm.storage('infohides') || fm.arrayFlip(fm.options.commandsOptions.info.hideItems, true);
  26296. $.each(items, function() {
  26297. var key = this,
  26298. name = fm.i18n(key);
  26299. tags.push('<span class="elfinder-preference-info-item"><label><input type="checkbox" value="'+key+'" '+(hides[key]? '' : 'checked')+'/>'+name+'</label></span>');
  26300. });
  26301. return $(tags.join(' ')).on('change', 'input', function() {
  26302. var v = $(this).val(),
  26303. o = $(this).is(':checked');
  26304. if (!o && !hides[v]) {
  26305. hides[v] = true;
  26306. } else if (o && hides[v]) {
  26307. delete hides[v];
  26308. }
  26309. fm.storage('infohides', hides);
  26310. fm.trigger('infopref', { repaint: true });
  26311. });
  26312. })());
  26313. forms.hashChecker && fm.hashCheckers.length && (forms.hashChecker = (function() {
  26314. var tags = [],
  26315. enabled = fm.arrayFlip(fm.storage('hashchekcer') || fm.options.commandsOptions.info.showHashAlgorisms, true);
  26316. $.each(fm.hashCheckers, function() {
  26317. var cmd = this,
  26318. name = fm.i18n(cmd);
  26319. tags.push('<span class="elfinder-preference-hashchecker-item"><label><input type="checkbox" value="'+cmd+'" '+(enabled[cmd]? 'checked' : '')+'/>'+name+'</label></span>');
  26320. });
  26321. return $(tags.join(' ')).on('change', 'input', function() {
  26322. var v = $(this).val(),
  26323. o = $(this).is(':checked');
  26324. if (o) {
  26325. enabled[v] = true;
  26326. } else if (enabled[v]) {
  26327. delete enabled[v];
  26328. }
  26329. fm.storage('hashchekcer', $.grep(fm.hashCheckers, function(v) {
  26330. return enabled[v];
  26331. }));
  26332. });
  26333. })());
  26334. forms.autoFocusDialog && (forms.autoFocusDialog = $('<input type="checkbox"/>').prop('checked', (function() {
  26335. var s = fm.storage('autoFocusDialog');
  26336. return s? (s > 0) : fm.options.uiOptions.dialog.focusOnMouseOver;
  26337. })()).on('change', function(e) {
  26338. fm.storage('autoFocusDialog', $(this).is(':checked')? 1 : -1);
  26339. }));
  26340. forms.clearBrowserData && (forms.clearBrowserData = $('<button></button>').text(fm.i18n('reset')).button().on('click', function(e) {
  26341. e.preventDefault();
  26342. fm.storage();
  26343. $('#'+fm.id).elfinder('reload');
  26344. }));
  26345. $.each(cats, function(id, prefs) {
  26346. var dls, found;
  26347. if (prefs === true) {
  26348. found = 1;
  26349. } else if (prefs) {
  26350. dls = $();
  26351. $.each(prefs, function(i, n) {
  26352. var f, title, chks = '', cbox;
  26353. if (f = forms[n]) {
  26354. found = 2;
  26355. title = fm.i18n(n);
  26356. cbox = $(f).filter('input[type="checkbox"]');
  26357. if (!cbox.length) {
  26358. cbox = $(f).find('input[type="checkbox"]');
  26359. }
  26360. if (cbox.length === 1) {
  26361. if (!cbox.attr('id')) {
  26362. cbox.attr('id', 'elfinder-preference-'+n+'-checkbox');
  26363. }
  26364. title = '<label for="'+cbox.attr('id')+'">'+title+'</label>';
  26365. } else if (cbox.length > 1) {
  26366. chks = ' elfinder-preference-checkboxes';
  26367. }
  26368. dls = dls.add($('<dt class="elfinder-preference-'+n+chks+'">'+title+'</dt>')).add($('<dd class="elfinder-preference-'+n+chks+'"></dd>').append(f));
  26369. }
  26370. });
  26371. }
  26372. if (found) {
  26373. ul.append(tab[r](/\{id\}/g, id)[r](/\{title\}/, fm.i18n(id))[r](/\{class\}/, openTab === id? 'elfinder-focus' : ''));
  26374. if (found === 2) {
  26375. tabs.append(
  26376. $('<div id="'+fm.namespace+'-preference-'+id+'" class="elfinder-preference-content"></div>')
  26377. .hide()
  26378. .append($('<dl></dl>').append(dls))
  26379. );
  26380. }
  26381. }
  26382. });
  26383. ul.on('click', 'a', function(e) {
  26384. var t = $(e.target),
  26385. h = t.attr('href');
  26386. e.preventDefault();
  26387. e.stopPropagation();
  26388. ul.children().removeClass(clTabActive);
  26389. t.removeClass('ui-state-hover').parent().addClass(clTabActive);
  26390. if (h.match(/all$/)) {
  26391. tabs.addClass('elfinder-preference-taball').children().show();
  26392. } else {
  26393. tabs.removeClass('elfinder-preference-taball').children().hide();
  26394. $(h).show();
  26395. }
  26396. }).on('focus blur', 'a', function(e) {
  26397. $(this).parent().toggleClass('ui-state-focus', e.type === 'focusin');
  26398. }).on('mouseenter mouseleave', 'li', function(e) {
  26399. $(this).toggleClass('ui-state-hover', e.type === 'mouseenter');
  26400. });
  26401. tabs.find('a,input,select,button').addClass('elfinder-tabstop');
  26402. base.append(ul, tabs);
  26403. dialog = self.fmDialog(base, {
  26404. title : self.title,
  26405. width : self.options.width || 600,
  26406. height: self.options.height || 400,
  26407. maxWidth: 'window',
  26408. maxHeight: 'window',
  26409. autoOpen : false,
  26410. destroyOnClose : false,
  26411. allowMinimize : false,
  26412. open : function() {
  26413. openTab && selectTab(openTab);
  26414. openTab = null;
  26415. },
  26416. resize : function() {
  26417. tabs.height(dialog.height() - ul.outerHeight(true) - (tabs.outerHeight(true) - tabs.height()) - 5);
  26418. }
  26419. })
  26420. .on('click', function(e) {
  26421. e.stopPropagation();
  26422. })
  26423. .css({
  26424. overflow: 'hidden'
  26425. });
  26426. dialog.closest('.ui-dialog')
  26427. .css({
  26428. overflow: 'hidden'
  26429. })
  26430. .addClass('elfinder-bg-translucent');
  26431. openTab = 'all';
  26432. },
  26433. dialog, openTab;
  26434. this.shortcuts = [{
  26435. pattern : 'ctrl+comma',
  26436. description : this.title
  26437. }];
  26438. this.alwaysEnabled = true;
  26439. this.getstate = function() {
  26440. return 0;
  26441. };
  26442. this.exec = function(sel, cOpts) {
  26443. !dialog && build();
  26444. if (cOpts) {
  26445. if (cOpts.tab) {
  26446. selectTab(cOpts.tab);
  26447. } else if (cOpts._currentType === 'cwd') {
  26448. selectTab('workspace');
  26449. }
  26450. }
  26451. dialog.elfinderdialog('open');
  26452. return $.Deferred().resolve();
  26453. };
  26454. };
  26455. /*
  26456. * File: /js/commands/quicklook.js
  26457. */
  26458. /**
  26459. * @class elFinder command "quicklook"
  26460. * Fast preview for some files types
  26461. *
  26462. * @author Dmitry (dio) Levashov
  26463. **/
  26464. (elFinder.prototype.commands.quicklook = function() {
  26465. var self = this,
  26466. fm = self.fm,
  26467. /**
  26468. * window closed state
  26469. *
  26470. * @type Number
  26471. **/
  26472. closed = 0,
  26473. /**
  26474. * window animated state
  26475. *
  26476. * @type Number
  26477. **/
  26478. animated = 1,
  26479. /**
  26480. * window opened state
  26481. *
  26482. * @type Number
  26483. **/
  26484. opened = 2,
  26485. /**
  26486. * window docked state
  26487. *
  26488. * @type Number
  26489. **/
  26490. docked = 3,
  26491. /**
  26492. * window docked and hidden state
  26493. *
  26494. * @type Number
  26495. **/
  26496. dockedhidden = 4,
  26497. /**
  26498. * window state
  26499. *
  26500. * @type Number
  26501. **/
  26502. state = closed,
  26503. /**
  26504. * Event name of update
  26505. * for fix conflicts with Prototype.JS
  26506. *
  26507. * `@see https://github.com/Studio-42/elFinder/pull/2346
  26508. * @type String
  26509. **/
  26510. evUpdate = Element.update? 'quicklookupdate' : 'update',
  26511. /**
  26512. * navbar icon class
  26513. *
  26514. * @type String
  26515. **/
  26516. navicon = 'elfinder-quicklook-navbar-icon',
  26517. /**
  26518. * navbar "fullscreen" icon class
  26519. *
  26520. * @type String
  26521. **/
  26522. fullscreen = 'elfinder-quicklook-fullscreen',
  26523. /**
  26524. * info wrapper class
  26525. *
  26526. * @type String
  26527. */
  26528. infocls = 'elfinder-quicklook-info-wrapper',
  26529. /**
  26530. * Triger keydown/keypress event with left/right arrow key code
  26531. *
  26532. * @param Number left/right arrow key code
  26533. * @return void
  26534. **/
  26535. navtrigger = function(code) {
  26536. $(document).trigger($.Event('keydown', { keyCode: code, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false }));
  26537. },
  26538. /**
  26539. * Return css for closed window
  26540. *
  26541. * @param jQuery file node in cwd
  26542. * @return void
  26543. **/
  26544. closedCss = function(node) {
  26545. var elf = fm.getUI().offset(),
  26546. base = (function() {
  26547. var target = node.find('.elfinder-cwd-file-wrapper');
  26548. return target.length? target : node;
  26549. })(),
  26550. baseOffset = base.offset() || { top: 0, left: 0 };
  26551. return {
  26552. opacity : 0,
  26553. width : base.width(),
  26554. height : base.height() - 30,
  26555. top : baseOffset.top - elf.top,
  26556. left : baseOffset.left - elf.left
  26557. };
  26558. },
  26559. /**
  26560. * Return css for opened window
  26561. *
  26562. * @return void
  26563. **/
  26564. openedCss = function() {
  26565. var contain = self.options.contain || fm.options.dialogContained,
  26566. win = contain? fm.getUI() : $(window),
  26567. elf = fm.getUI().offset(),
  26568. w = Math.min(width, win.width()-10),
  26569. h = Math.min(height, win.height()-80);
  26570. return {
  26571. opacity : 1,
  26572. width : w,
  26573. height : h,
  26574. top : parseInt((win.height() - h - 60) / 2 + (contain? 0 : win.scrollTop() - elf.top)),
  26575. left : parseInt((win.width() - w) / 2 + (contain? 0 : win.scrollLeft() - elf.left))
  26576. };
  26577. },
  26578. mediaNode = {},
  26579. support = function(codec, name) {
  26580. var node = name || codec.substr(0, codec.indexOf('/')),
  26581. media = mediaNode[node]? mediaNode[node] : (mediaNode[node] = document.createElement(node)),
  26582. value = false;
  26583. try {
  26584. value = media.canPlayType && media.canPlayType(codec);
  26585. } catch(e) {}
  26586. return (value && value !== '' && value != 'no')? true : false;
  26587. },
  26588. platformWin = (window.navigator.platform.indexOf('Win') != -1),
  26589. /**
  26590. * Opened window width (from config)
  26591. *
  26592. * @type Number
  26593. **/
  26594. width,
  26595. /**
  26596. * Opened window height (from config)
  26597. *
  26598. * @type Number
  26599. **/
  26600. height,
  26601. /**
  26602. * Previous style before docked
  26603. *
  26604. * @type String
  26605. **/
  26606. prevStyle,
  26607. /**
  26608. * elFinder node
  26609. *
  26610. * @type jQuery
  26611. **/
  26612. parent,
  26613. /**
  26614. * elFinder current directory node
  26615. *
  26616. * @type jQuery
  26617. **/
  26618. cwd,
  26619. /**
  26620. * Current directory hash
  26621. *
  26622. * @type String
  26623. **/
  26624. cwdHash,
  26625. dockEnabled = false,
  26626. navdrag = false,
  26627. navmove = false,
  26628. navtm = null,
  26629. leftKey = $.ui.keyCode.LEFT,
  26630. rightKey = $.ui.keyCode.RIGHT,
  26631. coverEv = 'mousemove touchstart ' + ('onwheel' in document? 'wheel' : 'onmousewheel' in document? 'mousewheel' : 'DOMMouseScroll'),
  26632. title = $('<span class="elfinder-dialog-title elfinder-quicklook-title"></span>'),
  26633. icon = $('<div></div>'),
  26634. info = $('<div class="elfinder-quicklook-info"></div>'),//.hide(),
  26635. cover = $('<div class="ui-front elfinder-quicklook-cover"></div>'),
  26636. fsicon = $('<div class="'+navicon+' '+navicon+'-fullscreen"></div>')
  26637. .on('click touchstart', function(e) {
  26638. if (navmove) {
  26639. return;
  26640. }
  26641. var win = self.window,
  26642. full = win.hasClass(fullscreen),
  26643. $window = $(window),
  26644. resize = function() { self.preview.trigger('changesize'); };
  26645. e.stopPropagation();
  26646. e.preventDefault();
  26647. if (full) {
  26648. navStyle = '';
  26649. navShow();
  26650. win.toggleClass(fullscreen)
  26651. .css(win.data('position'));
  26652. $window.trigger(self.resize).off(self.resize, resize);
  26653. navbar.off('mouseenter mouseleave');
  26654. cover.off(coverEv);
  26655. } else {
  26656. win.toggleClass(fullscreen)
  26657. .data('position', {
  26658. left : win.css('left'),
  26659. top : win.css('top'),
  26660. width : win.width(),
  26661. height : win.height(),
  26662. display: 'block'
  26663. })
  26664. .removeAttr('style');
  26665. $(window).on(self.resize, resize)
  26666. .trigger(self.resize);
  26667. cover.on(coverEv, function(e) {
  26668. if (! navdrag) {
  26669. if (e.type === 'mousemove' || e.type === 'touchstart') {
  26670. navShow();
  26671. navtm = setTimeout(function() {
  26672. if (fm.UA.Mobile || navbar.parent().find('.elfinder-quicklook-navbar:hover').length < 1) {
  26673. navbar.fadeOut('slow', function() {
  26674. cover.show();
  26675. });
  26676. }
  26677. }, 3000);
  26678. }
  26679. if (cover.is(':visible')) {
  26680. coverHide();
  26681. cover.data('tm', setTimeout(function() {
  26682. cover.show();
  26683. }, 3000));
  26684. }
  26685. }
  26686. }).show().trigger('mousemove');
  26687. navbar.on('mouseenter mouseleave', function(e) {
  26688. if (! navdrag) {
  26689. if (e.type === 'mouseenter') {
  26690. navShow();
  26691. } else {
  26692. cover.trigger('mousemove');
  26693. }
  26694. }
  26695. });
  26696. }
  26697. if (fm.zIndex) {
  26698. win.css('z-index', fm.zIndex + 1);
  26699. }
  26700. if (fm.UA.Mobile) {
  26701. navbar.attr('style', navStyle);
  26702. } else {
  26703. navbar.attr('style', navStyle).draggable(full ? 'destroy' : {
  26704. start: function() {
  26705. navdrag = true;
  26706. navmove = true;
  26707. cover.show();
  26708. navShow();
  26709. },
  26710. stop: function() {
  26711. navdrag = false;
  26712. navStyle = self.navbar.attr('style');
  26713. requestAnimationFrame(function() {
  26714. navmove = false;
  26715. });
  26716. }
  26717. });
  26718. }
  26719. $(this).toggleClass(navicon+'-fullscreen-off');
  26720. var collection = win;
  26721. if (parent.is('.ui-resizable')) {
  26722. collection = collection.add(parent);
  26723. }
  26724. collection.resizable(full ? 'enable' : 'disable').removeClass('ui-state-disabled');
  26725. win.trigger('viewchange');
  26726. }
  26727. ),
  26728. updateOnSel = function() {
  26729. self.update(void(0), (function() {
  26730. var fm = self.fm,
  26731. files = fm.selectedFiles(),
  26732. cnt = files.length,
  26733. inDock = self.docked(),
  26734. getInfo = function() {
  26735. var ts = 0;
  26736. $.each(files, function(i, f) {
  26737. var t = parseInt(f.ts);
  26738. if (ts >= 0) {
  26739. if (t > ts) {
  26740. ts = t;
  26741. }
  26742. } else {
  26743. ts = 'unknown';
  26744. }
  26745. });
  26746. return {
  26747. hash : files[0].hash + '/' + (+new Date()),
  26748. name : fm.i18n('items') + ': ' + cnt,
  26749. mime : 'group',
  26750. size : spinner,
  26751. ts : ts,
  26752. files : $.map(files, function(f) { return f.hash; }),
  26753. getSize : true
  26754. };
  26755. };
  26756. if (! cnt) {
  26757. cnt = 1;
  26758. files = [fm.cwd()];
  26759. }
  26760. return (cnt === 1)? files[0] : getInfo();
  26761. })());
  26762. },
  26763. navShow = function() {
  26764. if (self.window.hasClass(fullscreen)) {
  26765. navtm && clearTimeout(navtm);
  26766. navtm = null;
  26767. // if use `show()` it make infinite loop with old jQuery (jQuery/jQuery UI: 1.8.0/1.9.0)
  26768. // see #1478 https://github.com/Studio-42/elFinder/issues/1478
  26769. navbar.stop(true, true).css('display', 'block');
  26770. coverHide();
  26771. }
  26772. },
  26773. coverHide = function() {
  26774. cover.data('tm') && clearTimeout(cover.data('tm'));
  26775. cover.removeData('tm');
  26776. cover.hide();
  26777. },
  26778. prev = $('<div class="'+navicon+' '+navicon+'-prev"></div>').on('click touchstart', function(e) { ! navmove && navtrigger(leftKey); return false; }),
  26779. next = $('<div class="'+navicon+' '+navicon+'-next"></div>').on('click touchstart', function(e) { ! navmove && navtrigger(rightKey); return false; }),
  26780. navbar = $('<div class="elfinder-quicklook-navbar"></div>')
  26781. .append(prev)
  26782. .append(fsicon)
  26783. .append(next)
  26784. .append('<div class="elfinder-quicklook-navbar-separator"></div>')
  26785. .append($('<div class="'+navicon+' '+navicon+'-close"></div>').on('click touchstart', function(e) { ! navmove && self.window.trigger('close'); return false; }))
  26786. ,
  26787. titleClose = $('<span class="ui-front ui-icon elfinder-icon-close ui-icon-closethick"></span>').on('mousedown', function(e) {
  26788. e.stopPropagation();
  26789. self.window.trigger('close');
  26790. }),
  26791. titleDock = $('<span class="ui-front ui-icon elfinder-icon-minimize ui-icon-minusthick"></span>').on('mousedown', function(e) {
  26792. e.stopPropagation();
  26793. if (! self.docked()) {
  26794. self.window.trigger('navdockin');
  26795. } else {
  26796. self.window.trigger('navdockout');
  26797. }
  26798. }),
  26799. spinner = '<span class="elfinder-spinner-text">' + fm.i18n('calc') + '</span>' + '<span class="elfinder-spinner"></span>',
  26800. navStyle = '',
  26801. init = true,
  26802. dockHeight, getSize, tm4cwd, dockedNode, selectTm;
  26803. /**
  26804. * Any flags for each plugin
  26805. */
  26806. this.flags = {};
  26807. this.cover = cover;
  26808. this.evUpdate = evUpdate;
  26809. (this.navbar = navbar)._show = navShow;
  26810. this.resize = 'resize.'+fm.namespace;
  26811. this.info = $('<div></div>').addClass(infocls)
  26812. .append(icon)
  26813. .append(info);
  26814. this.autoPlay = function() {
  26815. if (self.opened()) {
  26816. return !! self.options[self.docked()? 'dockAutoplay' : 'autoplay'];
  26817. }
  26818. return false;
  26819. };
  26820. this.preview = $('<div class="elfinder-quicklook-preview ui-helper-clearfix"></div>')
  26821. // clean info/icon
  26822. .on('change', function() {
  26823. navShow();
  26824. navbar.attr('style', navStyle);
  26825. self.docked() && navbar.hide();
  26826. self.preview.attr('style', '').removeClass('elfinder-overflow-auto');
  26827. self.info.attr('style', '').hide();
  26828. self.cover.removeClass('elfinder-quicklook-coverbg');
  26829. icon.removeAttr('class').attr('style', '');
  26830. info.html('');
  26831. })
  26832. // update info/icon
  26833. .on(evUpdate, function(e) {
  26834. var preview = self.preview,
  26835. file = e.file,
  26836. tpl = '<div class="elfinder-quicklook-info-data">{value}</div>',
  26837. update = function() {
  26838. var win = self.window.css('overflow', 'hidden');
  26839. name = fm.escape(file.i18 || file.name);
  26840. !file.read && e.stopImmediatePropagation();
  26841. self.window.data('hash', file.hash);
  26842. self.preview.off('changesize').trigger('change').children().remove();
  26843. title.html(name);
  26844. prev.css('visibility', '');
  26845. next.css('visibility', '');
  26846. if (file.hash === fm.cwdId2Hash(cwd.find('[id]:not(.elfinder-cwd-parent):first').attr('id'))) {
  26847. prev.css('visibility', 'hidden');
  26848. }
  26849. if (file.hash === fm.cwdId2Hash(cwd.find('[id]:last').attr('id'))) {
  26850. next.css('visibility', 'hidden');
  26851. }
  26852. if (file.mime === 'directory') {
  26853. getSizeHashes = [ file.hash ];
  26854. } else if (file.mime === 'group' && file.getSize) {
  26855. getSizeHashes = file.files;
  26856. }
  26857. info.html(
  26858. tpl.replace(/\{value\}/, name)
  26859. + tpl.replace(/\{value\}/, fm.mime2kind(file))
  26860. + tpl.replace(/\{value\}/, getSizeHashes.length ? spinner : fm.formatSize(file.size))
  26861. + tpl.replace(/\{value\}/, fm.i18n('modify')+': '+ fm.formatDate(file))
  26862. );
  26863. if (getSizeHashes.length) {
  26864. getSize = fm.getSize(getSizeHashes).done(function(data) {
  26865. info.find('span.elfinder-spinner').parent().html(data.formated);
  26866. }).fail(function() {
  26867. info.find('span.elfinder-spinner').parent().html(fm.i18n('unknown'));
  26868. }).always(function() {
  26869. getSize = null;
  26870. });
  26871. getSize._hash = file.hash;
  26872. }
  26873. icon.addClass('elfinder-cwd-icon ui-corner-all '+fm.mime2class(file.mime));
  26874. if (file.icon) {
  26875. icon.css(fm.getIconStyle(file, true));
  26876. }
  26877. self.info.attr('class', infocls);
  26878. if (file.csscls) {
  26879. self.info.addClass(file.csscls);
  26880. }
  26881. if (file.read && (tmb = fm.tmb(file))) {
  26882. $('<img/>')
  26883. .hide()
  26884. .appendTo(self.preview)
  26885. .on('load', function() {
  26886. icon.addClass(tmb.className).css('background-image', "url('"+tmb.url+"')");
  26887. $(this).remove();
  26888. })
  26889. .attr('src', tmb.url);
  26890. }
  26891. self.info.delay(100).fadeIn(10);
  26892. if (self.window.hasClass(fullscreen)) {
  26893. cover.trigger('mousemove');
  26894. }
  26895. win.css('overflow', '');
  26896. },
  26897. tmb, name, getSizeHashes = [];
  26898. if (file && ! Object.keys(file).length) {
  26899. file = fm.cwd();
  26900. }
  26901. if (file && getSize && getSize.state() === 'pending' && getSize._hash !== file.hash) {
  26902. getSize.reject();
  26903. }
  26904. if (file && (e.forceUpdate || self.window.data('hash') !== file.hash)) {
  26905. update();
  26906. } else {
  26907. e.stopImmediatePropagation();
  26908. }
  26909. });
  26910. this.window = $('<div class="ui-front ui-helper-reset ui-widget elfinder-quicklook touch-punch" style="position:absolute"></div>')
  26911. .hide()
  26912. .addClass(fm.UA.Touch? 'elfinder-touch' : '')
  26913. .on('click', function(e) {
  26914. var win = this;
  26915. e.stopPropagation();
  26916. if (state === opened) {
  26917. requestAnimationFrame(function() {
  26918. state === opened && fm.toFront(win);
  26919. });
  26920. }
  26921. })
  26922. .append(
  26923. $('<div class="ui-dialog-titlebar ui-widget-header ui-corner-top ui-helper-clearfix elfinder-quicklook-titlebar"></div>')
  26924. .append(
  26925. $('<span class="ui-widget-header ui-dialog-titlebar-close ui-corner-all elfinder-titlebar-button elfinder-quicklook-titlebar-icon'+(platformWin? ' elfinder-titlebar-button-right' : '')+'"></span>').append(
  26926. titleClose, titleDock
  26927. ),
  26928. title
  26929. ),
  26930. this.preview,
  26931. self.info.hide(),
  26932. cover.hide(),
  26933. navbar
  26934. )
  26935. .draggable({handle : 'div.elfinder-quicklook-titlebar'})
  26936. .on('open', function(e, clcss) {
  26937. var win = self.window,
  26938. file = self.value,
  26939. node = fm.getUI('cwd'),
  26940. open = function(status) {
  26941. state = status;
  26942. self.update(1, self.value);
  26943. self.change();
  26944. win.trigger('resize.' + fm.namespace);
  26945. };
  26946. if (!init && state === closed) {
  26947. if (file && file.hash !== cwdHash) {
  26948. node = fm.cwdHash2Elm(file.hash.split('/', 2)[0]);
  26949. }
  26950. navStyle = '';
  26951. navbar.attr('style', '');
  26952. state = animated;
  26953. node.trigger('scrolltoview');
  26954. coverHide();
  26955. win.css(clcss || closedCss(node))
  26956. .show()
  26957. .animate(openedCss(), 550, function() {
  26958. open(opened);
  26959. navShow();
  26960. });
  26961. fm.toFront(win);
  26962. } else if (state === dockedhidden) {
  26963. fm.getUI('navdock').data('addNode')(dockedNode);
  26964. open(docked);
  26965. self.preview.trigger('changesize');
  26966. fm.storage('previewDocked', '1');
  26967. if (fm.getUI('navdock').width() === 0) {
  26968. win.trigger('navdockout');
  26969. }
  26970. }
  26971. })
  26972. .on('close', function(e, dfd) {
  26973. var win = self.window,
  26974. preview = self.preview.trigger('change'),
  26975. file = self.value,
  26976. hash = (win.data('hash') || '').split('/', 2)[0],
  26977. close = function(status, winhide) {
  26978. state = status;
  26979. winhide && fm.toHide(win);
  26980. preview.children().remove();
  26981. self.update(0, self.value);
  26982. win.data('hash', '');
  26983. dfd && dfd.resolve();
  26984. },
  26985. node;
  26986. if (self.opened()) {
  26987. getSize && getSize.state() === 'pending' && getSize.reject();
  26988. if (! self.docked()) {
  26989. state = animated;
  26990. win.hasClass(fullscreen) && fsicon.click();
  26991. (hash && (node = cwd.find('#'+hash)).length)
  26992. ? win.animate(closedCss(node), 500, function() {
  26993. preview.off('changesize');
  26994. close(closed, true);
  26995. })
  26996. : close(closed, true);
  26997. } else {
  26998. dockedNode = fm.getUI('navdock').data('removeNode')(self.window.attr('id'), 'detach');
  26999. close(dockedhidden);
  27000. fm.storage('previewDocked', '2');
  27001. }
  27002. }
  27003. })
  27004. .on('navdockin', function(e, data) {
  27005. var w = self.window,
  27006. box = fm.getUI('navdock'),
  27007. height = dockHeight || box.width(),
  27008. opts = data || {};
  27009. if (init) {
  27010. opts.init = true;
  27011. }
  27012. state = docked;
  27013. prevStyle = w.attr('style');
  27014. w.toggleClass('ui-front').removeClass('ui-widget').draggable('disable').resizable('disable').removeAttr('style').css({
  27015. width: '100%',
  27016. height: height,
  27017. boxSizing: 'border-box',
  27018. paddingBottom: 0,
  27019. zIndex: 'unset'
  27020. });
  27021. navbar.hide();
  27022. titleDock.toggleClass('ui-icon-plusthick ui-icon-minusthick elfinder-icon-full elfinder-icon-minimize');
  27023. fm.toHide(w, true);
  27024. box.data('addNode')(w, opts);
  27025. self.preview.trigger('changesize');
  27026. fm.storage('previewDocked', '1');
  27027. })
  27028. .on('navdockout', function(e) {
  27029. var w = self.window,
  27030. box = fm.getUI('navdock'),
  27031. dfd = $.Deferred(),
  27032. clcss = closedCss(self.preview);
  27033. dockHeight = w.outerHeight();
  27034. box.data('removeNode')(w.attr('id'), fm.getUI());
  27035. w.toggleClass('ui-front').addClass('ui-widget').draggable('enable').resizable('enable').attr('style', prevStyle);
  27036. titleDock.toggleClass('ui-icon-plusthick ui-icon-minusthick elfinder-icon-full elfinder-icon-minimize');
  27037. state = closed;
  27038. w.trigger('open', clcss);
  27039. fm.storage('previewDocked', '0');
  27040. })
  27041. .on('resize.' + fm.namespace, function() {
  27042. self.preview.trigger('changesize');
  27043. });
  27044. /**
  27045. * This command cannot be disable by backend
  27046. *
  27047. * @type Boolean
  27048. **/
  27049. this.alwaysEnabled = true;
  27050. /**
  27051. * Selected file
  27052. *
  27053. * @type Object
  27054. **/
  27055. this.value = null;
  27056. this.handlers = {
  27057. // save selected file
  27058. select : function(e, d) {
  27059. selectTm && cancelAnimationFrame(selectTm);
  27060. if (! e.data || ! e.data.selected || ! e.data.selected.length) {
  27061. selectTm = requestAnimationFrame(function() {
  27062. self.opened() && updateOnSel();
  27063. });
  27064. } else {
  27065. self.opened() && updateOnSel();
  27066. }
  27067. },
  27068. error : function() { self.window.is(':visible') && self.window.trigger('close'); },
  27069. 'searchshow searchhide' : function() { this.opened() && this.window.trigger('close'); },
  27070. navbarshow : function() {
  27071. requestAnimationFrame(function() {
  27072. self.docked() && self.preview.trigger('changesize');
  27073. });
  27074. },
  27075. destroy : function() { self.window.remove(); }
  27076. };
  27077. this.shortcuts = [{
  27078. pattern : 'space'
  27079. }];
  27080. this.support = {
  27081. audio : {
  27082. ogg : support('audio/ogg;'),
  27083. webm: support('audio/webm;'),
  27084. mp3 : support('audio/mpeg;'),
  27085. wav : support('audio/wav;'),
  27086. m4a : support('audio/mp4;') || support('audio/x-m4a;') || support('audio/aac;'),
  27087. flac: support('audio/flac;'),
  27088. amr : support('audio/amr;')
  27089. },
  27090. video : {
  27091. ogg : support('video/ogg;'),
  27092. webm : support('video/webm;'),
  27093. mp4 : support('video/mp4;'),
  27094. mkv : support('video/x-matroska;') || support('video/webm;'),
  27095. '3gp': support('video/3gpp;') || support('video/mp4;'), // try as mp4
  27096. m3u8 : support('application/x-mpegURL', 'video') || support('application/vnd.apple.mpegURL', 'video'),
  27097. mpd : support('application/dash+xml', 'video')
  27098. }
  27099. };
  27100. // for GC
  27101. mediaNode = {};
  27102. /**
  27103. * Return true if quickLoock window is hiddenReturn true if quickLoock window is visible and not animated
  27104. *
  27105. * @return Boolean
  27106. **/
  27107. this.closed = function() {
  27108. return (state == closed || state == dockedhidden);
  27109. };
  27110. /**
  27111. * Return true if quickLoock window is visible and not animated
  27112. *
  27113. * @return Boolean
  27114. **/
  27115. this.opened = function() {
  27116. return state == opened || state == docked;
  27117. };
  27118. /**
  27119. * Return true if quickLoock window is in NavDock
  27120. *
  27121. * @return Boolean
  27122. **/
  27123. this.docked = function() {
  27124. return state == docked;
  27125. };
  27126. /**
  27127. * Adds an integration into help dialog.
  27128. *
  27129. * @param Object opts options
  27130. */
  27131. this.addIntegration = function(opts) {
  27132. requestAnimationFrame(function() {
  27133. fm.trigger('helpIntegration', Object.assign({cmd: 'quicklook'}, opts));
  27134. });
  27135. };
  27136. /**
  27137. * Init command.
  27138. * Add default plugins and init other plugins
  27139. *
  27140. * @return Object
  27141. **/
  27142. this.init = function() {
  27143. var o = this.options,
  27144. win = this.window,
  27145. preview = this.preview,
  27146. i, p, cwdDispInlineRegex;
  27147. width = o.width > 0 ? parseInt(o.width) : 450;
  27148. height = o.height > 0 ? parseInt(o.height) : 300;
  27149. if (o.dockHeight !== 'auto') {
  27150. dockHeight = parseInt(o.dockHeight);
  27151. if (! dockHeight) {
  27152. dockHeight = void(0);
  27153. }
  27154. }
  27155. fm.one('load', function() {
  27156. dockEnabled = fm.getUI('navdock').data('dockEnabled');
  27157. ! dockEnabled && titleDock.hide();
  27158. parent = fm.getUI();
  27159. cwd = fm.getUI('cwd');
  27160. if (fm.zIndex) {
  27161. win.css('z-index', fm.zIndex + 1);
  27162. }
  27163. win.appendTo(parent);
  27164. // close window on escape
  27165. $(document).on('keydown.'+fm.namespace, function(e) {
  27166. e.keyCode == $.ui.keyCode.ESCAPE && self.opened() && ! self.docked() && win.hasClass('elfinder-frontmost') && win.trigger('close');
  27167. });
  27168. win.resizable({
  27169. handles : 'se',
  27170. minWidth : 350,
  27171. minHeight : 120,
  27172. resize : function() {
  27173. // use another event to avoid recursion in fullscreen mode
  27174. // may be there is clever solution, but i cant find it :(
  27175. preview.trigger('changesize');
  27176. }
  27177. });
  27178. self.change(function() {
  27179. if (self.opened()) {
  27180. if (self.value) {
  27181. if (self.value.tmb && self.value.tmb == 1) {
  27182. // try re-get file object
  27183. self.value = Object.assign({}, fm.file(self.value.hash));
  27184. }
  27185. preview.trigger($.Event(evUpdate, {file : self.value}));
  27186. }
  27187. }
  27188. });
  27189. preview.on(evUpdate, function(e) {
  27190. var file, hash, serach;
  27191. if (file = e.file) {
  27192. hash = file.hash;
  27193. serach = (fm.searchStatus.mixed && fm.searchStatus.state > 1);
  27194. if (file.mime !== 'directory') {
  27195. if (parseInt(file.size) || file.mime.match(o.mimeRegexNotEmptyCheck)) {
  27196. // set current dispInlineRegex
  27197. self.dispInlineRegex = cwdDispInlineRegex;
  27198. if (serach || fm.optionsByHashes[hash]) {
  27199. try {
  27200. self.dispInlineRegex = new RegExp(fm.option('dispInlineRegex', hash), 'i');
  27201. } catch(e) {
  27202. try {
  27203. self.dispInlineRegex = new RegExp(!fm.isRoot(file)? fm.option('dispInlineRegex', file.phash) : fm.options.dispInlineRegex, 'i');
  27204. } catch(e) {
  27205. self.dispInlineRegex = /^$/;
  27206. }
  27207. }
  27208. }
  27209. } else {
  27210. // do not preview of file that size = 0
  27211. e.stopImmediatePropagation();
  27212. }
  27213. } else {
  27214. self.dispInlineRegex = /^$/;
  27215. }
  27216. self.info.show();
  27217. } else {
  27218. e.stopImmediatePropagation();
  27219. }
  27220. });
  27221. $.each(fm.commands.quicklook.plugins || [], function(i, plugin) {
  27222. if (typeof(plugin) == 'function') {
  27223. new plugin(self);
  27224. }
  27225. });
  27226. }).one('open', function() {
  27227. var dock = Number(fm.storage('previewDocked') || o.docked),
  27228. win;
  27229. if (dockEnabled && dock >= 1) {
  27230. win = self.window;
  27231. self.exec();
  27232. win.trigger('navdockin', { init : true });
  27233. if (dock === 2) {
  27234. win.trigger('close');
  27235. } else {
  27236. self.update(void(0), fm.cwd());
  27237. self.change();
  27238. }
  27239. }
  27240. init = false;
  27241. }).bind('open', function() {
  27242. cwdHash = fm.cwd().hash;
  27243. self.value = fm.cwd();
  27244. // set current volume dispInlineRegex
  27245. try {
  27246. cwdDispInlineRegex = new RegExp(fm.option('dispInlineRegex'), 'i');
  27247. } catch(e) {
  27248. cwdDispInlineRegex = /^$/;
  27249. }
  27250. }).bind('change', function(e) {
  27251. if (e.data && e.data.changed && self.opened()) {
  27252. $.each(e.data.changed, function() {
  27253. if (self.window.data('hash') === this.hash) {
  27254. self.window.data('hash', null);
  27255. self.preview.trigger(evUpdate);
  27256. return false;
  27257. }
  27258. });
  27259. }
  27260. }).bind('navdockresizestart navdockresizestop', function(e) {
  27261. cover[e.type === 'navdockresizestart'? 'show' : 'hide']();
  27262. });
  27263. };
  27264. this.getstate = function() {
  27265. return self.opened()? 1 : 0;
  27266. };
  27267. this.exec = function() {
  27268. self.closed() && updateOnSel();
  27269. self.enabled() && self.window.trigger(self.opened() ? 'close' : 'open');
  27270. return $.Deferred().resolve();
  27271. };
  27272. this.hideinfo = function() {
  27273. this.info.stop(true, true).hide();
  27274. };
  27275. }).prototype = { forceLoad : true }; // this is required command
  27276. /*
  27277. * File: /js/commands/quicklook.plugins.js
  27278. */
  27279. elFinder.prototype.commands.quicklook.plugins = [
  27280. /**
  27281. * Images preview plugin
  27282. *
  27283. * @param elFinder.commands.quicklook
  27284. **/
  27285. function(ql) {
  27286. var mimes = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/x-ms-bmp'],
  27287. getDimSize = ql.fm.returnBytes((ql.options.getDimThreshold || 0)),
  27288. preview = ql.preview,
  27289. WebP, flipMime;
  27290. // webp support
  27291. WebP = new Image();
  27292. WebP.onload = WebP.onerror = function() {
  27293. if (WebP.height == 2) {
  27294. mimes.push('image/webp');
  27295. }
  27296. };
  27297. WebP.src='data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';
  27298. // what kind of images we can display
  27299. $.each(navigator.mimeTypes, function(i, o) {
  27300. var mime = o.type;
  27301. if (mime.indexOf('image/') === 0 && $.inArray(mime, mimes)) {
  27302. mimes.push(mime);
  27303. }
  27304. });
  27305. preview.on(ql.evUpdate, function(e) {
  27306. var fm = ql.fm,
  27307. file = e.file,
  27308. showed = false,
  27309. dimreq = null,
  27310. setdim = function(dim) {
  27311. var rfile = fm.file(file.hash);
  27312. rfile.width = dim[0];
  27313. rfile.height = dim[1];
  27314. },
  27315. show = function() {
  27316. var elm, varelm, memSize, width, height, prop;
  27317. dimreq && dimreq.state && dimreq.state() === 'pending' && dimreq.reject();
  27318. if (showed) {
  27319. return;
  27320. }
  27321. showed = true;
  27322. elm = img.get(0);
  27323. memSize = file.width && file.height? {w: file.width, h: file.height} : (elm.naturalWidth? null : {w: img.width(), h: img.height()});
  27324. memSize && img.removeAttr('width').removeAttr('height');
  27325. width = file.width || elm.naturalWidth || elm.width || img.width();
  27326. height = file.height || elm.naturalHeight || elm.height || img.height();
  27327. if (!file.width || !file.height) {
  27328. setdim([width, height]);
  27329. }
  27330. memSize && img.width(memSize.w).height(memSize.h);
  27331. prop = (width/height).toFixed(2);
  27332. preview.on('changesize', function() {
  27333. var pw = parseInt(preview.width()),
  27334. ph = parseInt(preview.height()),
  27335. w, h;
  27336. if (prop < (pw/ph).toFixed(2)) {
  27337. h = ph;
  27338. w = Math.floor(h * prop);
  27339. } else {
  27340. w = pw;
  27341. h = Math.floor(w/prop);
  27342. }
  27343. img.width(w).height(h).css('margin-top', h < ph ? Math.floor((ph - h)/2) : 0);
  27344. })
  27345. .trigger('changesize');
  27346. //show image
  27347. img.fadeIn(100);
  27348. },
  27349. hideInfo = function() {
  27350. loading.remove();
  27351. // hide info/icon
  27352. ql.hideinfo();
  27353. },
  27354. url, img, loading, prog, m, opDfd;
  27355. if (!flipMime) {
  27356. flipMime = fm.arrayFlip(mimes);
  27357. }
  27358. if (flipMime[file.mime] && ql.dispInlineRegex.test(file.mime)) {
  27359. // this is our file - stop event propagation
  27360. e.stopImmediatePropagation();
  27361. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  27362. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  27363. img = $('<img/>')
  27364. .hide()
  27365. .appendTo(preview)
  27366. .on('load', function() {
  27367. hideInfo();
  27368. show();
  27369. })
  27370. .on('error', function() {
  27371. loading.remove();
  27372. });
  27373. opDfd = fm.openUrl(file.hash, false, function(url) {
  27374. img.attr('src', url);
  27375. }, { progressBar: prog });
  27376. // stop loading on change file if not loaded yet
  27377. preview.one('change', function() {
  27378. opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject();
  27379. });
  27380. if (file.width && file.height) {
  27381. show();
  27382. } else if (file.size > getDimSize) {
  27383. dimreq = fm.request({
  27384. data : {cmd : 'dim', target : file.hash},
  27385. preventDefault : true
  27386. })
  27387. .done(function(data) {
  27388. if (data.dim) {
  27389. var dim = data.dim.split('x');
  27390. file.width = dim[0];
  27391. file.height = dim[1];
  27392. setdim(dim);
  27393. show();
  27394. }
  27395. });
  27396. }
  27397. }
  27398. });
  27399. },
  27400. /**
  27401. * TIFF image preview
  27402. *
  27403. * @param object ql elFinder.commands.quicklook
  27404. */
  27405. function(ql) {
  27406. var fm = ql.fm,
  27407. mime = 'image/tiff',
  27408. preview = ql.preview;
  27409. if (window.Worker && window.Uint8Array) {
  27410. preview.on(ql.evUpdate, function(e) {
  27411. var file = e.file,
  27412. err = function(e) {
  27413. wk && wk.terminate();
  27414. loading.remove();
  27415. fm.debug('error', e);
  27416. },
  27417. setdim = function(dim) {
  27418. var rfile = fm.file(file.hash);
  27419. rfile.width = dim[0];
  27420. rfile.height = dim[1];
  27421. },
  27422. loading, prog, url, base, wk, opDfd;
  27423. if (file.mime === mime) {
  27424. e.stopImmediatePropagation();
  27425. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  27426. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  27427. // stop loading on change file if not loaded yet
  27428. preview.one('change', function() {
  27429. wk && wk.terminate();
  27430. loading.remove();
  27431. });
  27432. opDfd = fm.getContents(file.hash, 'arraybuffer', { progressBar: prog }).done(function(data) {
  27433. if (data) {
  27434. base = $('<div></div>').css({width:'100%',height:'100%'}).hide().appendTo(preview);
  27435. try {
  27436. wk = fm.getWorker();
  27437. wk.onmessage = function(res) {
  27438. var data = res.data,
  27439. cv, co, id, prop;
  27440. wk && wk.terminate();
  27441. cv = document.createElement('canvas');
  27442. co = cv.getContext('2d');
  27443. cv.width = data.width;
  27444. cv.height = data.height;
  27445. id = co.createImageData(data.width, data.height);
  27446. (id).data.set(new Uint8Array(data.image));
  27447. co.putImageData(id, 0, 0);
  27448. base.append(cv).show();
  27449. loading.remove();
  27450. prop = (data.width/data.height).toFixed(2);
  27451. preview.on('changesize', function() {
  27452. var pw = parseInt(preview.width()),
  27453. ph = parseInt(preview.height()),
  27454. w, h;
  27455. if (prop < (pw/ph).toFixed(2)) {
  27456. h = ph;
  27457. w = Math.floor(h * prop);
  27458. } else {
  27459. w = pw;
  27460. h = Math.floor(w/prop);
  27461. }
  27462. $(cv).width(w).height(h).css('margin-top', h < ph ? Math.floor((ph - h)/2) : 0);
  27463. }).trigger('changesize');
  27464. if (!file.width || !file.height) {
  27465. setdim([data.width, data.height]);
  27466. }
  27467. ql.hideinfo();
  27468. };
  27469. wk.onerror = err;
  27470. wk.postMessage({
  27471. scripts: [fm.options.cdns.tiff, fm.getWorkerUrl('quicklook.tiff.js')],
  27472. data: { data: data }
  27473. });
  27474. } catch(e) {
  27475. err(e);
  27476. }
  27477. } else {
  27478. err();
  27479. }
  27480. });
  27481. // stop loading on change file if not loaded yet
  27482. preview.one('change', function() {
  27483. opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject();
  27484. });
  27485. }
  27486. });
  27487. }
  27488. },
  27489. /**
  27490. * PSD(Adobe Photoshop data) preview plugin
  27491. *
  27492. * @param elFinder.commands.quicklook
  27493. **/
  27494. function(ql) {
  27495. var fm = ql.fm,
  27496. mimes = fm.arrayFlip(['image/vnd.adobe.photoshop', 'image/x-photoshop']),
  27497. preview = ql.preview,
  27498. load = function(url, img, loading) {
  27499. try {
  27500. fm.replaceXhrSend();
  27501. PSD.fromURL(url).then(function(psd) {
  27502. var prop;
  27503. img.attr('src', psd.image.toBase64());
  27504. requestAnimationFrame(function() {
  27505. prop = (img.width()/img.height()).toFixed(2);
  27506. preview.on('changesize', function() {
  27507. var pw = parseInt(preview.width()),
  27508. ph = parseInt(preview.height()),
  27509. w, h;
  27510. if (prop < (pw/ph).toFixed(2)) {
  27511. h = ph;
  27512. w = Math.floor(h * prop);
  27513. } else {
  27514. w = pw;
  27515. h = Math.floor(w/prop);
  27516. }
  27517. img.width(w).height(h).css('margin-top', h < ph ? Math.floor((ph - h)/2) : 0);
  27518. }).trigger('changesize');
  27519. loading.remove();
  27520. // hide info/icon
  27521. ql.hideinfo();
  27522. //show image
  27523. img.fadeIn(100);
  27524. });
  27525. }, function() {
  27526. loading.remove();
  27527. img.remove();
  27528. });
  27529. fm.restoreXhrSend();
  27530. } catch(e) {
  27531. fm.restoreXhrSend();
  27532. loading.remove();
  27533. img.remove();
  27534. }
  27535. },
  27536. PSD;
  27537. preview.on(ql.evUpdate, function(e) {
  27538. var file = e.file,
  27539. url, img, loading, prog, m,
  27540. _define, _require, opDfd;
  27541. if (mimes[file.mime] && fm.options.cdns.psd && ! fm.UA.ltIE10 && ql.dispInlineRegex.test(file.mime)) {
  27542. // this is our file - stop event propagation
  27543. e.stopImmediatePropagation();
  27544. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  27545. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  27546. opDfd = fm.openUrl(file.hash, 'sameorigin', function(url) {
  27547. if (url) {
  27548. img = $('<img/>').hide().appendTo(preview);
  27549. if (PSD) {
  27550. load(url, img, loading);
  27551. } else {
  27552. _define = window.define;
  27553. _require = window.require;
  27554. window.require = null;
  27555. window.define = null;
  27556. fm.loadScript(
  27557. [ fm.options.cdns.psd ],
  27558. function() {
  27559. PSD = require('psd');
  27560. _define? (window.define = _define) : (delete window.define);
  27561. _require? (window.require = _require) : (delete window.require);
  27562. load(url, img, loading);
  27563. }
  27564. );
  27565. }
  27566. }
  27567. }, { progressBar: prog });
  27568. // stop loading on change file if not loaded yet
  27569. preview.one('change', function() {
  27570. opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject();
  27571. });
  27572. }
  27573. });
  27574. },
  27575. /**
  27576. * HTML preview plugin
  27577. *
  27578. * @param elFinder.commands.quicklook
  27579. **/
  27580. function(ql) {
  27581. var fm = ql.fm,
  27582. mimes = fm.arrayFlip(['text/html', 'application/xhtml+xml']),
  27583. preview = ql.preview;
  27584. preview.on(ql.evUpdate, function(e) {
  27585. var file = e.file, jqxhr, loading, prog;
  27586. if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax)) {
  27587. e.stopImmediatePropagation();
  27588. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  27589. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  27590. // stop loading on change file if not loaded yet
  27591. preview.one('change', function() {
  27592. jqxhr.state() == 'pending' && jqxhr.reject();
  27593. }).addClass('elfinder-overflow-auto');
  27594. jqxhr = fm.request({
  27595. data : {cmd : 'get', target : file.hash, conv : 1, _t : file.ts},
  27596. options : {type: 'get', cache : true},
  27597. preventDefault : true,
  27598. progressBar : prog
  27599. })
  27600. .done(function(data) {
  27601. ql.hideinfo();
  27602. var doc = $('<iframe class="elfinder-quicklook-preview-html"></iframe>').appendTo(preview)[0].contentWindow.document;
  27603. doc.open();
  27604. doc.write(data.content);
  27605. doc.close();
  27606. })
  27607. .always(function() {
  27608. loading.remove();
  27609. });
  27610. }
  27611. });
  27612. },
  27613. /**
  27614. * MarkDown preview plugin
  27615. *
  27616. * @param elFinder.commands.quicklook
  27617. **/
  27618. function(ql) {
  27619. var fm = ql.fm,
  27620. mimes = fm.arrayFlip(['text/x-markdown']),
  27621. preview = ql.preview,
  27622. marked = null,
  27623. show = function(data, loading) {
  27624. ql.hideinfo();
  27625. var doc = $('<iframe class="elfinder-quicklook-preview-html"></iframe>').appendTo(preview)[0].contentWindow.document;
  27626. doc.open();
  27627. doc.write(marked(data.content));
  27628. doc.close();
  27629. loading.remove();
  27630. },
  27631. error = function(loading) {
  27632. marked = false;
  27633. loading.remove();
  27634. };
  27635. preview.on(ql.evUpdate, function(e) {
  27636. var file = e.file, jqxhr, loading, prog;
  27637. if (mimes[file.mime] && fm.options.cdns.marked && marked !== false && ql.dispInlineRegex.test(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax)) {
  27638. e.stopImmediatePropagation();
  27639. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  27640. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  27641. // stop loading on change file if not loaded yet
  27642. preview.one('change', function() {
  27643. jqxhr.state() == 'pending' && jqxhr.reject();
  27644. }).addClass('elfinder-overflow-auto');
  27645. jqxhr = fm.request({
  27646. data : {cmd : 'get', target : file.hash, conv : 1, _t : file.ts},
  27647. options : {type: 'get', cache : true},
  27648. preventDefault : true,
  27649. progressBar : prog
  27650. })
  27651. .done(function(data) {
  27652. if (marked || window.marked) {
  27653. if (!marked) {
  27654. marked = window.marked;
  27655. }
  27656. show(data, loading);
  27657. } else {
  27658. fm.loadScript([fm.options.cdns.marked],
  27659. function(res) {
  27660. marked = res || window.marked || false;
  27661. delete window.marked;
  27662. if (marked) {
  27663. show(data, loading);
  27664. } else {
  27665. error(loading);
  27666. }
  27667. },
  27668. {
  27669. tryRequire: true,
  27670. error: function() {
  27671. error(loading);
  27672. }
  27673. }
  27674. );
  27675. }
  27676. })
  27677. .fail(function() {
  27678. error(loading);
  27679. });
  27680. }
  27681. });
  27682. },
  27683. /**
  27684. * PDF/ODT/ODS/ODP preview with ViewerJS
  27685. *
  27686. * @param elFinder.commands.quicklook
  27687. */
  27688. function(ql) {
  27689. if (ql.options.viewerjs) {
  27690. var fm = ql.fm,
  27691. preview = ql.preview,
  27692. opts = ql.options.viewerjs,
  27693. mimes = opts.url? fm.arrayFlip(opts.mimes || []) : [],
  27694. win = ql.window,
  27695. navi = ql.navbar,
  27696. setNavi = function() {
  27697. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '30px' : '');
  27698. };
  27699. if (opts.url) {
  27700. preview.on('update', function(e) {
  27701. var file = e.file, node, loading, prog, opDfd;
  27702. if (mimes[file.mime] && (file.mime !== 'application/pdf' || !opts.pdfNative || !ql.flags.pdfNative)) {
  27703. e.stopImmediatePropagation();
  27704. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  27705. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  27706. opDfd = fm.openUrl(file.hash, 'sameorigin', function(url) {
  27707. if (url) {
  27708. node = $('<iframe class="elfinder-quicklook-preview-iframe"></iframe>')
  27709. .css('background-color', 'transparent')
  27710. .on('load', function() {
  27711. ql.hideinfo();
  27712. loading.remove();
  27713. node.css('background-color', '#fff');
  27714. })
  27715. .on('error', function() {
  27716. loading.remove();
  27717. node.remove();
  27718. })
  27719. .appendTo(preview)
  27720. .attr('src', opts.url + '#' + url);
  27721. win.on('viewchange.viewerjs', setNavi);
  27722. setNavi();
  27723. preview.one('change', function() {
  27724. win.off('viewchange.viewerjs');
  27725. loading.remove();
  27726. node.off('load').remove();
  27727. });
  27728. }
  27729. }, { progressBar: prog });
  27730. // stop loading on change file if not loaded yet
  27731. preview.one('change', function() {
  27732. opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject();
  27733. });
  27734. }
  27735. });
  27736. }
  27737. }
  27738. },
  27739. /**
  27740. * PDF preview plugin
  27741. *
  27742. * @param elFinder.commands.quicklook
  27743. **/
  27744. function(ql) {
  27745. var fm = ql.fm,
  27746. mime = 'application/pdf',
  27747. preview = ql.preview,
  27748. active = false,
  27749. urlhash = '',
  27750. firefox, toolbar;
  27751. if ((fm.UA.Safari && fm.OS === 'mac' && !fm.UA.iOS) || fm.UA.IE || fm.UA.Firefox) {
  27752. active = true;
  27753. } else {
  27754. $.each(navigator.plugins, function(i, plugins) {
  27755. $.each(plugins, function(i, plugin) {
  27756. if (plugin.type === mime) {
  27757. return !(active = true);
  27758. }
  27759. });
  27760. });
  27761. }
  27762. ql.flags.pdfNative = active;
  27763. if (active) {
  27764. if (typeof ql.options.pdfToolbar !== 'undefined' && !ql.options.pdfToolbar) {
  27765. urlhash = '#toolbar=0';
  27766. }
  27767. preview.on(ql.evUpdate, function(e) {
  27768. var file = e.file,
  27769. opDfd;
  27770. if (active && file.mime === mime && ql.dispInlineRegex.test(file.mime)) {
  27771. e.stopImmediatePropagation();
  27772. opDfd = fm.openUrl(file.hash, false, function(url) {
  27773. if (url) {
  27774. ql.hideinfo();
  27775. ql.cover.addClass('elfinder-quicklook-coverbg');
  27776. $('<object class="elfinder-quicklook-preview-pdf" data="'+url+urlhash+'" type="application/pdf" ></object>')
  27777. .on('error', function(e) {
  27778. active = false;
  27779. ql.update(void(0), fm.cwd());
  27780. ql.update(void(0), file);
  27781. })
  27782. .appendTo(preview);
  27783. }
  27784. });
  27785. // stop loading on change file if not loaded yet
  27786. preview.one('change', function() {
  27787. opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject();
  27788. });
  27789. }
  27790. });
  27791. }
  27792. },
  27793. /**
  27794. * Flash preview plugin
  27795. *
  27796. * @param elFinder.commands.quicklook
  27797. **/
  27798. function(ql) {
  27799. var fm = ql.fm,
  27800. mime = 'application/x-shockwave-flash',
  27801. preview = ql.preview,
  27802. active = false;
  27803. $.each(navigator.plugins, function(i, plugins) {
  27804. $.each(plugins, function(i, plugin) {
  27805. if (plugin.type === mime) {
  27806. return !(active = true);
  27807. }
  27808. });
  27809. });
  27810. active && preview.on(ql.evUpdate, function(e) {
  27811. var file = e.file,
  27812. node, opDfd;
  27813. if (file.mime === mime && ql.dispInlineRegex.test(file.mime)) {
  27814. e.stopImmediatePropagation();
  27815. opDfd = fm.openUrl(file.hash, false, function(url) {
  27816. if (url) {
  27817. ql.hideinfo();
  27818. node = $('<embed class="elfinder-quicklook-preview-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" src="'+url+'" quality="high" type="application/x-shockwave-flash" wmode="transparent" />')
  27819. .appendTo(preview);
  27820. }
  27821. });
  27822. // stop loading on change file if not loaded yet
  27823. preview.one('change', function() {
  27824. opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject();
  27825. });
  27826. }
  27827. });
  27828. },
  27829. /**
  27830. * HTML5 audio preview plugin
  27831. *
  27832. * @param elFinder.commands.quicklook
  27833. **/
  27834. function(ql) {
  27835. var fm = ql.fm,
  27836. preview = ql.preview,
  27837. mimes = {
  27838. 'audio/mpeg' : 'mp3',
  27839. 'audio/mpeg3' : 'mp3',
  27840. 'audio/mp3' : 'mp3',
  27841. 'audio/x-mpeg3' : 'mp3',
  27842. 'audio/x-mp3' : 'mp3',
  27843. 'audio/x-wav' : 'wav',
  27844. 'audio/wav' : 'wav',
  27845. 'audio/x-m4a' : 'm4a',
  27846. 'audio/aac' : 'm4a',
  27847. 'audio/mp4' : 'm4a',
  27848. 'audio/x-mp4' : 'm4a',
  27849. 'audio/ogg' : 'ogg',
  27850. 'audio/webm' : 'webm',
  27851. 'audio/flac' : 'flac',
  27852. 'audio/x-flac' : 'flac',
  27853. 'audio/amr' : 'amr'
  27854. },
  27855. node, curHash,
  27856. win = ql.window,
  27857. navi = ql.navbar,
  27858. AMR, autoplay,
  27859. controlsList = typeof ql.options.mediaControlsList === 'string' && ql.options.mediaControlsList? ' controlsList="' + fm.escape(ql.options.mediaControlsList) + '"' : '',
  27860. setNavi = function() {
  27861. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : '');
  27862. },
  27863. getNode = function(src, hash) {
  27864. return $('<audio class="elfinder-quicklook-preview-audio ui-front" controls' + controlsList + ' preload="auto" autobuffer><source src="'+src+'" ></source></audio>')
  27865. .on('change', function(e) {
  27866. // Firefox fire change event on seek or volume change
  27867. e.stopPropagation();
  27868. })
  27869. .on('error', function(e) {
  27870. node && node.data('hash') === hash && reset();
  27871. })
  27872. .data('hash', hash)
  27873. .appendTo(preview);
  27874. },
  27875. amrToWavUrl = function(hash) {
  27876. var dfd = $.Deferred(),
  27877. loader = $.Deferred().done(function() {
  27878. var opDfd;
  27879. opDfd = fm.getContents(hash, 'arraybuffer', { progressBar: prog }).done(function(data) {
  27880. try {
  27881. var buffer = AMR.toWAV(new Uint8Array(data));
  27882. if (buffer) {
  27883. dfd.resolve(URL.createObjectURL(new Blob([buffer], { type: 'audio/x-wav' })));
  27884. } else {
  27885. dfd.reject();
  27886. }
  27887. } catch(e) {
  27888. dfd.reject();
  27889. }
  27890. }).fail(function() {
  27891. dfd.reject();
  27892. });
  27893. // stop loading on change file if not loaded yet
  27894. preview.one('change', function() {
  27895. opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject();
  27896. });
  27897. }).fail(function() {
  27898. AMR = false;
  27899. dfd.reject();
  27900. }),
  27901. _AMR;
  27902. if (window.TextEncoder && window.URL && URL.createObjectURL && typeof AMR === 'undefined') {
  27903. // previous window.AMR
  27904. _AMR = window.AMR;
  27905. delete window.AMR;
  27906. fm.loadScript(
  27907. [ fm.options.cdns.amr ],
  27908. function() {
  27909. AMR = window.AMR? window.AMR : false;
  27910. // restore previous window.AMR
  27911. window.AMR = _AMR;
  27912. loader[AMR? 'resolve':'reject']();
  27913. },
  27914. {
  27915. error: function() {
  27916. loader.reject();
  27917. }
  27918. }
  27919. );
  27920. } else {
  27921. loader[AMR? 'resolve':'reject']();
  27922. }
  27923. return dfd;
  27924. },
  27925. play = function(player) {
  27926. var hash = node.data('hash'),
  27927. playPromise;
  27928. autoplay && (playPromise = player.play());
  27929. // uses "playPromise['catch']" instead "playPromise.catch" to support Old IE
  27930. if (playPromise && playPromise['catch']) {
  27931. playPromise['catch'](function(e) {
  27932. if (!player.paused) {
  27933. node && node.data('hash') === hash && reset();
  27934. }
  27935. });
  27936. }
  27937. },
  27938. reset = function() {
  27939. if (node && node.parent().length) {
  27940. var elm = node[0],
  27941. url = node.children('source').attr('src');
  27942. win.off('viewchange.audio');
  27943. try {
  27944. elm.pause();
  27945. node.empty();
  27946. if (url.match(/^blob:/)) {
  27947. URL.revokeObjectURL(url);
  27948. }
  27949. elm.src = '';
  27950. elm.load();
  27951. } catch(e) {}
  27952. node.remove();
  27953. node = null;
  27954. }
  27955. },
  27956. loading, prog;
  27957. preview.on(ql.evUpdate, function(e) {
  27958. var file = e.file,
  27959. type = mimes[file.mime],
  27960. html5, opDfd;
  27961. if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime) && ((html5 = ql.support.audio[type]) || (type === 'amr'))) {
  27962. autoplay = ql.autoPlay();
  27963. curHash = file.hash;
  27964. if (!html5) {
  27965. if (fm.options.cdns.amr && type === 'amr' && AMR !== false) {
  27966. e.stopImmediatePropagation();
  27967. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  27968. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  27969. node = getNode('', curHash);
  27970. amrToWavUrl(file.hash).done(function(url) {
  27971. loading.remove();
  27972. if (curHash === file.hash) {
  27973. var elm = node[0];
  27974. try {
  27975. node.children('source').attr('src', url);
  27976. elm.pause();
  27977. elm.load();
  27978. play(elm);
  27979. win.on('viewchange.audio', setNavi);
  27980. setNavi();
  27981. } catch(e) {
  27982. URL.revokeObjectURL(url);
  27983. node.remove();
  27984. }
  27985. } else {
  27986. URL.revokeObjectURL(url);
  27987. }
  27988. }).fail(function() {
  27989. node.remove();
  27990. });
  27991. }
  27992. } else {
  27993. e.stopImmediatePropagation();
  27994. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  27995. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  27996. opDfd = fm.openUrl(curHash, false, function(url) {
  27997. loading.remove();
  27998. if (url) {
  27999. node = getNode(url, curHash);
  28000. play(node[0]);
  28001. win.on('viewchange.audio', setNavi);
  28002. setNavi();
  28003. } else {
  28004. node.remove();
  28005. }
  28006. }, { progressBar: prog });
  28007. // stop loading on change file if not loaded yet
  28008. preview.one('change', function() {
  28009. opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject();
  28010. });
  28011. }
  28012. }
  28013. }).one('change', reset);
  28014. },
  28015. /**
  28016. * HTML5 video preview plugin
  28017. *
  28018. * @param elFinder.commands.quicklook
  28019. **/
  28020. function(ql) {
  28021. var fm = ql.fm,
  28022. preview = ql.preview,
  28023. mimes = {
  28024. 'video/mp4' : 'mp4',
  28025. 'video/x-m4v' : 'mp4',
  28026. 'video/quicktime' : 'mp4',
  28027. 'video/mpeg' : 'mpeg',
  28028. 'video/ogg' : 'ogg',
  28029. 'application/ogg' : 'ogg',
  28030. 'video/webm' : 'webm',
  28031. 'video/x-matroska': 'mkv',
  28032. 'video/3gpp' : '3gp',
  28033. 'application/vnd.apple.mpegurl' : 'm3u8',
  28034. 'application/x-mpegurl' : 'm3u8',
  28035. 'application/dash+xml' : 'mpd',
  28036. 'video/x-flv' : 'flv',
  28037. 'video/x-msvideo' : 'avi'
  28038. },
  28039. node,
  28040. win = ql.window,
  28041. navi = ql.navbar,
  28042. cHls, cDash, pDash, cFlv, cVideojs, autoplay, tm, loading, prog,
  28043. controlsList = typeof ql.options.mediaControlsList === 'string' && ql.options.mediaControlsList? ' controlsList="' + fm.escape(ql.options.mediaControlsList) + '"' : '',
  28044. setNavi = function() {
  28045. if (fm.UA.iOS) {
  28046. if (win.hasClass('elfinder-quicklook-fullscreen')) {
  28047. preview.css('height', '-webkit-calc(100% - 50px)');
  28048. navi._show();
  28049. } else {
  28050. preview.css('height', '');
  28051. }
  28052. } else {
  28053. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : '');
  28054. }
  28055. },
  28056. render = function(file, opts) {
  28057. var errTm = function(e) {
  28058. if (err > 1) {
  28059. tm && clearTimeout(tm);
  28060. tm = setTimeout(function() {
  28061. !canPlay && reset(true);
  28062. }, 800);
  28063. }
  28064. },
  28065. err = 0,
  28066. canPlay;
  28067. //reset();
  28068. pDash = null;
  28069. opts = opts || {};
  28070. ql.hideinfo();
  28071. node = $('<video class="elfinder-quicklook-preview-video" controls' + controlsList + ' preload="auto" autobuffer playsinline>'
  28072. +'</video>')
  28073. .on('change', function(e) {
  28074. // Firefox fire change event on seek or volume change
  28075. e.stopPropagation();
  28076. })
  28077. .on('timeupdate progress', errTm)
  28078. .on('canplay', function() {
  28079. canPlay = true;
  28080. })
  28081. .data('hash', file.hash);
  28082. // can not handling error event with jQuery `on` event handler
  28083. node[0].addEventListener('error', function(e) {
  28084. if (opts.src && fm.convAbsUrl(opts.src) === fm.convAbsUrl(e.target.src)) {
  28085. ++err;
  28086. errTm();
  28087. }
  28088. }, true);
  28089. if (opts.src) {
  28090. node.append('<source src="'+opts.src+'" type="'+file.mime+'"></source><source src="'+opts.src+'"></source>');
  28091. }
  28092. node.appendTo(preview);
  28093. win.on('viewchange.video', setNavi);
  28094. setNavi();
  28095. },
  28096. loadHls = function(file) {
  28097. var hls, opDfd;
  28098. opDfd = fm.openUrl(file.hash, false, function(url) {
  28099. loading.remove();
  28100. if (url) {
  28101. render(file);
  28102. hls = new cHls();
  28103. hls.loadSource(url);
  28104. hls.attachMedia(node[0]);
  28105. if (autoplay) {
  28106. hls.on(cHls.Events.MANIFEST_PARSED, function() {
  28107. play(node[0]);
  28108. });
  28109. }
  28110. }
  28111. }, { progressBar: prog });
  28112. // stop loading on change file if not loaded yet
  28113. preview.one('change', function() {
  28114. opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject();
  28115. });
  28116. },
  28117. loadDash = function(file) {
  28118. var opDfd;
  28119. opDfd = fm.openUrl(file.hash, false, function(url) {
  28120. var debug;
  28121. loading.remove();
  28122. if (url) {
  28123. render(file);
  28124. pDash = window.dashjs.MediaPlayer().create();
  28125. debug = pDash.getDebug();
  28126. if (debug.setLogLevel) {
  28127. debug.setLogLevel(dashjs.Debug.LOG_LEVEL_FATAL);
  28128. } else if (debug.setLogToBrowserConsole) {
  28129. debug.setLogToBrowserConsole(false);
  28130. }
  28131. pDash.initialize(node[0], url, autoplay);
  28132. pDash.on('error', function(e) {
  28133. reset(true);
  28134. });
  28135. }
  28136. }, { progressBar: prog });
  28137. // stop loading on change file if not loaded yet
  28138. preview.one('change', function() {
  28139. opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject();
  28140. });
  28141. },
  28142. loadFlv = function(file) {
  28143. var opDfd
  28144. if (!cFlv.isSupported()) {
  28145. cFlv = false;
  28146. return;
  28147. }
  28148. opDfd = fm.openUrl(file.hash, false, function(url) {
  28149. loading.remove();
  28150. if (url) {
  28151. var player = cFlv.createPlayer({
  28152. type: 'flv',
  28153. url: url
  28154. });
  28155. render(file);
  28156. player.on(cFlv.Events.ERROR, function() {
  28157. player.destroy();
  28158. reset(true);
  28159. });
  28160. player.attachMediaElement(node[0]);
  28161. player.load();
  28162. play(player);
  28163. }
  28164. }, { progressBar: prog });
  28165. // stop loading on change file if not loaded yet
  28166. preview.one('change', function() {
  28167. opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject();
  28168. });
  28169. },
  28170. loadVideojs = function(file) {
  28171. var opDfd;
  28172. opDfd = fm.openUrl(file.hash, false, function(url) {
  28173. loading.remove();
  28174. if (url) {
  28175. render(file);
  28176. node[0].src = url;
  28177. cVideojs(node[0], {
  28178. src: url
  28179. });
  28180. }
  28181. }, { progressBar: prog });
  28182. // stop loading on change file if not loaded yet
  28183. preview.one('change', function() {
  28184. opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject();
  28185. });
  28186. },
  28187. play = function(player) {
  28188. var hash = node.data('hash'),
  28189. playPromise;
  28190. autoplay && (playPromise = player.play());
  28191. // uses "playPromise['catch']" instead "playPromise.catch" to support Old IE
  28192. if (playPromise && playPromise['catch']) {
  28193. playPromise['catch'](function(e) {
  28194. if (!player.paused) {
  28195. node && node.data('hash') === hash && reset(true);
  28196. }
  28197. });
  28198. }
  28199. },
  28200. reset = function(showInfo) {
  28201. tm && clearTimeout(tm);
  28202. if (node && node.parent().length) {
  28203. var elm = node[0];
  28204. win.off('viewchange.video');
  28205. pDash && pDash.reset();
  28206. try {
  28207. elm.pause();
  28208. node.empty();
  28209. elm.src = '';
  28210. elm.load();
  28211. } catch(e) {}
  28212. node.remove();
  28213. node = null;
  28214. }
  28215. showInfo && ql.info.show();
  28216. };
  28217. preview.on(ql.evUpdate, function(e) {
  28218. var file = e.file,
  28219. mime = file.mime.toLowerCase(),
  28220. type = mimes[mime],
  28221. stock, playPromise, opDfd;
  28222. if (mimes[mime] && ql.dispInlineRegex.test(file.mime) /*&& (((type === 'm3u8' || (type === 'mpd' && !fm.UA.iOS) || type === 'flv') && !fm.UA.ltIE10) || ql.support.video[type])*/) {
  28223. autoplay = ql.autoPlay();
  28224. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>');
  28225. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  28226. if (ql.support.video[type] && (type !== 'm3u8' || fm.UA.Safari)) {
  28227. e.stopImmediatePropagation();
  28228. loading.appendTo(ql.info.find('.elfinder-quicklook-info'));
  28229. opDfd = fm.openUrl(file.hash, false, function(url) {
  28230. loading.remove();
  28231. if (url) {
  28232. render(file, { src: url });
  28233. play(node[0]);
  28234. }
  28235. }, { progressBar: prog });
  28236. // stop loading on change file if not loaded yet
  28237. preview.one('change', function() {
  28238. opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject();
  28239. });
  28240. } else {
  28241. if (cHls !== false && fm.options.cdns.hls && type === 'm3u8') {
  28242. e.stopImmediatePropagation();
  28243. loading.appendTo(ql.info.find('.elfinder-quicklook-info'));
  28244. if (cHls) {
  28245. loadHls(file);
  28246. } else {
  28247. stock = window.Hls;
  28248. delete window.Hls;
  28249. fm.loadScript(
  28250. [ fm.options.cdns.hls ],
  28251. function(res) {
  28252. cHls = res || window.Hls || false;
  28253. window.Hls = stock;
  28254. cHls && loadHls(file);
  28255. },
  28256. {
  28257. tryRequire: true,
  28258. error : function() {
  28259. cHls = false;
  28260. }
  28261. }
  28262. );
  28263. }
  28264. } else if (cDash !== false && fm.options.cdns.dash && type === 'mpd') {
  28265. e.stopImmediatePropagation();
  28266. loading.appendTo(ql.info.find('.elfinder-quicklook-info'));
  28267. if (cDash) {
  28268. loadDash(file);
  28269. } else {
  28270. fm.loadScript(
  28271. [ fm.options.cdns.dash ],
  28272. function() {
  28273. // dashjs require window.dashjs in global scope
  28274. cDash = window.dashjs? true : false;
  28275. cDash && loadDash(file);
  28276. },
  28277. {
  28278. tryRequire: true,
  28279. error : function() {
  28280. cDash = false;
  28281. }
  28282. }
  28283. );
  28284. }
  28285. } else if (cFlv !== false && fm.options.cdns.flv && type === 'flv') {
  28286. e.stopImmediatePropagation();
  28287. loading.appendTo(ql.info.find('.elfinder-quicklook-info'));
  28288. if (cFlv) {
  28289. loadFlv(file);
  28290. } else {
  28291. stock = window.flvjs;
  28292. delete window.flvjs;
  28293. fm.loadScript(
  28294. [ fm.options.cdns.flv ],
  28295. function(res) {
  28296. cFlv = res || window.flvjs || false;
  28297. window.flvjs = stock;
  28298. cFlv && loadFlv(file);
  28299. },
  28300. {
  28301. tryRequire: true,
  28302. error : function() {
  28303. cFlv = false;
  28304. }
  28305. }
  28306. );
  28307. }
  28308. } else if (fm.options.cdns.videojs) {
  28309. e.stopImmediatePropagation();
  28310. loading.appendTo(ql.info.find('.elfinder-quicklook-info'));
  28311. if (cVideojs) {
  28312. loadVideojs(file);
  28313. } else {
  28314. fm.loadScript(
  28315. [ fm.options.cdns.videojs + '/video.min.js' ],
  28316. function(res) {
  28317. cVideojs = res || window.videojs || false;
  28318. //window.flvjs = stock;
  28319. cVideojs && loadVideojs(file);
  28320. },
  28321. {
  28322. tryRequire: true,
  28323. error : function() {
  28324. cVideojs = false;
  28325. }
  28326. }
  28327. ).loadCss([fm.options.cdns.videojs + '/video-js.min.css']);
  28328. }
  28329. }
  28330. }
  28331. }
  28332. }).one('change', reset);
  28333. },
  28334. /**
  28335. * Audio/video preview plugin using browser plugins
  28336. *
  28337. * @param elFinder.commands.quicklook
  28338. **/
  28339. function(ql) {
  28340. var preview = ql.preview,
  28341. mimes = [],
  28342. node,
  28343. win = ql.window,
  28344. navi = ql.navbar;
  28345. $.each(navigator.plugins, function(i, plugins) {
  28346. $.each(plugins, function(i, plugin) {
  28347. (plugin.type.indexOf('audio/') === 0 || plugin.type.indexOf('video/') === 0) && mimes.push(plugin.type);
  28348. });
  28349. });
  28350. mimes = ql.fm.arrayFlip(mimes);
  28351. preview.on(ql.evUpdate, function(e) {
  28352. var file = e.file,
  28353. mime = file.mime,
  28354. video, opDfd, loading, prog,
  28355. setNavi = function() {
  28356. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : '');
  28357. };
  28358. if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime)) {
  28359. e.stopImmediatePropagation();
  28360. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  28361. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  28362. opDfd = ql.fm.openUrl(file.hash, false, function(url) {
  28363. loading.remove();
  28364. if (url) {
  28365. (video = mime.indexOf('video/') === 0) && ql.hideinfo();
  28366. node = $('<embed src="'+url+'" type="'+mime+'" class="elfinder-quicklook-preview-'+(video ? 'video' : 'audio')+'"/>')
  28367. .appendTo(preview);
  28368. win.on('viewchange.embed', setNavi);
  28369. setNavi();
  28370. }
  28371. }, { progressBar: prog });
  28372. // stop loading on change file if not loaded yet
  28373. preview.one('change', function() {
  28374. opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject();
  28375. });
  28376. }
  28377. }).one('change', function() {
  28378. if (node && node.parent().length) {
  28379. win.off('viewchange.embed');
  28380. node.remove();
  28381. node= null;
  28382. }
  28383. });
  28384. },
  28385. /**
  28386. * Archive(zip|gzip|tar|bz2) preview plugin using https://github.com/imaya/zlib.js
  28387. *
  28388. * @param elFinder.commands.quicklook
  28389. **/
  28390. function(ql) {
  28391. var fm = ql.fm,
  28392. mimes = fm.arrayFlip(['application/zip', 'application/x-gzip', 'application/x-tar', 'application/x-bzip2']),
  28393. preview = ql.preview,
  28394. sizeMax = fm.returnBytes(ql.options.unzipMaxSize || 0),
  28395. Zlib = (fm.options.cdns.zlibUnzip && fm.options.cdns.zlibGunzip)? true : false,
  28396. bzip2 = fm.options.cdns.bzip2? true : false;
  28397. if (window.Worker && window.Uint8Array && window.DataView) {
  28398. preview.on(ql.evUpdate, function(e) {
  28399. var file = e.file,
  28400. isTar = (file.mime === 'application/x-tar'),
  28401. isBzip2 = (file.mime === 'application/x-bzip2'),
  28402. isZlib = (file.mime === 'application/zip' || file.mime === 'application/x-gzip');
  28403. if (mimes[file.mime] && (!sizeMax || file.size <= sizeMax) && (
  28404. isTar
  28405. || (isBzip2 && bzip2)
  28406. || (isZlib && Zlib)
  28407. )) {
  28408. var jqxhr, wk, loading, prog, url,
  28409. req = function() {
  28410. jqxhr = fm.getContents(file.hash, 'arraybuffer', { progressBar: prog })
  28411. .fail(function() {
  28412. loading.remove();
  28413. })
  28414. .done(function(data) {
  28415. var unzip, filenames,
  28416. err = function(e) {
  28417. wk && wk.terminate();
  28418. loading.remove();
  28419. if (isZlib) {
  28420. Zlib = false;
  28421. } else if (isBzip2) {
  28422. bzip2 = false;
  28423. }
  28424. fm.debug('error', e);
  28425. };
  28426. try {
  28427. wk = fm.getWorker();
  28428. wk.onmessage = function(res) {
  28429. wk && wk.terminate();
  28430. loading.remove();
  28431. if (!res.data || res.data.error) {
  28432. new Error(res.data && res.data.error? res.data.error : '');
  28433. } else {
  28434. makeList(res.data.files);
  28435. }
  28436. };
  28437. wk.onerror = err;
  28438. if (file.mime === 'application/x-tar') {
  28439. wk.postMessage({
  28440. scripts: [fm.getWorkerUrl('quicklook.unzip.js')],
  28441. data: { type: 'tar', bin: data }
  28442. });
  28443. } else if (file.mime === 'application/zip') {
  28444. wk.postMessage({
  28445. scripts: [fm.options.cdns.zlibUnzip, fm.getWorkerUrl('quicklook.unzip.js')],
  28446. data: { type: 'zip', bin: data }
  28447. });
  28448. } else if (file.mime === 'application/x-gzip') {
  28449. wk.postMessage({
  28450. scripts: [fm.options.cdns.zlibGunzip, fm.getWorkerUrl('quicklook.unzip.js')],
  28451. data: { type: 'gzip', bin: data }
  28452. });
  28453. } else if (file.mime === 'application/x-bzip2') {
  28454. wk.postMessage({
  28455. scripts: [fm.options.cdns.bzip2, fm.getWorkerUrl('quicklook.unzip.js')],
  28456. data: { type: 'bzip2', bin: data }
  28457. });
  28458. }
  28459. } catch (e) {
  28460. err(e);
  28461. }
  28462. });
  28463. },
  28464. makeList = function(filenames) {
  28465. var header, list, doc, tsize = 0;
  28466. if (filenames && filenames.length) {
  28467. filenames = $.map(filenames, function(str) {
  28468. return fm.decodeRawString(str);
  28469. });
  28470. filenames.sort();
  28471. list = fm.escape(filenames.join("\n").replace(/\{formatSize\((\d+)\)\}/g, function(m, s) {
  28472. tsize += parseInt(s);
  28473. return fm.formatSize(s);
  28474. }));
  28475. header = '<strong>'+fm.escape(file.mime)+'</strong> ('+fm.formatSize(file.size)+' / '+fm.formatSize(tsize)+')'+'<hr/>';
  28476. doc = $('<div class="elfinder-quicklook-preview-archive-wrapper">'+header+'<pre class="elfinder-quicklook-preview-text">'+list+'</pre></div>')
  28477. .on('touchstart', function(e) {
  28478. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  28479. e.originalEvent._preventSwipeX = true;
  28480. }
  28481. })
  28482. .appendTo(preview);
  28483. ql.hideinfo();
  28484. }
  28485. loading.remove();
  28486. };
  28487. // this is our file - stop event propagation
  28488. e.stopImmediatePropagation();
  28489. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  28490. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  28491. // stop loading on change file if not loaded yet
  28492. preview.one('change', function() {
  28493. jqxhr.state() === 'pending' && jqxhr.reject();
  28494. wk && wk.terminate();
  28495. loading.remove();
  28496. });
  28497. req();
  28498. }
  28499. });
  28500. }
  28501. },
  28502. /**
  28503. * RAR Archive preview plugin using https://github.com/43081j/rar.js
  28504. *
  28505. * @param elFinder.commands.quicklook
  28506. **/
  28507. function(ql) {
  28508. var fm = ql.fm,
  28509. mimes = fm.arrayFlip(['application/x-rar']),
  28510. preview = ql.preview,
  28511. RAR;
  28512. if (window.DataView) {
  28513. preview.on(ql.evUpdate, function(e) {
  28514. var file = e.file;
  28515. if (mimes[file.mime] && fm.options.cdns.rar && RAR !== false) {
  28516. var loading, prog, url, archive, abort,
  28517. getList = function(url) {
  28518. if (abort) {
  28519. loading.remove();
  28520. return;
  28521. }
  28522. try {
  28523. archive = RAR({
  28524. file: url,
  28525. type: 2,
  28526. xhrHeaders: fm.customHeaders,
  28527. xhrFields: fm.xhrFields
  28528. }, function(err) {
  28529. loading.remove();
  28530. var filenames = [],
  28531. header, doc;
  28532. if (abort || err) {
  28533. // An error occurred (not a rar, read error, etc)
  28534. err && fm.debug('error', err);
  28535. return;
  28536. }
  28537. $.each(archive.entries, function() {
  28538. filenames.push(this.path + (this.size? ' (' + fm.formatSize(this.size) + ')' : ''));
  28539. });
  28540. if (filenames.length) {
  28541. filenames = $.map(filenames, function(str) {
  28542. return fm.decodeRawString(str);
  28543. });
  28544. filenames.sort();
  28545. header = '<strong>'+fm.escape(file.mime)+'</strong> ('+fm.formatSize(file.size)+')'+'<hr/>';
  28546. doc = $('<div class="elfinder-quicklook-preview-archive-wrapper">'+header+'<pre class="elfinder-quicklook-preview-text">'+fm.escape(filenames.join("\n"))+'</pre></div>')
  28547. .on('touchstart', function(e) {
  28548. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  28549. e.originalEvent._preventSwipeX = true;
  28550. }
  28551. })
  28552. .appendTo(preview);
  28553. ql.hideinfo();
  28554. }
  28555. });
  28556. } catch(e) {
  28557. loading.remove();
  28558. }
  28559. },
  28560. error = function() {
  28561. RAR = false;
  28562. loading.remove();
  28563. },
  28564. _RAR, opDfd;
  28565. // this is our file - stop event propagation
  28566. e.stopImmediatePropagation();
  28567. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  28568. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  28569. // stop loading on change file if not loaded yet
  28570. preview.one('change', function() {
  28571. archive && (archive.abort = true);
  28572. loading.remove();
  28573. abort = true;
  28574. });
  28575. opDfd = fm.openUrl(file.hash, 'sameorigin', function(url) {
  28576. if (url) {
  28577. if (RAR) {
  28578. getList(url);
  28579. } else {
  28580. if (window.RarArchive) {
  28581. _RAR = window.RarArchive;
  28582. delete window.RarArchive;
  28583. }
  28584. fm.loadScript(
  28585. [ fm.options.cdns.rar ],
  28586. function() {
  28587. if (fm.hasRequire) {
  28588. require(['rar'], function(RarArchive) {
  28589. RAR = RarArchive;
  28590. getList(url);
  28591. }, error);
  28592. } else {
  28593. if (RAR = window.RarArchive) {
  28594. if (_RAR) {
  28595. window.RarArchive = _RAR;
  28596. } else {
  28597. delete window.RarArchive;
  28598. }
  28599. getList(url);
  28600. } else {
  28601. error();
  28602. }
  28603. }
  28604. },
  28605. {
  28606. tryRequire: true,
  28607. error : error
  28608. }
  28609. );
  28610. }
  28611. }
  28612. }, { progressBar: prog, temporary: true });
  28613. // stop loading on change file if not loaded yet
  28614. preview.one('change', function() {
  28615. opDfd && opDfd.state && opDfd.state() === 'pending' && opDfd.reject();
  28616. });
  28617. }
  28618. });
  28619. }
  28620. },
  28621. /**
  28622. * CAD-Files and 3D-Models online viewer on sharecad.org
  28623. *
  28624. * @param elFinder.commands.quicklook
  28625. **/
  28626. function(ql) {
  28627. var fm = ql.fm,
  28628. mimes = fm.arrayFlip(ql.options.sharecadMimes || []),
  28629. preview = ql.preview,
  28630. win = ql.window,
  28631. node;
  28632. if (ql.options.sharecadMimes.length) {
  28633. ql.addIntegration({
  28634. title: 'ShareCAD.org CAD and 3D-Models viewer',
  28635. link: 'https://sharecad.org/DWGOnlinePlugin'
  28636. });
  28637. }
  28638. preview.on(ql.evUpdate, function(e) {
  28639. var file = e.file;
  28640. if (mimes[file.mime.toLowerCase()] && fm.option('onetimeUrl', file.hash)) {
  28641. var win = ql.window,
  28642. loading, prog, url;
  28643. e.stopImmediatePropagation();
  28644. if (file.url == '1') {
  28645. preview.hide();
  28646. $('<div class="elfinder-quicklook-info-data"><button class="elfinder-info-button">'+fm.i18n('getLink')+'</button></div>').appendTo(ql.info.find('.elfinder-quicklook-info'))
  28647. .on('click', function() {
  28648. var self = $(this);
  28649. self.html('<span class="elfinder-spinner">');
  28650. fm.request({
  28651. data : {cmd : 'url', target : file.hash},
  28652. preventDefault : true,
  28653. progressBar : prog
  28654. })
  28655. .always(function() {
  28656. self.html('');
  28657. })
  28658. .done(function(data) {
  28659. var rfile = fm.file(file.hash);
  28660. file.url = rfile.url = data.url || '';
  28661. if (file.url) {
  28662. preview.trigger({
  28663. type: ql.evUpdate,
  28664. file: file,
  28665. forceUpdate: true
  28666. });
  28667. }
  28668. });
  28669. });
  28670. }
  28671. if (file.url !== '' && file.url != '1') {
  28672. preview.one('change', function() {
  28673. loading.remove();
  28674. node.off('load').remove();
  28675. node = null;
  28676. }).addClass('elfinder-overflow-auto');
  28677. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  28678. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  28679. url = fm.convAbsUrl(fm.url(file.hash));
  28680. node = $('<iframe class="elfinder-quicklook-preview-iframe" scrolling="no"></iframe>')
  28681. .css('background-color', 'transparent')
  28682. .appendTo(preview)
  28683. .on('load', function() {
  28684. ql.hideinfo();
  28685. loading.remove();
  28686. ql.preview.after(ql.info);
  28687. $(this).css('background-color', '#fff').show();
  28688. })
  28689. .on('error', function() {
  28690. loading.remove();
  28691. ql.preview.after(ql.info);
  28692. })
  28693. .attr('src', '//sharecad.org/cadframe/load?url=' + encodeURIComponent(url));
  28694. ql.info.after(ql.preview);
  28695. }
  28696. }
  28697. });
  28698. },
  28699. /**
  28700. * KML preview with GoogleMaps API
  28701. *
  28702. * @param elFinder.commands.quicklook
  28703. */
  28704. function(ql) {
  28705. var fm = ql.fm,
  28706. mimes = {
  28707. 'application/vnd.google-earth.kml+xml' : true,
  28708. 'application/vnd.google-earth.kmz' : true
  28709. },
  28710. preview = ql.preview,
  28711. gMaps, loadMap, wGmfail, fail, mapScr;
  28712. if (ql.options.googleMapsApiKey) {
  28713. ql.addIntegration({
  28714. title: 'Google Maps',
  28715. link: 'https://www.google.com/intl/' + fm.lang.replace('_', '-') + '/help/terms_maps.html'
  28716. });
  28717. gMaps = (window.google && google.maps);
  28718. // start load maps
  28719. loadMap = function(file, node, prog) {
  28720. var mapsOpts = ql.options.googleMapsOpts.maps;
  28721. fm.forExternalUrl(file.hash, { progressBar: prog }).done(function(url) {
  28722. if (url) {
  28723. try {
  28724. new gMaps.KmlLayer(url, Object.assign({
  28725. map: new gMaps.Map(node.get(0), mapsOpts)
  28726. }, ql.options.googleMapsOpts.kml));
  28727. ql.hideinfo();
  28728. } catch(e) {
  28729. fail();
  28730. }
  28731. } else {
  28732. fail();
  28733. }
  28734. });
  28735. };
  28736. // keep stored error handler if exists
  28737. wGmfail = window.gm_authFailure;
  28738. // on error function
  28739. fail = function() {
  28740. mapScr = null;
  28741. };
  28742. // API script url
  28743. mapScr = 'https://maps.googleapis.com/maps/api/js?key=' + ql.options.googleMapsApiKey;
  28744. // error handler
  28745. window.gm_authFailure = function() {
  28746. fail();
  28747. wGmfail && wGmfail();
  28748. };
  28749. preview.on(ql.evUpdate, function(e) {
  28750. var file = e.file;
  28751. if (mapScr && mimes[file.mime.toLowerCase()]) {
  28752. var win = ql.window,
  28753. getLink = (file.url == '1' && !fm.option('onetimeUrl', file.hash)),
  28754. loading, prog, url, node;
  28755. e.stopImmediatePropagation();
  28756. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  28757. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  28758. if (getLink) {
  28759. preview.hide();
  28760. $('<div class="elfinder-quicklook-info-data"><button class="elfinder-info-button">'+fm.i18n('getLink')+'</button></div>').appendTo(ql.info.find('.elfinder-quicklook-info'))
  28761. .on('click', function() {
  28762. var self = $(this);
  28763. self.html('<span class="elfinder-spinner">');
  28764. fm.request({
  28765. data : {cmd : 'url', target : file.hash},
  28766. preventDefault : true,
  28767. progressBar : prog
  28768. })
  28769. .always(function() {
  28770. loading.remove();
  28771. self.html('');
  28772. })
  28773. .done(function(data) {
  28774. var rfile = fm.file(file.hash);
  28775. file.url = rfile.url = data.url || '';
  28776. if (file.url) {
  28777. preview.trigger({
  28778. type: ql.evUpdate,
  28779. file: file,
  28780. forceUpdate: true
  28781. });
  28782. }
  28783. });
  28784. });
  28785. }
  28786. if (file.url !== '' && !getLink) {
  28787. node = $('<div style="width:100%;height:100%;"></div>').appendTo(preview);
  28788. preview.one('change', function() {
  28789. node.remove();
  28790. node = null;
  28791. });
  28792. if (!gMaps) {
  28793. fm.loadScript([mapScr], function() {
  28794. gMaps = window.google && google.maps;
  28795. gMaps && loadMap(file, node, prog);
  28796. });
  28797. } else {
  28798. loadMap(file, node, prog);
  28799. }
  28800. }
  28801. }
  28802. });
  28803. }
  28804. },
  28805. /**
  28806. * Any supported files preview plugin using (Google docs | MS Office) online viewer
  28807. *
  28808. * @param elFinder.commands.quicklook
  28809. **/
  28810. function(ql) {
  28811. var fm = ql.fm,
  28812. mimes = Object.assign(fm.arrayFlip(ql.options.googleDocsMimes || [], 'g'), fm.arrayFlip(ql.options.officeOnlineMimes || [], 'm')),
  28813. preview = ql.preview,
  28814. win = ql.window,
  28815. navi = ql.navbar,
  28816. urls = {
  28817. g: 'docs.google.com/gview?embedded=true&url=',
  28818. m: 'view.officeapps.live.com/op/embed.aspx?wdStartOn=0&src='
  28819. },
  28820. navBottom = {
  28821. g: '56px',
  28822. m: '24px'
  28823. },
  28824. mLimits = {
  28825. xls : 5242880, // 5MB
  28826. xlsb : 5242880,
  28827. xlsx : 5242880,
  28828. xlsm : 5242880,
  28829. other: 10485760 // 10MB
  28830. },
  28831. node, enable;
  28832. if (ql.options.googleDocsMimes.length) {
  28833. enable = true;
  28834. ql.addIntegration({
  28835. title: 'Google Docs Viewer',
  28836. link: 'https://docs.google.com/'
  28837. });
  28838. }
  28839. if (ql.options.officeOnlineMimes.length) {
  28840. enable = true;
  28841. ql.addIntegration({
  28842. title: 'MS Online Doc Viewer',
  28843. link: 'https://products.office.com/office-online/view-office-documents-online'
  28844. });
  28845. }
  28846. if (enable) {
  28847. preview.on(ql.evUpdate, function(e) {
  28848. var file = e.file,
  28849. type, dfd;
  28850. // 25MB is maximum filesize of Google Docs prevew
  28851. if (file.size <= 26214400 && (type = mimes[file.mime])) {
  28852. var win = ql.window,
  28853. setNavi = function() {
  28854. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? navBottom[type] : '');
  28855. },
  28856. ext = fm.mimeTypes[file.mime],
  28857. getLink = (file.url == '1' && !fm.option('onetimeUrl', file.hash)),
  28858. loading, prog, url, tm;
  28859. if (type === 'm') {
  28860. if ((mLimits[ext] && file.size > mLimits[ext]) || file.size > mLimits.other) {
  28861. type = 'g';
  28862. }
  28863. }
  28864. if (getLink) {
  28865. preview.hide();
  28866. $('<div class="elfinder-quicklook-info-data"><button class="elfinder-info-button">'+fm.i18n('getLink')+'</button></div>').appendTo(ql.info.find('.elfinder-quicklook-info'))
  28867. .on('click', function() {
  28868. var self = $(this);
  28869. self.html('<span class="elfinder-spinner">');
  28870. fm.request({
  28871. data : {cmd : 'url', target : file.hash},
  28872. preventDefault : true
  28873. })
  28874. .always(function() {
  28875. self.html('');
  28876. })
  28877. .done(function(data) {
  28878. var rfile = fm.file(file.hash);
  28879. file.url = rfile.url = data.url || '';
  28880. if (file.url) {
  28881. preview.trigger({
  28882. type: ql.evUpdate,
  28883. file: file,
  28884. forceUpdate: true
  28885. });
  28886. }
  28887. });
  28888. });
  28889. }
  28890. if (file.url !== '' && !getLink) {
  28891. e.stopImmediatePropagation();
  28892. preview.one('change', function() {
  28893. dfd && dfd.status && dfd.status() === 'pending' && dfd.reject();
  28894. win.off('viewchange.googledocs');
  28895. loading.remove();
  28896. node.off('load').remove();
  28897. node = null;
  28898. }).addClass('elfinder-overflow-auto');
  28899. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  28900. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  28901. node = $('<iframe class="elfinder-quicklook-preview-iframe"></iframe>')
  28902. .css('background-color', 'transparent')
  28903. .appendTo(preview);
  28904. dfd = fm.forExternalUrl(file.hash, { progressBar: prog }).done(function(url) {
  28905. var load = function() {
  28906. try {
  28907. if (node && (!node.attr('src') || node.get(0).contentWindow.document/*maybe HTTP 204*/)) {
  28908. node.attr('src', 'https://' + urls[type] + encodeURIComponent(url));
  28909. // Retry because Google Docs viewer sometimes returns HTTP 204
  28910. tm = setTimeout(load, 2000);
  28911. }
  28912. } catch(e) {}
  28913. };
  28914. if (url) {
  28915. if (file.ts) {
  28916. url += (url.match(/\?/)? '&' : '?') + '_t=' + file.ts;
  28917. }
  28918. node.on('load', function() {
  28919. tm && clearTimeout(tm);
  28920. ql.hideinfo();
  28921. loading.remove();
  28922. ql.preview.after(ql.info);
  28923. $(this).css('background-color', '#fff').show();
  28924. })
  28925. .on('error', function() {
  28926. tm && clearTimeout(tm);
  28927. loading.remove();
  28928. ql.preview.after(ql.info);
  28929. });
  28930. load();
  28931. } else {
  28932. loading.remove();
  28933. node.remove();
  28934. }
  28935. });
  28936. win.on('viewchange.googledocs', setNavi);
  28937. setNavi();
  28938. ql.info.after(ql.preview);
  28939. }
  28940. }
  28941. });
  28942. }
  28943. },
  28944. /**
  28945. * Texts preview plugin
  28946. *
  28947. * @param elFinder.commands.quicklook
  28948. **/
  28949. function(ql) {
  28950. "use strict";
  28951. var fm = ql.fm,
  28952. preview = ql.preview,
  28953. textLines = parseInt(ql.options.textInitialLines) || 150,
  28954. prettifyLines = parseInt(ql.options.prettifyMaxLines) || 500,
  28955. PR, _PR,
  28956. error = function() {
  28957. prettify = function() { return false; };
  28958. _PR && (window.PR = _PR);
  28959. PR = false;
  28960. },
  28961. prettify = function(node) {
  28962. if (fm.options.cdns.prettify) {
  28963. prettify = function(node) {
  28964. setTimeout(function() {
  28965. PRcheck(node);
  28966. }, 100);
  28967. return 'pending';
  28968. };
  28969. if (window.PR) {
  28970. _PR = window.PR;
  28971. }
  28972. fm.loadScript([fm.options.cdns.prettify + (fm.options.cdns.prettify.match(/\?/)? '&' : '?') + 'autorun=false'], function(wPR) {
  28973. PR = wPR || window.PR;
  28974. if (typeof PR === 'object') {
  28975. prettify = function() { return true; };
  28976. if (_PR) {
  28977. window.PR = _PR;
  28978. } else {
  28979. delete window.PR;
  28980. }
  28981. exec(node);
  28982. } else {
  28983. error();
  28984. }
  28985. }, {
  28986. tryRequire: true,
  28987. error : error
  28988. });
  28989. } else {
  28990. error();
  28991. }
  28992. },
  28993. exec = function(node) {
  28994. if (node && !node.hasClass('prettyprinted')) {
  28995. node.css('cursor', 'wait');
  28996. requestAnimationFrame(function() {
  28997. PR.prettyPrint && PR.prettyPrint(null, node.get(0));
  28998. node.css('cursor', '');
  28999. });
  29000. }
  29001. },
  29002. PRcheck = function(node) {
  29003. var status = prettify(node);
  29004. if (status === true) {
  29005. exec(node);
  29006. }
  29007. };
  29008. preview.on(ql.evUpdate, function(e) {
  29009. var file = e.file,
  29010. mime = file.mime,
  29011. jqxhr, loading, prog, encSelect;
  29012. if (fm.mimeIsText(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax) && PR !== false) {
  29013. e.stopImmediatePropagation();
  29014. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"></span></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  29015. prog = $('<div class="elfinder-quicklook-info-progress"></div>').appendTo(loading);
  29016. // stop loading on change file if not loadin yet
  29017. preview.one('change', function() {
  29018. jqxhr.state() == 'pending' && jqxhr.reject();
  29019. encSelect && encSelect.remove();
  29020. });
  29021. jqxhr = fm.request({
  29022. data : {cmd : 'get', target : file.hash, conv : (file.encoding || 1), _t : file.ts},
  29023. options : {type: 'get', cache : true},
  29024. preventDefault : true,
  29025. progressBar : prog
  29026. })
  29027. .done(function(data) {
  29028. var reg = new RegExp('^(data:'+file.mime.replace(/([.+])/g, '\\$1')+';base64,)', 'i'),
  29029. text = data.content,
  29030. part, more, node, lines, m;
  29031. if (typeof text !== 'string') {
  29032. return;
  29033. }
  29034. ql.hideinfo();
  29035. if (window.atob && (m = text.match(reg))) {
  29036. text = atob(text.substr(m[1].length));
  29037. }
  29038. lines = text.match(/([^\r\n]{1,100}[\r\n]*)/g);
  29039. more = lines.length - textLines;
  29040. if (more > 10) {
  29041. part = lines.splice(0, textLines).join('');
  29042. } else {
  29043. more = 0;
  29044. }
  29045. node = $('<div class="elfinder-quicklook-preview-text-wrapper"><pre class="elfinder-quicklook-preview-text prettyprint"></pre></div>');
  29046. if (more) {
  29047. node.append($('<div class="elfinder-quicklook-preview-charsleft"><hr/><span>' + fm.i18n('linesLeft', fm.toLocaleString(more)) + '</span></div>')
  29048. .on('click', function() {
  29049. var top = node.scrollTop();
  29050. $(this).remove();
  29051. node.children('pre').removeClass('prettyprinted').text(text).scrollTop(top);
  29052. if (lines.length <= prettifyLines) {
  29053. PRcheck(node);
  29054. }
  29055. })
  29056. );
  29057. }
  29058. node.children('pre').text(part || text);
  29059. node.on('touchstart', function(e) {
  29060. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  29061. e.originalEvent._preventSwipeX = true;
  29062. }
  29063. }).appendTo(preview);
  29064. // make toast message
  29065. if (data.toasts && Array.isArray(data.toasts)) {
  29066. $.each(data.toasts, function() {
  29067. this.msg && fm.toast(this);
  29068. });
  29069. }
  29070. PRcheck(node);
  29071. })
  29072. .always(function(data) {
  29073. var cmdEdit, sel, head;
  29074. if (cmdEdit = fm.getCommand('edit')) {
  29075. head = [];
  29076. if (data && data.encoding) {
  29077. head.push({value: data.encoding});
  29078. }
  29079. head.push({value: 'UTF-8'});
  29080. sel = cmdEdit.getEncSelect(head);
  29081. sel.on('change', function() {
  29082. file.encoding = sel.val();
  29083. fm.cache(file, 'change');
  29084. preview.trigger({
  29085. type: ql.evUpdate,
  29086. file: file,
  29087. forceUpdate: true
  29088. });
  29089. });
  29090. encSelect = $('<div class="elfinder-quicklook-encoding"></div>').append(sel);
  29091. ql.window.append(encSelect);
  29092. }
  29093. loading.remove();
  29094. });
  29095. }
  29096. });
  29097. }
  29098. ];
  29099. /*
  29100. * File: /js/commands/reload.js
  29101. */
  29102. /**
  29103. * @class elFinder command "reload"
  29104. * Sync files and folders
  29105. *
  29106. * @author Dmitry (dio) Levashov
  29107. **/
  29108. (elFinder.prototype.commands.reload = function() {
  29109. "use strict";
  29110. var self = this,
  29111. search = false;
  29112. this.alwaysEnabled = true;
  29113. this.updateOnSelect = true;
  29114. this.shortcuts = [{
  29115. pattern : 'ctrl+shift+r f5'
  29116. }];
  29117. this.getstate = function() {
  29118. return 0;
  29119. };
  29120. this.init = function() {
  29121. this.fm.bind('search searchend', function() {
  29122. search = this.type == 'search';
  29123. });
  29124. };
  29125. this.fm.bind('contextmenu', function(){
  29126. var fm = self.fm;
  29127. if (fm.options.sync >= 1000) {
  29128. self.extra = {
  29129. icon: 'accept',
  29130. node: $('<span></span>')
  29131. .attr({title: fm.i18n('autoSync')})
  29132. .on('click touchstart', function(e){
  29133. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  29134. return;
  29135. }
  29136. e.stopPropagation();
  29137. e.preventDefault();
  29138. $(this).parent()
  29139. .toggleClass('ui-state-disabled', fm.options.syncStart)
  29140. .parent().removeClass('ui-state-hover');
  29141. fm.options.syncStart = !fm.options.syncStart;
  29142. fm.autoSync(fm.options.syncStart? null : 'stop');
  29143. }).on('ready', function(){
  29144. $(this).parent().toggleClass('ui-state-disabled', !fm.options.syncStart).css('pointer-events', 'auto');
  29145. })
  29146. };
  29147. }
  29148. });
  29149. this.exec = function() {
  29150. var fm = this.fm;
  29151. if (!search) {
  29152. var dfrd = fm.sync(),
  29153. timeout = setTimeout(function() {
  29154. fm.notify({type : 'reload', cnt : 1, hideCnt : true});
  29155. dfrd.always(function() { fm.notify({type : 'reload', cnt : -1}); });
  29156. }, fm.notifyDelay);
  29157. return dfrd.always(function() {
  29158. clearTimeout(timeout);
  29159. fm.trigger('reload');
  29160. });
  29161. } else {
  29162. $('div.elfinder-toolbar > div.'+fm.res('class', 'searchbtn') + ' > span.ui-icon-search').click();
  29163. }
  29164. };
  29165. }).prototype = { forceLoad : true }; // this is required command
  29166. /*
  29167. * File: /js/commands/rename.js
  29168. */
  29169. /**
  29170. * @class elFinder command "rename".
  29171. * Rename selected file.
  29172. *
  29173. * @author Dmitry (dio) Levashov, dio@std42.ru
  29174. * @author Naoki Sawada
  29175. **/
  29176. elFinder.prototype.commands.rename = function() {
  29177. "use strict";
  29178. // set alwaysEnabled to allow root rename on client size
  29179. this.alwaysEnabled = true;
  29180. this.syncTitleOnChange = true;
  29181. var self = this,
  29182. fm = self.fm,
  29183. request = function(dfrd, targtes, file, name) {
  29184. var sel = targtes? [file.hash].concat(targtes) : [file.hash],
  29185. cnt = sel.length,
  29186. data = {}, rootNames;
  29187. fm.lockfiles({files : sel});
  29188. if (fm.isRoot(file) && !file.netkey) {
  29189. if (!(rootNames = fm.storage('rootNames'))) {
  29190. rootNames = {};
  29191. }
  29192. if (name === '') {
  29193. if (rootNames[file.hash]) {
  29194. file.name = file._name;
  29195. file.i18 = file._i18;
  29196. delete rootNames[file.hash];
  29197. delete file._name;
  29198. delete file._i18;
  29199. } else {
  29200. dfrd && dfrd.reject();
  29201. fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel});
  29202. return;
  29203. }
  29204. } else {
  29205. if (typeof file._name === 'undefined') {
  29206. file._name = file.name;
  29207. file._i18 = file.i18;
  29208. }
  29209. file.name = rootNames[file.hash] = name;
  29210. delete file.i18;
  29211. }
  29212. fm.storage('rootNames', rootNames);
  29213. data = { changed: [file] };
  29214. fm.updateCache(data);
  29215. fm.change(data);
  29216. dfrd && dfrd.resolve(data);
  29217. fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel});
  29218. return;
  29219. }
  29220. data = {
  29221. cmd : 'rename',
  29222. name : name,
  29223. target : file.hash
  29224. };
  29225. if (cnt > 1) {
  29226. data['targets'] = targtes;
  29227. if (name.match(/\*/)) {
  29228. data['q'] = name;
  29229. }
  29230. }
  29231. fm.request({
  29232. data : data,
  29233. notify : {type : 'rename', cnt : cnt},
  29234. navigate : {}
  29235. })
  29236. .fail(function(error) {
  29237. var err = fm.parseError(error);
  29238. dfrd && dfrd.reject();
  29239. if (! err || ! Array.isArray(err) || err[0] !== 'errRename') {
  29240. fm.sync();
  29241. }
  29242. })
  29243. .done(function(data) {
  29244. var cwdHash;
  29245. if (data.added && data.added.length && cnt === 1) {
  29246. data.undo = {
  29247. cmd : 'rename',
  29248. callback : function() {
  29249. return fm.request({
  29250. data : {cmd : 'rename', target : data.added[0].hash, name : file.name},
  29251. notify : {type : 'undo', cnt : 1}
  29252. });
  29253. }
  29254. };
  29255. data.redo = {
  29256. cmd : 'rename',
  29257. callback : function() {
  29258. return fm.request({
  29259. data : {cmd : 'rename', target : file.hash, name : name},
  29260. notify : {type : 'rename', cnt : 1}
  29261. });
  29262. }
  29263. };
  29264. }
  29265. dfrd && dfrd.resolve(data);
  29266. if (!(cwdHash = fm.cwd().hash) || cwdHash === file.hash) {
  29267. fm.exec('open', $.map(data.added, function(f) {
  29268. return (f.mime === 'directory')? f.hash : null;
  29269. })[0]);
  29270. }
  29271. })
  29272. .always(function() {
  29273. fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel});
  29274. }
  29275. );
  29276. },
  29277. getHint = function(name, target) {
  29278. var sel = target || fm.selected(),
  29279. splits = fm.splitFileExtention(name),
  29280. f1 = fm.file(sel[0]),
  29281. f2 = fm.file(sel[1]),
  29282. ext, hint, add;
  29283. ext = splits[1]? ('.' + splits[1]) : '';
  29284. if (splits[1] && splits[0] === '*') {
  29285. // change extention
  29286. hint = '"' + fm.splitFileExtention(f1.name)[0] + ext + '", ';
  29287. hint += '"' + fm.splitFileExtention(f2.name)[0] + ext + '"';
  29288. } else if (splits[0].length > 1) {
  29289. if (splits[0].substr(-1) === '*') {
  29290. // add prefix
  29291. add = splits[0].substr(0, splits[0].length - 1);
  29292. hint = '"' + add + f1.name+'", ';
  29293. hint += '"' + add + f2.name+'"';
  29294. } else if (splits[0].substr(0, 1) === '*') {
  29295. // add suffix
  29296. add = splits[0].substr(1);
  29297. hint = '"'+fm.splitFileExtention(f1.name)[0] + add + ext + '", ';
  29298. hint += '"'+fm.splitFileExtention(f2.name)[0] + add + ext + '"';
  29299. }
  29300. }
  29301. if (!hint) {
  29302. hint = '"'+splits[0] + '1' + ext + '", "' + splits[0] + '2' + ext + '"';
  29303. }
  29304. if (sel.length > 2) {
  29305. hint += ' ...';
  29306. }
  29307. return hint;
  29308. },
  29309. batchRename = function() {
  29310. var sel = fm.selected(),
  29311. tplr = '<input name="type" type="radio" class="elfinder-tabstop">',
  29312. mkChk = function(node, label) {
  29313. return $('<label class="elfinder-rename-batch-checks">' + fm.i18n(label) + '</label>').prepend(node);
  29314. },
  29315. name = $('<input type="text" class="ui-corner-all elfinder-tabstop">'),
  29316. num = $(tplr),
  29317. prefix = $(tplr),
  29318. suffix = $(tplr),
  29319. extention = $(tplr),
  29320. checks = $('<div></div>').append(
  29321. mkChk(num, 'plusNumber'),
  29322. mkChk(prefix, 'asPrefix'),
  29323. mkChk(suffix, 'asSuffix'),
  29324. mkChk(extention, 'changeExtention')
  29325. ),
  29326. preview = $('<div class="elfinder-rename-batch-preview"></div>'),
  29327. node = $('<div class="elfinder-rename-batch"></div>').append(
  29328. $('<div class="elfinder-rename-batch-name"></div>').append(name),
  29329. $('<div class="elfinder-rename-batch-type"></div>').append(checks),
  29330. preview
  29331. ),
  29332. opts = {
  29333. title : fm.i18n('batchRename'),
  29334. modal : true,
  29335. destroyOnClose : true,
  29336. width: Math.min(380, fm.getUI().width() - 20),
  29337. buttons : {},
  29338. open : function() {
  29339. name.on('input', mkPrev).trigger('focus');
  29340. }
  29341. },
  29342. getName = function() {
  29343. var vName = name.val(),
  29344. ext = fm.splitFileExtention(fm.file(sel[0]).name)[1];
  29345. if (vName !== '' || num.is(':checked')) {
  29346. if (prefix.is(':checked')) {
  29347. vName += '*';
  29348. } else if (suffix.is(':checked')) {
  29349. vName = '*' + vName + '.' + ext;
  29350. } else if (extention.is(':checked')) {
  29351. vName = '*.' + vName;
  29352. } else if (ext) {
  29353. vName += '.' + ext;
  29354. }
  29355. }
  29356. return vName;
  29357. },
  29358. mkPrev = function() {
  29359. var vName = getName();
  29360. if (vName !== '') {
  29361. preview.html(fm.i18n(['renameMultiple', sel.length, getHint(vName)]));
  29362. } else {
  29363. preview.empty();
  29364. }
  29365. },
  29366. radios = checks.find('input:radio').on('change', mkPrev),
  29367. dialog;
  29368. opts.buttons[fm.i18n('btnApply')] = function() {
  29369. var vName = getName(),
  29370. file, targets;
  29371. if (vName !== '') {
  29372. dialog.elfinderdialog('close');
  29373. targets = sel;
  29374. file = fm.file(targets.shift());
  29375. request(void(0), targets, file, vName);
  29376. }
  29377. };
  29378. opts.buttons[fm.i18n('btnCancel')] = function() {
  29379. dialog.elfinderdialog('close');
  29380. };
  29381. if ($.fn.checkboxradio) {
  29382. radios.checkboxradio({
  29383. create: function(e, ui) {
  29384. if (this === num.get(0)) {
  29385. num.prop('checked', true).change();
  29386. }
  29387. }
  29388. });
  29389. } else {
  29390. checks.buttonset({
  29391. create: function(e, ui) {
  29392. num.prop('checked', true).change();
  29393. }
  29394. });
  29395. }
  29396. dialog = self.fmDialog(node, opts);
  29397. };
  29398. this.noChangeDirOnRemovedCwd = true;
  29399. this.shortcuts = [{
  29400. pattern : 'f2' + (fm.OS == 'mac' ? ' enter' : '')
  29401. }, {
  29402. pattern : 'shift+f2',
  29403. description : 'batchRename',
  29404. callback : function() {
  29405. fm.selected().length > 1 && batchRename();
  29406. }
  29407. }];
  29408. this.getstate = function(select) {
  29409. var sel = this.files(select),
  29410. cnt = sel.length,
  29411. phash, ext, mime, brk, state, isRoot;
  29412. if (!cnt) {
  29413. return -1;
  29414. }
  29415. if (cnt > 1 && sel[0].phash) {
  29416. phash = sel[0].phash;
  29417. ext = fm.splitFileExtention(sel[0].name)[1].toLowerCase();
  29418. mime = sel[0].mime;
  29419. }
  29420. if (cnt === 1) {
  29421. isRoot = fm.isRoot(sel[0]);
  29422. }
  29423. state = (cnt === 1 && ((fm.cookieEnabled && isRoot) || !sel[0].locked) || (fm.api > 2.1030 && cnt === $.grep(sel, function(f) {
  29424. if (!brk && !f.locked && f.phash === phash && !fm.isRoot(f) && (mime === f.mime || ext === fm.splitFileExtention(f.name)[1].toLowerCase())) {
  29425. return true;
  29426. } else {
  29427. brk && (brk = true);
  29428. return false;
  29429. }
  29430. }).length)) ? 0 : -1;
  29431. // because alwaysEnabled = true, it need check disabled on connector
  29432. if (!isRoot && state === 0 && fm.option('disabledFlip', sel[0].hash)['rename']) {
  29433. state = -1;
  29434. }
  29435. if (state !== -1 && cnt > 1) {
  29436. self.extra = {
  29437. icon: 'preference',
  29438. node: $('<span></span>')
  29439. .attr({title: fm.i18n('batchRename')})
  29440. .on('click touchstart', function(e){
  29441. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  29442. return;
  29443. }
  29444. e.stopPropagation();
  29445. e.preventDefault();
  29446. fm.getUI().trigger('click'); // to close the context menu immediately
  29447. batchRename();
  29448. })
  29449. };
  29450. } else {
  29451. delete self.extra;
  29452. }
  29453. return state;
  29454. };
  29455. this.exec = function(hashes, cOpts) {
  29456. var cwd = fm.getUI('cwd'),
  29457. sel = hashes || (fm.selected().length? fm.selected() : false) || [fm.cwd().hash],
  29458. cnt = sel.length,
  29459. file = fm.file(sel.shift()),
  29460. filename = '.elfinder-cwd-filename',
  29461. opts = cOpts || {},
  29462. incwd = (fm.cwd().hash == file.hash),
  29463. type = (opts._currentType === 'navbar' || opts._currentType === 'files')? opts._currentType : (incwd? 'navbar' : 'files'),
  29464. navbar = (type !== 'files'),
  29465. target = fm[navbar? 'navHash2Elm' : 'cwdHash2Elm'](file.hash),
  29466. tarea = (!navbar && fm.storage('view') != 'list'),
  29467. split = function(name) {
  29468. var ext = fm.splitFileExtention(name)[1];
  29469. return [name.substr(0, name.length - ext.length - 1), ext];
  29470. },
  29471. unselect = function() {
  29472. requestAnimationFrame(function() {
  29473. input && input.trigger('blur');
  29474. });
  29475. },
  29476. rest = function(){
  29477. if (!overlay.is(':hidden')) {
  29478. overlay.elfinderoverlay('hide').off('click close', cancel);
  29479. }
  29480. pnode.removeClass('ui-front')
  29481. .css('position', '')
  29482. .off('unselect.'+fm.namespace, unselect);
  29483. if (tarea) {
  29484. node && node.css('max-height', '');
  29485. } else if (!navbar) {
  29486. pnode.css('width', '')
  29487. .parent('td').css('overflow', '');
  29488. }
  29489. }, colwidth,
  29490. dfrd = $.Deferred()
  29491. .fail(function(error) {
  29492. var parent = input.parent(),
  29493. name = fm.escape(file.i18 || file.name);
  29494. input.off();
  29495. if (tarea) {
  29496. name = name.replace(/([_.])/g, '&#8203;$1');
  29497. }
  29498. requestAnimationFrame(function() {
  29499. if (navbar) {
  29500. input.replaceWith(name);
  29501. } else {
  29502. if (parent.length) {
  29503. input.remove();
  29504. parent.html(name);
  29505. } else {
  29506. target.find(filename).html(name);
  29507. }
  29508. }
  29509. });
  29510. error && fm.error(error);
  29511. })
  29512. .always(function() {
  29513. rest();
  29514. fm.unbind('resize', resize);
  29515. fm.enable();
  29516. }),
  29517. blur = function(e) {
  29518. var name = $.trim(input.val()),
  29519. splits = fm.splitFileExtention(name),
  29520. valid = true,
  29521. req = function() {
  29522. input.off();
  29523. rest();
  29524. if (navbar) {
  29525. input.replaceWith(fm.escape(name));
  29526. } else {
  29527. node.html(fm.escape(name));
  29528. }
  29529. request(dfrd, sel, file, name);
  29530. };
  29531. if (!overlay.is(':hidden')) {
  29532. pnode.css('z-index', '');
  29533. }
  29534. if (name === '') {
  29535. if (!fm.isRoot(file)) {
  29536. return cancel();
  29537. }
  29538. if (navbar) {
  29539. input.replaceWith(fm.escape(file.name));
  29540. } else {
  29541. node.html(fm.escape(file.name));
  29542. }
  29543. }
  29544. if (!inError && pnode.length) {
  29545. input.off('blur');
  29546. if (cnt === 1 && name === file.name) {
  29547. return dfrd.reject();
  29548. }
  29549. if (fm.options.validName && fm.options.validName.test) {
  29550. try {
  29551. valid = fm.options.validName.test(name);
  29552. } catch(e) {
  29553. valid = false;
  29554. }
  29555. }
  29556. if (name === '.' || name === '..' || !valid) {
  29557. inError = true;
  29558. fm.error(file.mime === 'directory'? 'errInvDirname' : 'errInvName', {modal: true, close: function(){setTimeout(select, 120);}});
  29559. return false;
  29560. }
  29561. if (cnt === 1 && fm.fileByName(name, file.phash)) {
  29562. inError = true;
  29563. fm.error(['errExists', name], {modal: true, close: function(){setTimeout(select, 120);}});
  29564. return false;
  29565. }
  29566. if (cnt === 1) {
  29567. req();
  29568. } else {
  29569. fm.confirm({
  29570. title : 'cmdrename',
  29571. text : ['renameMultiple', cnt, getHint(name, [file.hash].concat(sel))],
  29572. accept : {
  29573. label : 'btnYes',
  29574. callback : req
  29575. },
  29576. cancel : {
  29577. label : 'btnCancel',
  29578. callback : function() {
  29579. setTimeout(function() {
  29580. inError = true;
  29581. select();
  29582. }, 120);
  29583. }
  29584. }
  29585. });
  29586. setTimeout(function() {
  29587. fm.trigger('unselectfiles', {files: fm.selected()})
  29588. .trigger('selectfiles', {files : [file.hash].concat(sel)});
  29589. }, 120);
  29590. }
  29591. }
  29592. },
  29593. input = $(tarea? '<textarea></textarea>' : '<input type="text"/>')
  29594. .on('keyup text', function(){
  29595. if (tarea) {
  29596. this.style.height = '1px';
  29597. this.style.height = this.scrollHeight + 'px';
  29598. } else if (colwidth) {
  29599. this.style.width = colwidth + 'px';
  29600. if (this.scrollWidth > colwidth) {
  29601. this.style.width = this.scrollWidth + 10 + 'px';
  29602. }
  29603. }
  29604. })
  29605. .on('keydown', function(e) {
  29606. e.stopImmediatePropagation();
  29607. if (e.keyCode == $.ui.keyCode.ESCAPE) {
  29608. dfrd.reject();
  29609. } else if (e.keyCode == $.ui.keyCode.ENTER) {
  29610. e.preventDefault();
  29611. input.trigger('blur');
  29612. }
  29613. })
  29614. .on('mousedown click dblclick', function(e) {
  29615. e.stopPropagation();
  29616. if (e.type === 'dblclick') {
  29617. e.preventDefault();
  29618. }
  29619. })
  29620. .on('blur', blur)
  29621. .on('dragenter dragleave dragover drop', function(e) {
  29622. // stop bubbling to prevent upload with native drop event
  29623. e.stopPropagation();
  29624. }),
  29625. select = function() {
  29626. var name = fm.splitFileExtention(input.val())[0];
  29627. if (!inError && fm.UA.Mobile && !fm.UA.iOS) { // since iOS has a bug? (z-index not effect) so disable it
  29628. overlay.on('click close', cancel).elfinderoverlay('show');
  29629. pnode.css('z-index', overlay.css('z-index') + 1);
  29630. }
  29631. ! fm.enabled() && fm.enable();
  29632. if (inError) {
  29633. inError = false;
  29634. input.on('blur', blur);
  29635. }
  29636. input.trigger('focus').trigger('select');
  29637. input[0].setSelectionRange && input[0].setSelectionRange(0, name.length);
  29638. },
  29639. node = navbar? target.contents().filter(function(){ return this.nodeType==3 && $(this).parent().attr('id') === fm.navHash2Id(file.hash); })
  29640. : target.find(filename),
  29641. pnode = node.parent(),
  29642. overlay = fm.getUI('overlay'),
  29643. cancel = function(e) {
  29644. if (!overlay.is(':hidden')) {
  29645. pnode.css('z-index', '');
  29646. }
  29647. if (! inError) {
  29648. dfrd.reject();
  29649. if (e) {
  29650. e.stopPropagation();
  29651. e.preventDefault();
  29652. }
  29653. }
  29654. },
  29655. resize = function() {
  29656. target.trigger('scrolltoview', {blink : false});
  29657. },
  29658. inError = false;
  29659. pnode.addClass('ui-front')
  29660. .css('position', 'relative')
  29661. .on('unselect.'+fm.namespace, unselect);
  29662. fm.bind('resize', resize);
  29663. if (navbar) {
  29664. node.replaceWith(input.val(file.name));
  29665. } else {
  29666. if (tarea) {
  29667. node.css('max-height', 'none');
  29668. } else if (!navbar) {
  29669. colwidth = pnode.width();
  29670. pnode.width(colwidth - 15)
  29671. .parent('td').css('overflow', 'visible');
  29672. }
  29673. node.empty().append(input.val(file.name));
  29674. }
  29675. if (cnt > 1 && fm.api <= 2.1030) {
  29676. return dfrd.reject();
  29677. }
  29678. if (!file || !node.length) {
  29679. return dfrd.reject('errCmdParams', this.title);
  29680. }
  29681. if (file.locked && !fm.isRoot(file)) {
  29682. return dfrd.reject(['errLocked', file.name]);
  29683. }
  29684. fm.one('select', function() {
  29685. input.parent().length && file && $.inArray(file.hash, fm.selected()) === -1 && input.trigger('blur');
  29686. });
  29687. input.trigger('keyup');
  29688. select();
  29689. return dfrd;
  29690. };
  29691. fm.bind('select contextmenucreate closecontextmenu', function(e) {
  29692. var sel = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected(),
  29693. file;
  29694. if (sel && sel.length === 1 && (file = fm.file(sel[0])) && fm.isRoot(file)) {
  29695. self.title = fm.i18n('kindAlias') + ' (' + fm.i18n('preference') + ')';
  29696. } else {
  29697. self.title = fm.i18n('cmdrename');
  29698. }
  29699. if (e.type !== 'closecontextmenu') {
  29700. self.update(void(0), self.title);
  29701. } else {
  29702. requestAnimationFrame(function() {
  29703. self.update(void(0), self.title);
  29704. });
  29705. }
  29706. }).remove(function(e) {
  29707. var rootNames;
  29708. if (e.data && e.data.removed && (rootNames = fm.storage('rootNames'))) {
  29709. $.each(e.data.removed, function(i, h) {
  29710. if (rootNames[h]) {
  29711. delete rootNames[h];
  29712. }
  29713. });
  29714. fm.storage('rootNames', rootNames);
  29715. }
  29716. });
  29717. };
  29718. /*
  29719. * File: /js/commands/resize.js
  29720. */
  29721. /**
  29722. * @class elFinder command "resize"
  29723. * Open dialog to resize image
  29724. *
  29725. * @author Dmitry (dio) Levashov
  29726. * @author Alexey Sukhotin
  29727. * @author Naoki Sawada
  29728. * @author Sergio Jovani
  29729. **/
  29730. elFinder.prototype.commands.resize = function() {
  29731. "use strict";
  29732. var fm = this.fm,
  29733. losslessRotate = 0,
  29734. getBounceBox = function(w, h, theta) {
  29735. var srcPts = [
  29736. {x: w/2, y: h/2},
  29737. {x: -w/2, y: h/2},
  29738. {x: -w/2, y: -h/2},
  29739. {x: w/2, y: -h/2}
  29740. ],
  29741. dstPts = [],
  29742. min = {x: Number.MAX_VALUE, y: Number.MAX_VALUE},
  29743. max = {x: Number.MIN_VALUE, y: Number.MIN_VALUE};
  29744. $.each(srcPts, function(i, srcPt){
  29745. dstPts.push({
  29746. x: srcPt.x * Math.cos(theta) - srcPt.y * Math.sin(theta),
  29747. y: srcPt.x * Math.sin(theta) + srcPt.y * Math.cos(theta)
  29748. });
  29749. });
  29750. $.each(dstPts, function(i, pt) {
  29751. min.x = Math.min(min.x, pt.x);
  29752. min.y = Math.min(min.y, pt.y);
  29753. max.x = Math.max(max.x, pt.x);
  29754. max.y = Math.max(max.y, pt.y);
  29755. });
  29756. return {
  29757. width: max.x - min.x, height: max.y - min.y
  29758. };
  29759. };
  29760. this.updateOnSelect = false;
  29761. this.getstate = function() {
  29762. var sel = fm.selectedFiles();
  29763. return sel.length == 1 && sel[0].read && sel[0].write && sel[0].mime.indexOf('image/') !== -1 ? 0 : -1;
  29764. };
  29765. this.resizeRequest = function(data, f, dfrd) {
  29766. var file = f || fm.file(data.target),
  29767. tmb = file? file.tmb : null,
  29768. enabled = fm.isCommandEnabled('resize', data.target);
  29769. if (enabled && (! file || (file && file.read && file.write && file.mime.indexOf('image/') !== -1 ))) {
  29770. return fm.request({
  29771. data : Object.assign(data, {
  29772. cmd : 'resize'
  29773. }),
  29774. notify : {type : 'resize', cnt : 1}
  29775. })
  29776. .fail(function(error) {
  29777. if (dfrd) {
  29778. dfrd.reject(error);
  29779. }
  29780. })
  29781. .done(function() {
  29782. if (data.quality) {
  29783. fm.storage('jpgQuality', data.quality === fm.option('jpgQuality')? null : data.quality);
  29784. }
  29785. dfrd && dfrd.resolve();
  29786. });
  29787. } else {
  29788. var error;
  29789. if (file) {
  29790. if (file.mime.indexOf('image/') === -1) {
  29791. error = ['errResize', file.name, 'errUsupportType'];
  29792. } else {
  29793. error = ['errResize', file.name, 'errPerm'];
  29794. }
  29795. } else {
  29796. error = ['errResize', data.target, 'errPerm'];
  29797. }
  29798. if (dfrd) {
  29799. dfrd.reject(error);
  29800. } else {
  29801. fm.error(error);
  29802. }
  29803. return $.Deferred().reject(error);
  29804. }
  29805. };
  29806. this.exec = function(hashes) {
  29807. var self = this,
  29808. files = this.files(hashes),
  29809. dfrd = $.Deferred(),
  29810. api2 = (fm.api > 1),
  29811. options = this.options,
  29812. dialogWidth = 650,
  29813. fmnode = fm.getUI(),
  29814. ctrgrup = $().controlgroup? 'controlgroup' : 'buttonset',
  29815. grid8Def = typeof options.grid8px === 'undefined' || options.grid8px !== 'disable'? true : false,
  29816. presetSize = Array.isArray(options.presetSize)? options.presetSize : [],
  29817. clactive = 'elfinder-dialog-active',
  29818. clsediting = fm.res('class', 'editing'),
  29819. open = function(file, id, src) {
  29820. var isJpeg = (file.mime === 'image/jpeg'),
  29821. dialog = $('<div class="elfinder-resize-container"></div>'),
  29822. input = '<input type="number" class="ui-corner-all"/>',
  29823. row = '<div class="elfinder-resize-row"></div>',
  29824. label = '<div class="elfinder-resize-label"></div>',
  29825. changeTm = null,
  29826. operate = false,
  29827. opStart = function() { operate = true; },
  29828. opStop = function() {
  29829. if (operate) {
  29830. operate = false;
  29831. control.trigger('change');
  29832. }
  29833. },
  29834. control = $('<div class="elfinder-resize-control"></div>')
  29835. .on('focus', 'input[type=text],input[type=number]', function() {
  29836. $(this).trigger('select');
  29837. })
  29838. .on('change', function() {
  29839. changeTm && cancelAnimationFrame(changeTm);
  29840. changeTm = requestAnimationFrame(function() {
  29841. var panel, quty, canvas, ctx, img, sx, sy, sw, sh, deg, theta, bb;
  29842. if (sizeImg && ! operate && (canvas = sizeImg.data('canvas'))) {
  29843. panel = control.children('div.elfinder-resize-control-panel:visible');
  29844. quty = panel.find('input.elfinder-resize-quality');
  29845. if (quty.is(':visible')) {
  29846. ctx = sizeImg.data('ctx');
  29847. img = sizeImg.get(0);
  29848. if (panel.hasClass('elfinder-resize-uiresize')) {
  29849. // resize
  29850. sw = canvas.width = width.val();
  29851. sh = canvas.height = height.val();
  29852. ctx.drawImage(img, 0, 0, sw, sh);
  29853. } else if (panel.hasClass('elfinder-resize-uicrop')) {
  29854. // crop
  29855. sx = pointX.val();
  29856. sy = pointY.val();
  29857. sw = offsetX.val();
  29858. sh = offsetY.val();
  29859. canvas.width = sw;
  29860. canvas.height = sh;
  29861. ctx.drawImage(img, sx, sy, sw, sh, 0, 0, sw, sh);
  29862. } else {
  29863. // rotate
  29864. deg = degree.val();
  29865. theta = (degree.val() * Math.PI) / 180;
  29866. bb = getBounceBox(owidth, oheight, theta);
  29867. sw = canvas.width = bb.width;
  29868. sh = canvas.height = bb.height;
  29869. ctx.save();
  29870. if (deg % 90 !== 0) {
  29871. ctx.fillStyle = bg.val() || '#FFF';
  29872. ctx.fillRect(0, 0, sw, sh);
  29873. }
  29874. ctx.translate(sw / 2, sh / 2);
  29875. ctx.rotate(theta);
  29876. ctx.drawImage(img, -img.width/2, -img.height/2, owidth, oheight);
  29877. ctx.restore();
  29878. }
  29879. canvas.toBlob(function(blob) {
  29880. if (blob) {
  29881. size1 = blob.size;
  29882. quty.next('span').text(' (' + fm.formatSize(blob.size) + ')');
  29883. }
  29884. }, 'image/jpeg', Math.max(Math.min(quty.val(), 100), 1) / 100);
  29885. }
  29886. }
  29887. });
  29888. })
  29889. .on('mouseup', 'input', function(e) {
  29890. $(e.target).trigger('change');
  29891. }),
  29892. preview = $('<div class="elfinder-resize-preview"></div>')
  29893. .on('touchmove', function(e) {
  29894. if ($(e.target).hasClass('touch-punch')) {
  29895. e.stopPropagation();
  29896. e.preventDefault();
  29897. }
  29898. }),
  29899. spinner = $('<div class="elfinder-resize-loading">'+fm.i18n('ntfloadimg')+'</div>'),
  29900. rhandle = $('<div class="elfinder-resize-handle touch-punch"></div>'),
  29901. rhandlec = $('<div class="elfinder-resize-handle touch-punch"></div>'),
  29902. uiresize = $('<div class="elfinder-resize-uiresize elfinder-resize-control-panel"></div>'),
  29903. uicrop = $('<div class="elfinder-resize-uicrop elfinder-resize-control-panel"></div>'),
  29904. uirotate = $('<div class="elfinder-resize-rotate elfinder-resize-control-panel"></div>'),
  29905. uideg270 = $('<button></button>').attr('title',fm.i18n('rotate-cw')).append($('<span class="elfinder-button-icon elfinder-button-icon-rotate-l"></span>')),
  29906. uideg90 = $('<button></button>').attr('title',fm.i18n('rotate-ccw')).append($('<span class="elfinder-button-icon elfinder-button-icon-rotate-r"></span>')),
  29907. uiprop = $('<span ></span>'),
  29908. reset = $('<button class="elfinder-resize-reset">').text(fm.i18n('reset'))
  29909. .on('click', function() {
  29910. resetView();
  29911. })
  29912. .button({
  29913. icons: {
  29914. primary: 'ui-icon-arrowrefresh-1-n'
  29915. },
  29916. text: false
  29917. }),
  29918. uitype = $('<div class="elfinder-resize-type"></div>')
  29919. .append('<input type="radio" name="type" id="'+id+'-resize" value="resize" checked="checked" /><label for="'+id+'-resize">'+fm.i18n('resize')+'</label>',
  29920. '<input class="api2" type="radio" name="type" id="'+id+'-crop" value="crop" /><label class="api2" for="'+id+'-crop">'+fm.i18n('crop')+'</label>',
  29921. '<input class="api2" type="radio" name="type" id="'+id+'-rotate" value="rotate" /><label class="api2" for="'+id+'-rotate">'+fm.i18n('rotate')+'</label>'),
  29922. mode = 'resize',
  29923. type = uitype[ctrgrup]()[ctrgrup]('disable').find('input')
  29924. .on('change', function() {
  29925. mode = $(this).val();
  29926. resetView();
  29927. resizable(true);
  29928. croppable(true);
  29929. rotateable(true);
  29930. if (mode == 'resize') {
  29931. uiresize.show();
  29932. uirotate.hide();
  29933. uicrop.hide();
  29934. resizable();
  29935. isJpeg && grid8px.insertAfter(uiresize.find('.elfinder-resize-grid8'));
  29936. }
  29937. else if (mode == 'crop') {
  29938. uirotate.hide();
  29939. uiresize.hide();
  29940. uicrop.show();
  29941. croppable();
  29942. isJpeg && grid8px.insertAfter(uicrop.find('.elfinder-resize-grid8'));
  29943. } else if (mode == 'rotate') {
  29944. uiresize.hide();
  29945. uicrop.hide();
  29946. uirotate.show();
  29947. rotateable();
  29948. }
  29949. }),
  29950. width = $(input)
  29951. .on('change', function() {
  29952. var w = round(parseInt(width.val())),
  29953. h = round(cratio ? w/ratio : parseInt(height.val()));
  29954. if (w > 0 && h > 0) {
  29955. resize.updateView(w, h);
  29956. width.val(w);
  29957. height.val(h);
  29958. }
  29959. }).addClass('elfinder-focus'),
  29960. height = $(input)
  29961. .on('change', function() {
  29962. var h = round(parseInt(height.val())),
  29963. w = round(cratio ? h*ratio : parseInt(width.val()));
  29964. if (w > 0 && h > 0) {
  29965. resize.updateView(w, h);
  29966. width.val(w);
  29967. height.val(h);
  29968. }
  29969. }),
  29970. pointX = $(input).on('change', function(){crop.updateView();}),
  29971. pointY = $(input).on('change', function(){crop.updateView();}),
  29972. offsetX = $(input).on('change', function(){crop.updateView('w');}),
  29973. offsetY = $(input).on('change', function(){crop.updateView('h');}),
  29974. quality = isJpeg && api2?
  29975. $(input).val(fm.storage('jpgQuality') > 0? fm.storage('jpgQuality') : fm.option('jpgQuality'))
  29976. .addClass('elfinder-resize-quality')
  29977. .attr('min', '1').attr('max', '100').attr('title', '1 - 100')
  29978. .on('blur', function(){
  29979. var q = Math.min(100, Math.max(1, parseInt(this.value)));
  29980. control.find('input.elfinder-resize-quality').val(q);
  29981. })
  29982. : null,
  29983. degree = $('<input type="number" class="ui-corner-all" maxlength="3" value="0" />')
  29984. .on('change', function() {
  29985. rotate.update();
  29986. }),
  29987. uidegslider = $('<div class="elfinder-resize-rotate-slider touch-punch"></div>')
  29988. .slider({
  29989. min: 0,
  29990. max: 360,
  29991. value: degree.val(),
  29992. animate: true,
  29993. start: opStart,
  29994. stop: opStop,
  29995. change: function(event, ui) {
  29996. if (ui.value != uidegslider.slider('value')) {
  29997. rotate.update(ui.value);
  29998. }
  29999. },
  30000. slide: function(event, ui) {
  30001. rotate.update(ui.value, false);
  30002. }
  30003. }).find('.ui-slider-handle')
  30004. .addClass('elfinder-tabstop')
  30005. .off('keydown')
  30006. .on('keydown', function(e) {
  30007. if (e.keyCode == $.ui.keyCode.LEFT || e.keyCode == $.ui.keyCode.RIGHT) {
  30008. e.stopPropagation();
  30009. e.preventDefault();
  30010. rotate.update(Number(degree.val()) + (e.keyCode == $.ui.keyCode.RIGHT? 1 : -1), false);
  30011. }
  30012. })
  30013. .end(),
  30014. pickimg,
  30015. pickcanv,
  30016. pickctx,
  30017. pickc = {},
  30018. pick = function(e) {
  30019. var color, r, g, b, h, s, l;
  30020. try {
  30021. color = pickc[Math.round(e.offsetX)][Math.round(e.offsetY)];
  30022. } catch(e) {}
  30023. if (!color) return;
  30024. r = color[0]; g = color[1]; b = color[2];
  30025. h = color[3]; s = color[4]; l = color[5];
  30026. setbg(r, g, b, (e.type === 'click'));
  30027. },
  30028. palpick = function(e) {
  30029. setbg($(this).css('backgroundColor'), '', '', (e.type === 'click'));
  30030. },
  30031. setbg = function(r, g, b, off) {
  30032. var s, m, cc;
  30033. if (typeof r === 'string') {
  30034. g = '';
  30035. if (r && (s = $('<span>').css('backgroundColor', r).css('backgroundColor')) && (m = s.match(/rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i))) {
  30036. r = Number(m[1]);
  30037. g = Number(m[2]);
  30038. b = Number(m[3]);
  30039. }
  30040. }
  30041. cc = (g === '')? r : '#' + getColorCode(r, g, b);
  30042. bg.val(cc).css({ backgroundColor: cc, backgroundImage: 'none', color: (r+g+b < 384? '#fff' : '#000') });
  30043. preview.css('backgroundColor', cc);
  30044. if (off) {
  30045. imgr.off('.picker').removeClass('elfinder-resize-picking');
  30046. pallet.off('.picker').removeClass('elfinder-resize-picking');
  30047. }
  30048. },
  30049. getColorCode = function(r, g, b) {
  30050. return $.map([r,g,b], function(c){return ('0'+parseInt(c).toString(16)).slice(-2);}).join('');
  30051. },
  30052. picker = $('<button>').text(fm.i18n('colorPicker'))
  30053. .on('click', function() {
  30054. imgr.on('mousemove.picker click.picker', pick).addClass('elfinder-resize-picking');
  30055. pallet.on('mousemove.picker click.picker', 'span', palpick).addClass('elfinder-resize-picking');
  30056. })
  30057. .button({
  30058. icons: {
  30059. primary: 'ui-icon-pin-s'
  30060. },
  30061. text: false
  30062. }),
  30063. reseter = $('<button>').text(fm.i18n('reset'))
  30064. .on('click', function() {
  30065. setbg('', '', '', true);
  30066. })
  30067. .button({
  30068. icons: {
  30069. primary: 'ui-icon-arrowrefresh-1-n'
  30070. },
  30071. text: false
  30072. }),
  30073. bg = $('<input class="ui-corner-all elfinder-resize-bg" type="text">')
  30074. .on('focus', function() {
  30075. $(this).attr('style', '');
  30076. })
  30077. .on('blur', function() {
  30078. setbg($(this).val());
  30079. }),
  30080. pallet = $('<div class="elfinder-resize-pallet">').on('click', 'span', function() {
  30081. setbg($(this).css('backgroundColor'));
  30082. }),
  30083. ratio = 1,
  30084. prop = 1,
  30085. owidth = 0,
  30086. oheight = 0,
  30087. cratio = true,
  30088. cratioc = false,
  30089. pwidth = 0,
  30090. pheight = 0,
  30091. rwidth = 0,
  30092. rheight = 0,
  30093. rdegree = 0,
  30094. grid8 = isJpeg? grid8Def : false,
  30095. constr = $('<button>').html(fm.i18n('aspectRatio'))
  30096. .on('click', function() {
  30097. cratio = ! cratio;
  30098. constr.button('option', {
  30099. icons : { primary: cratio? 'ui-icon-locked' : 'ui-icon-unlocked'}
  30100. });
  30101. resize.fixHeight();
  30102. rhandle.resizable('option', 'aspectRatio', cratio).data('uiResizable')._aspectRatio = cratio;
  30103. })
  30104. .button({
  30105. icons : {
  30106. primary: cratio? 'ui-icon-locked' : 'ui-icon-unlocked'
  30107. },
  30108. text: false
  30109. }),
  30110. constrc = $('<button>').html(fm.i18n('aspectRatio'))
  30111. .on('click', function() {
  30112. cratioc = ! cratioc;
  30113. constrc.button('option', {
  30114. icons : { primary: cratioc? 'ui-icon-locked' : 'ui-icon-unlocked'}
  30115. });
  30116. rhandlec.resizable('option', 'aspectRatio', cratioc).data('uiResizable')._aspectRatio = cratioc;
  30117. })
  30118. .button({
  30119. icons : {
  30120. primary: cratioc? 'ui-icon-locked' : 'ui-icon-unlocked'
  30121. },
  30122. text: false
  30123. }),
  30124. grid8px = $('<button>').html(fm.i18n(grid8? 'enabled' : 'disabled')).toggleClass('ui-state-active', grid8)
  30125. .on('click', function() {
  30126. grid8 = ! grid8;
  30127. grid8px.html(fm.i18n(grid8? 'enabled' : 'disabled')).toggleClass('ui-state-active', grid8);
  30128. setStep8();
  30129. })
  30130. .button(),
  30131. setStep8 = function() {
  30132. var step = grid8? 8 : 1;
  30133. $.each([width, height, offsetX, offsetY, pointX, pointY], function() {
  30134. this.attr('step', step);
  30135. });
  30136. if (grid8) {
  30137. width.val(round(width.val()));
  30138. height.val(round(height.val()));
  30139. offsetX.val(round(offsetX.val()));
  30140. offsetY.val(round(offsetY.val()));
  30141. pointX.val(round(pointX.val()));
  30142. pointY.val(round(pointY.val()));
  30143. if (uiresize.is(':visible')) {
  30144. resize.updateView(width.val(), height.val());
  30145. } else if (uicrop.is(':visible')) {
  30146. crop.updateView();
  30147. }
  30148. }
  30149. },
  30150. setuprimg = function() {
  30151. var r_scale,
  30152. fail = function() {
  30153. bg.parent().hide();
  30154. pallet.hide();
  30155. };
  30156. r_scale = Math.min(pwidth, pheight) / Math.sqrt(Math.pow(owidth, 2) + Math.pow(oheight, 2));
  30157. rwidth = Math.ceil(owidth * r_scale);
  30158. rheight = Math.ceil(oheight * r_scale);
  30159. imgr.width(rwidth)
  30160. .height(rheight)
  30161. .css('margin-top', (pheight-rheight)/2 + 'px')
  30162. .css('margin-left', (pwidth-rwidth)/2 + 'px');
  30163. if (imgr.is(':visible') && bg.is(':visible')) {
  30164. if (file.mime !== 'image/png') {
  30165. preview.css('backgroundColor', bg.val());
  30166. pickimg = $('<img>');
  30167. if (fm.isCORS) {
  30168. pickimg.attr('crossorigin', 'use-credentials');
  30169. }
  30170. pickimg.on('load', function() {
  30171. if (pickcanv && pickcanv.width !== rwidth) {
  30172. setColorData();
  30173. }
  30174. })
  30175. .on('error', fail)
  30176. .attr('src', canvSrc);
  30177. } else {
  30178. fail();
  30179. }
  30180. }
  30181. },
  30182. setupimg = function() {
  30183. resize.updateView(owidth, oheight);
  30184. setuprimg();
  30185. basec
  30186. .width(img.width())
  30187. .height(img.height());
  30188. imgc
  30189. .width(img.width())
  30190. .height(img.height());
  30191. crop.updateView();
  30192. jpgCalc();
  30193. },
  30194. setColorData = function() {
  30195. if (pickctx) {
  30196. var n, w, h, r, g, b, a, s, l, hsl, hue,
  30197. data, scale, tx1, tx2, ty1, ty2, rgb,
  30198. domi = {},
  30199. domic = [],
  30200. domiv, palc,
  30201. rgbToHsl = function (r, g, b) {
  30202. var h, s, l,
  30203. max = Math.max(Math.max(r, g), b),
  30204. min = Math.min(Math.min(r, g), b);
  30205. // Hue, 0 ~ 359
  30206. if (max === min) {
  30207. h = 0;
  30208. } else if (r === max) {
  30209. h = ((g - b) / (max - min) * 60 + 360) % 360;
  30210. } else if (g === max) {
  30211. h = (b - r) / (max - min) * 60 + 120;
  30212. } else if (b === max) {
  30213. h = (r - g) / (max - min) * 60 + 240;
  30214. }
  30215. // Saturation, 0 ~ 1
  30216. s = (max - min) / max;
  30217. // Lightness, 0 ~ 1
  30218. l = (r * 0.3 + g * 0.59 + b * 0.11) / 255;
  30219. return [h, s, l, 'hsl'];
  30220. },
  30221. rgbRound = function(c) {
  30222. return Math.round(c / 8) * 8;
  30223. };
  30224. calc:
  30225. try {
  30226. w = pickcanv.width = imgr.width();
  30227. h = pickcanv.height = imgr.height();
  30228. scale = w / owidth;
  30229. pickctx.scale(scale, scale);
  30230. pickctx.drawImage(pickimg.get(0), 0, 0);
  30231. data = pickctx.getImageData(0, 0, w, h).data;
  30232. // Range to detect the dominant color
  30233. tx1 = w * 0.1;
  30234. tx2 = w * 0.9;
  30235. ty1 = h * 0.1;
  30236. ty2 = h * 0.9;
  30237. for (var y = 0; y < h - 1; y++) {
  30238. for (var x = 0; x < w - 1; x++) {
  30239. n = x * 4 + y * w * 4;
  30240. // RGB
  30241. r = data[n]; g = data[n + 1]; b = data[n + 2]; a = data[n + 3];
  30242. // check alpha ch
  30243. if (a !== 255) {
  30244. bg.parent().hide();
  30245. pallet.hide();
  30246. break calc;
  30247. }
  30248. // HSL
  30249. hsl = rgbToHsl(r, g, b);
  30250. hue = Math.round(hsl[0]); s = Math.round(hsl[1] * 100); l = Math.round(hsl[2] * 100);
  30251. if (! pickc[x]) {
  30252. pickc[x] = {};
  30253. }
  30254. // set pickc
  30255. pickc[x][y] = [r, g, b, hue, s, l];
  30256. // detect the dominant color
  30257. if ((x < tx1 || x > tx2) && (y < ty1 || y > ty2)) {
  30258. rgb = rgbRound(r) + ',' + rgbRound(g) + ',' + rgbRound(b);
  30259. if (! domi[rgb]) {
  30260. domi[rgb] = 1;
  30261. } else {
  30262. ++domi[rgb];
  30263. }
  30264. }
  30265. }
  30266. }
  30267. if (! pallet.children(':first').length) {
  30268. palc = 1;
  30269. $.each(domi, function(c, v) {
  30270. domic.push({c: c, v: v});
  30271. });
  30272. $.each(domic.sort(function(a, b) {
  30273. return (a.v > b.v)? -1 : 1;
  30274. }), function() {
  30275. if (this.v < 2 || palc > 10) {
  30276. return false;
  30277. }
  30278. pallet.append($('<span style="width:20px;height:20px;display:inline-block;background-color:rgb('+this.c+');">'));
  30279. ++palc;
  30280. });
  30281. }
  30282. } catch(e) {
  30283. picker.hide();
  30284. pallet.hide();
  30285. }
  30286. }
  30287. },
  30288. setupPicker = function() {
  30289. try {
  30290. pickcanv = document.createElement('canvas');
  30291. pickctx = pickcanv.getContext('2d');
  30292. } catch(e) {
  30293. picker.hide();
  30294. pallet.hide();
  30295. }
  30296. },
  30297. setupPreset = function() {
  30298. preset.on('click', 'span.elfinder-resize-preset', function() {
  30299. var btn = $(this),
  30300. w = btn.data('s')[0],
  30301. h = btn.data('s')[1],
  30302. r = owidth / oheight;
  30303. btn.data('s', [h, w]).text(h + 'x' + w);
  30304. if (owidth > w || oheight > h) {
  30305. if (owidth <= w) {
  30306. w = round(h * r);
  30307. } else if (oheight <= h) {
  30308. h = round(w / r);
  30309. } else {
  30310. if (owidth - w > oheight - h) {
  30311. h = round(w / r);
  30312. } else {
  30313. w = round(h * r);
  30314. }
  30315. }
  30316. } else {
  30317. w = owidth;
  30318. h = oheight;
  30319. }
  30320. width.val(w);
  30321. height.val(h);
  30322. resize.updateView(w, h);
  30323. jpgCalc();
  30324. });
  30325. presetc.on('click', 'span.elfinder-resize-preset', function() {
  30326. var btn = $(this),
  30327. w = btn.data('s')[0],
  30328. h = btn.data('s')[1],
  30329. x = pointX.val(),
  30330. y = pointY.val();
  30331. btn.data('s', [h, w]).text(h + 'x' + w);
  30332. if (owidth >= w && oheight >= h) {
  30333. if (owidth - w - x < 0) {
  30334. x = owidth - w;
  30335. }
  30336. if (oheight - h - y < 0) {
  30337. y = oheight - h;
  30338. }
  30339. pointX.val(x);
  30340. pointY.val(y);
  30341. offsetX.val(w);
  30342. offsetY.val(h);
  30343. crop.updateView();
  30344. jpgCalc();
  30345. }
  30346. });
  30347. presetc.children('span.elfinder-resize-preset').each(function() {
  30348. var btn = $(this),
  30349. w = btn.data('s')[0],
  30350. h = btn.data('s')[1];
  30351. btn[(owidth >= w && oheight >= h)? 'show' : 'hide']();
  30352. });
  30353. },
  30354. dimreq = null,
  30355. inited = false,
  30356. setdim = function(dim) {
  30357. var rfile = fm.file(file.hash);
  30358. rfile.width = dim[0];
  30359. rfile.height = dim[1];
  30360. },
  30361. init = function() {
  30362. var elm, memSize, r_scale, imgRatio;
  30363. if (inited) {
  30364. return;
  30365. }
  30366. inited = true;
  30367. dimreq && dimreq.state && dimreq.state() === 'pending' && dimreq.reject();
  30368. // check lossless rotete
  30369. if (fm.api >= 2.1030) {
  30370. if (losslessRotate === 0) {
  30371. fm.request({
  30372. data: {
  30373. cmd : 'resize',
  30374. target : file.hash,
  30375. degree : 0,
  30376. mode : 'rotate'
  30377. },
  30378. preventDefault : true
  30379. }).done(function(data) {
  30380. losslessRotate = data.losslessRotate? 1 : -1;
  30381. if (losslessRotate === 1 && (degree.val() % 90 === 0)) {
  30382. uirotate.children('div.elfinder-resize-quality').hide();
  30383. }
  30384. }).fail(function() {
  30385. losslessRotate = -1;
  30386. });
  30387. }
  30388. } else {
  30389. losslessRotate = -1;
  30390. }
  30391. elm = img.get(0);
  30392. memSize = file.width && file.height? {w: file.width, h: file.height} : (elm.naturalWidth? null : {w: img.width(), h: img.height()});
  30393. memSize && img.removeAttr('width').removeAttr('height');
  30394. owidth = file.width || elm.naturalWidth || elm.width || img.width();
  30395. oheight = file.height || elm.naturalHeight || elm.height || img.height();
  30396. if (!file.width || !file.height) {
  30397. setdim([owidth, oheight]);
  30398. }
  30399. memSize && img.width(memSize.w).height(memSize.h);
  30400. dMinBtn.show();
  30401. imgRatio = oheight / owidth;
  30402. if (imgRatio < 1 && preview.height() > preview.width() * imgRatio) {
  30403. preview.height(preview.width() * imgRatio);
  30404. }
  30405. if (preview.height() > img.height() + 20) {
  30406. preview.height(img.height() + 20);
  30407. }
  30408. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  30409. spinner.remove();
  30410. ratio = owidth/oheight;
  30411. rhandle.append(img.show()).show();
  30412. width.val(owidth);
  30413. height.val(oheight);
  30414. setupPicker();
  30415. setupPreset();
  30416. setupimg();
  30417. uitype[ctrgrup]('enable');
  30418. control.find('input,select').prop('disabled', false)
  30419. .filter(':text').on('keydown', function(e) {
  30420. var cOpts;
  30421. if (e.keyCode == $.ui.keyCode.ENTER) {
  30422. e.stopPropagation();
  30423. e.preventDefault();
  30424. cOpts = {
  30425. title : $('input:checked', uitype).val(),
  30426. text : 'confirmReq',
  30427. accept : {
  30428. label : 'btnApply',
  30429. callback : function() {
  30430. save();
  30431. }
  30432. },
  30433. cancel : {
  30434. label : 'btnCancel',
  30435. callback : function(){
  30436. $(this).trigger('focus');
  30437. }
  30438. }
  30439. };
  30440. if (useSaveAs) {
  30441. cOpts['buttons'] = [{
  30442. label : 'btnSaveAs',
  30443. callback : function() {
  30444. requestAnimationFrame(saveAs);
  30445. }
  30446. }];
  30447. }
  30448. fm.confirm(cOpts);
  30449. return;
  30450. }
  30451. })
  30452. .on('keyup', function() {
  30453. var $this = $(this);
  30454. if (! $this.hasClass('elfinder-resize-bg')) {
  30455. requestAnimationFrame(function() {
  30456. $this.val($this.val().replace(/[^0-9]/g, ''));
  30457. });
  30458. }
  30459. })
  30460. .filter(':first');
  30461. setStep8();
  30462. !fm.UA.Mobile && width.trigger('focus');
  30463. resizable();
  30464. },
  30465. img = $('<img/>')
  30466. .on('load', init)
  30467. .on('error', function() {
  30468. spinner.html(fm.i18n('ntfsmth')).css('background', 'transparent');
  30469. }),
  30470. basec = $('<div></div>'),
  30471. imgc = $('<img/>'),
  30472. coverc = $('<div></div>'),
  30473. imgr = $('<img class="elfinder-resize-imgrotate" />'),
  30474. round = function(v, max) {
  30475. v = grid8? Math.round(v/8)*8 : Math.round(v);
  30476. v = Math.max(0, v);
  30477. if (max && v > max) {
  30478. v = grid8? Math.floor(max/8)*8 : max;
  30479. }
  30480. return v;
  30481. },
  30482. resetView = function() {
  30483. width.val(owidth);
  30484. height.val(oheight);
  30485. resize.updateView(owidth, oheight);
  30486. pointX.val(0);
  30487. pointY.val(0);
  30488. offsetX.val(owidth);
  30489. offsetY.val(oheight);
  30490. crop.updateView();
  30491. jpgCalc();
  30492. },
  30493. resize = {
  30494. update : function() {
  30495. width.val(round(img.width()/prop));
  30496. height.val(round(img.height()/prop));
  30497. jpgCalc();
  30498. },
  30499. updateView : function(w, h) {
  30500. if (w > pwidth || h > pheight) {
  30501. if (w / pwidth > h / pheight) {
  30502. prop = pwidth / w;
  30503. img.width(pwidth).height(round(h*prop));
  30504. } else {
  30505. prop = pheight / h;
  30506. img.height(pheight).width(round(w*prop));
  30507. }
  30508. } else {
  30509. img.width(round(w)).height(round(h));
  30510. }
  30511. prop = img.width()/w;
  30512. uiprop.text('1 : '+(1/prop).toFixed(2));
  30513. resize.updateHandle();
  30514. },
  30515. updateHandle : function() {
  30516. rhandle.width(img.width()).height(img.height());
  30517. },
  30518. fixHeight : function() {
  30519. var w, h;
  30520. if (cratio) {
  30521. w = width.val();
  30522. h = round(w/ratio);
  30523. resize.updateView(w, h);
  30524. height.val(h);
  30525. }
  30526. }
  30527. },
  30528. crop = {
  30529. update : function(change) {
  30530. pointX.val(round(((rhandlec.data('x')||rhandlec.position().left))/prop, owidth));
  30531. pointY.val(round(((rhandlec.data('y')||rhandlec.position().top))/prop, oheight));
  30532. if (change !== 'xy') {
  30533. offsetX.val(round((rhandlec.data('w')||rhandlec.width())/prop, owidth - pointX.val()));
  30534. offsetY.val(round((rhandlec.data('h')||rhandlec.height())/prop, oheight - pointY.val()));
  30535. }
  30536. jpgCalc();
  30537. },
  30538. updateView : function(change) {
  30539. var r, x, y, w, h;
  30540. pointX.val(round(pointX.val(), owidth - (grid8? 8 : 1)));
  30541. pointY.val(round(pointY.val(), oheight - (grid8? 8 : 1)));
  30542. offsetX.val(round(offsetX.val(), owidth - pointX.val()));
  30543. offsetY.val(round(offsetY.val(), oheight - pointY.val()));
  30544. if (cratioc) {
  30545. r = coverc.width() / coverc.height();
  30546. if (change === 'w') {
  30547. offsetY.val(round(parseInt(offsetX.val()) / r));
  30548. } else if (change === 'h') {
  30549. offsetX.val(round(parseInt(offsetY.val()) * r));
  30550. }
  30551. }
  30552. x = Math.round(parseInt(pointX.val()) * prop);
  30553. y = Math.round(parseInt(pointY.val()) * prop);
  30554. if (change !== 'xy') {
  30555. w = Math.round(parseInt(offsetX.val()) * prop);
  30556. h = Math.round(parseInt(offsetY.val()) * prop);
  30557. } else {
  30558. w = rhandlec.data('w');
  30559. h = rhandlec.data('h');
  30560. }
  30561. rhandlec.data({x: x, y: y, w: w, h: h})
  30562. .width(w)
  30563. .height(h)
  30564. .css({left: x, top: y});
  30565. coverc.width(w)
  30566. .height(h);
  30567. },
  30568. resize_update : function(e, ui) {
  30569. rhandlec.data({x: ui.position.left, y: ui.position.top, w: ui.size.width, h: ui.size.height});
  30570. crop.update();
  30571. crop.updateView();
  30572. },
  30573. drag_update : function(e, ui) {
  30574. rhandlec.data({x: ui.position.left, y: ui.position.top});
  30575. crop.update('xy');
  30576. }
  30577. },
  30578. rotate = {
  30579. mouseStartAngle : 0,
  30580. imageStartAngle : 0,
  30581. imageBeingRotated : false,
  30582. setQuality : function() {
  30583. uirotate.children('div.elfinder-resize-quality')[(losslessRotate > 0 && (degree.val() % 90) === 0)? 'hide' : 'show']();
  30584. },
  30585. update : function(value, animate) {
  30586. if (typeof value == 'undefined') {
  30587. rdegree = value = parseInt(degree.val());
  30588. }
  30589. if (typeof animate == 'undefined') {
  30590. animate = true;
  30591. }
  30592. if (! animate || fm.UA.Opera || fm.UA.ltIE8) {
  30593. imgr.rotate(value);
  30594. } else {
  30595. imgr.animate({rotate: value + 'deg'});
  30596. }
  30597. value = value % 360;
  30598. if (value < 0) {
  30599. value += 360;
  30600. }
  30601. degree.val(parseInt(value));
  30602. uidegslider.slider('value', degree.val());
  30603. rotate.setQuality();
  30604. },
  30605. execute : function ( e ) {
  30606. if ( !rotate.imageBeingRotated ) return;
  30607. var imageCentre = rotate.getCenter( imgr );
  30608. var ev = e.originalEvent.touches? e.originalEvent.touches[0] : e;
  30609. var mouseXFromCentre = ev.pageX - imageCentre[0];
  30610. var mouseYFromCentre = ev.pageY - imageCentre[1];
  30611. var mouseAngle = Math.atan2( mouseYFromCentre, mouseXFromCentre );
  30612. var rotateAngle = mouseAngle - rotate.mouseStartAngle + rotate.imageStartAngle;
  30613. rotateAngle = Math.round(parseFloat(rotateAngle) * 180 / Math.PI);
  30614. if ( e.shiftKey ) {
  30615. rotateAngle = Math.round((rotateAngle + 6)/15) * 15;
  30616. }
  30617. imgr.rotate(rotateAngle);
  30618. rotateAngle = rotateAngle % 360;
  30619. if (rotateAngle < 0) {
  30620. rotateAngle += 360;
  30621. }
  30622. degree.val(rotateAngle);
  30623. uidegslider.slider('value', degree.val());
  30624. rotate.setQuality();
  30625. return false;
  30626. },
  30627. start : function ( e ) {
  30628. if (imgr.hasClass('elfinder-resize-picking')) {
  30629. return;
  30630. }
  30631. opStart();
  30632. rotate.imageBeingRotated = true;
  30633. var imageCentre = rotate.getCenter( imgr );
  30634. var ev = e.originalEvent.touches? e.originalEvent.touches[0] : e;
  30635. var mouseStartXFromCentre = ev.pageX - imageCentre[0];
  30636. var mouseStartYFromCentre = ev.pageY - imageCentre[1];
  30637. rotate.mouseStartAngle = Math.atan2( mouseStartYFromCentre, mouseStartXFromCentre );
  30638. rotate.imageStartAngle = parseFloat(imgr.rotate()) * Math.PI / 180.0;
  30639. $(document).on('mousemove', rotate.execute);
  30640. imgr.on('touchmove', rotate.execute);
  30641. return false;
  30642. },
  30643. stop : function ( e ) {
  30644. if ( !rotate.imageBeingRotated ) return;
  30645. $(document).off('mousemove', rotate.execute);
  30646. imgr.off('touchmove', rotate.execute);
  30647. requestAnimationFrame(function() { rotate.imageBeingRotated = false; });
  30648. opStop();
  30649. return false;
  30650. },
  30651. getCenter : function ( image ) {
  30652. var currentRotation = imgr.rotate();
  30653. imgr.rotate(0);
  30654. var imageOffset = imgr.offset();
  30655. var imageCentreX = imageOffset.left + imgr.width() / 2;
  30656. var imageCentreY = imageOffset.top + imgr.height() / 2;
  30657. imgr.rotate(currentRotation);
  30658. return Array( imageCentreX, imageCentreY );
  30659. }
  30660. },
  30661. resizable = function(destroy) {
  30662. if (destroy) {
  30663. rhandle.filter(':ui-resizable').resizable('destroy');
  30664. rhandle.hide();
  30665. }
  30666. else {
  30667. rhandle.show();
  30668. rhandle.resizable({
  30669. alsoResize : img,
  30670. aspectRatio : cratio,
  30671. resize : resize.update,
  30672. start : opStart,
  30673. stop : function(e) {
  30674. resize.fixHeight;
  30675. resize.updateView(width.val(), height.val());
  30676. opStop();
  30677. }
  30678. });
  30679. dinit();
  30680. }
  30681. },
  30682. croppable = function(destroy) {
  30683. if (destroy) {
  30684. rhandlec.filter(':ui-resizable').resizable('destroy')
  30685. .filter(':ui-draggable').draggable('destroy');
  30686. basec.hide();
  30687. }
  30688. else {
  30689. basec.show();
  30690. rhandlec
  30691. .resizable({
  30692. containment : basec,
  30693. aspectRatio : cratioc,
  30694. resize : crop.resize_update,
  30695. start : opStart,
  30696. stop : opStop,
  30697. handles : 'all'
  30698. })
  30699. .draggable({
  30700. handle : coverc,
  30701. containment : imgc,
  30702. drag : crop.drag_update,
  30703. start : opStart,
  30704. stop : function() {
  30705. crop.updateView('xy');
  30706. opStop();
  30707. }
  30708. });
  30709. dinit();
  30710. crop.update();
  30711. }
  30712. },
  30713. rotateable = function(destroy) {
  30714. if (destroy) {
  30715. imgr.hide();
  30716. }
  30717. else {
  30718. imgr.show();
  30719. dinit();
  30720. }
  30721. },
  30722. checkVals = function() {
  30723. var w, h, x, y, d, q, b = '';
  30724. if (mode == 'resize') {
  30725. w = parseInt(width.val()) || 0;
  30726. h = parseInt(height.val()) || 0;
  30727. } else if (mode == 'crop') {
  30728. w = parseInt(offsetX.val()) || 0;
  30729. h = parseInt(offsetY.val()) || 0;
  30730. x = parseInt(pointX.val()) || 0;
  30731. y = parseInt(pointY.val()) || 0;
  30732. } else if (mode == 'rotate') {
  30733. w = owidth;
  30734. h = oheight;
  30735. d = parseInt(degree.val()) || 0;
  30736. if (d < 0 || d > 360) {
  30737. fm.error('Invalid rotate degree');
  30738. return false;
  30739. }
  30740. if (d == 0 || d == 360) {
  30741. fm.error('errResizeNoChange');
  30742. return false;
  30743. }
  30744. b = bg.val();
  30745. }
  30746. q = quality? parseInt(quality.val()) : 0;
  30747. if (mode != 'rotate') {
  30748. if (w <= 0 || h <= 0) {
  30749. fm.error('Invalid image size');
  30750. return false;
  30751. }
  30752. if (w == owidth && h == oheight && parseInt(size0 / 1000) === parseInt(size1/1000)) {
  30753. fm.error('errResizeNoChange');
  30754. return false;
  30755. }
  30756. }
  30757. return {w: w, h: h, x: x, y: y, d: d, q: q, b: b};
  30758. },
  30759. save = function() {
  30760. var vals;
  30761. if (vals = checkVals()) {
  30762. dialog.elfinderdialog('close');
  30763. self.resizeRequest({
  30764. target : file.hash,
  30765. width : vals.w,
  30766. height : vals.h,
  30767. x : vals.x,
  30768. y : vals.y,
  30769. degree : vals.d,
  30770. quality: vals.q,
  30771. bg : vals.b,
  30772. mode : mode
  30773. }, file, dfrd);
  30774. }
  30775. },
  30776. saveAs = function() {
  30777. var fail = function() {
  30778. dialogs.addClass(clsediting).fadeIn(function() {
  30779. base.addClass(clactive);
  30780. });
  30781. fm.disable();
  30782. },
  30783. make = function() {
  30784. self.mime = file.mime;
  30785. self.prefix = file.name.replace(/ \d+(\.[^.]+)?$/, '$1');
  30786. self.requestCmd = 'mkfile';
  30787. self.nextAction = {};
  30788. self.data = {target : file.phash};
  30789. $.proxy(fm.res('mixin', 'make'), self)()
  30790. .done(function(data) {
  30791. var hash, dfd;
  30792. if (data.added && data.added.length) {
  30793. hash = data.added[0].hash;
  30794. dfd = fm.api < 2.1032? fm.url(file.hash, { async: true, temporary: true }) : null;
  30795. $.when(dfd).done(function(url) {
  30796. fm.request({
  30797. options : {type : 'post'},
  30798. data : {
  30799. cmd : 'put',
  30800. target : hash,
  30801. encoding: dfd? 'scheme' : 'hash',
  30802. content : dfd? fm.convAbsUrl(url) : file.hash
  30803. },
  30804. notify : {type : 'copy', cnt : 1},
  30805. syncOnFail : true
  30806. })
  30807. .fail(fail)
  30808. .done(function(data) {
  30809. data = fm.normalize(data);
  30810. fm.updateCache(data);
  30811. file = fm.file(hash);
  30812. data.changed && data.changed.length && fm.change(data);
  30813. base.show().find('.elfinder-dialog-title').html(fm.escape(file.name));
  30814. save();
  30815. dialogs.fadeIn();
  30816. });
  30817. }).fail(fail);
  30818. } else {
  30819. fail();
  30820. }
  30821. })
  30822. .fail(fail)
  30823. .always(function() {
  30824. delete self.mime;
  30825. delete self.prefix;
  30826. delete self.nextAction;
  30827. delete self.data;
  30828. });
  30829. fm.trigger('unselectfiles', { files: [ file.hash ] });
  30830. },
  30831. reqOpen = null,
  30832. dialogs;
  30833. if (checkVals()) {
  30834. dialogs = fmnode.children('.' + self.dialogClass + ':visible').removeClass(clsediting).fadeOut();
  30835. base.removeClass(clactive);
  30836. fm.enable();
  30837. if (fm.searchStatus.state < 2 && file.phash !== fm.cwd().hash) {
  30838. reqOpen = fm.exec('open', [file.phash], {thash: file.phash});
  30839. }
  30840. $.when([reqOpen]).done(function() {
  30841. reqOpen? fm.one('cwdrender', make) : make();
  30842. }).fail(fail);
  30843. }
  30844. },
  30845. buttons = {},
  30846. hline = 'elfinder-resize-handle-hline',
  30847. vline = 'elfinder-resize-handle-vline',
  30848. rpoint = 'elfinder-resize-handle-point',
  30849. canvSrc = src,
  30850. sizeImg = quality? $('<img>').attr('crossorigin', fm.isCORS? 'use-credentials' : '').attr('src', canvSrc).on('load', function() {
  30851. try {
  30852. var canv = document.createElement('canvas');
  30853. sizeImg.data('canvas', canv).data('ctx', canv.getContext('2d'));
  30854. jpgCalc();
  30855. } catch(e) {
  30856. sizeImg.removeData('canvas').removeData('ctx');
  30857. }
  30858. }) : null,
  30859. jpgCalc = function() {
  30860. control.find('input.elfinder-resize-quality:visible').trigger('change');
  30861. },
  30862. dinit = function(e) {
  30863. if (base.hasClass('elfinder-dialog-minimized') || base.is(':hidden')) {
  30864. return;
  30865. }
  30866. preset.hide();
  30867. presetc.hide();
  30868. var win = fm.options.dialogContained? fmnode : $(window),
  30869. winH = win.height(),
  30870. winW = win.width(),
  30871. presW = 'auto',
  30872. presIn = true,
  30873. dw, ctrW, prvW;
  30874. base.width(Math.min(dialogWidth, winW - 30));
  30875. preview.attr('style', '');
  30876. if (owidth && oheight) {
  30877. pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width());
  30878. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  30879. resize.updateView(owidth, oheight);
  30880. }
  30881. ctrW = dialog.find('div.elfinder-resize-control').width();
  30882. prvW = preview.width();
  30883. dw = dialog.width() - 20;
  30884. if (prvW > dw) {
  30885. preview.width(dw);
  30886. presIn = false;
  30887. } else if ((dw - prvW) < ctrW) {
  30888. if (winW > winH) {
  30889. preview.width(dw - ctrW - 20);
  30890. } else {
  30891. preview.css({ float: 'none', marginLeft: 'auto', marginRight: 'auto'});
  30892. presIn = false;
  30893. }
  30894. }
  30895. if (presIn) {
  30896. presW = ctrW;
  30897. }
  30898. pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width());
  30899. if (fmnode.hasClass('elfinder-fullscreen')) {
  30900. if (base.height() > winH) {
  30901. winH -= 2;
  30902. preview.height(winH - base.height() + preview.height());
  30903. base.css('top', 0 - fmnode.offset().top);
  30904. }
  30905. } else {
  30906. winH -= 30;
  30907. (preview.height() > winH) && preview.height(winH);
  30908. }
  30909. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  30910. if (owidth && oheight) {
  30911. setupimg();
  30912. }
  30913. if (img.height() && preview.height() > img.height() + 20) {
  30914. preview.height(img.height() + 20);
  30915. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  30916. setuprimg();
  30917. }
  30918. preset.css('width', presW).show();
  30919. presetc.css('width', presW).show();
  30920. if (!presetc.children('span.elfinder-resize-preset:visible').length) {
  30921. presetc.hide();
  30922. }
  30923. dialog.elfinderdialog('posInit');
  30924. },
  30925. preset = (function() {
  30926. var sets = $('<fieldset class="elfinder-resize-preset-container">').append($('<legend>').html(fm.i18n('presets'))).css('box-sizing', 'border-box').hide(),
  30927. hasC;
  30928. $.each(presetSize, function(i, s) {
  30929. if (s.length === 2) {
  30930. hasC = true;
  30931. sets.append($('<span class="elfinder-resize-preset"></span>')
  30932. .data('s', s)
  30933. .text(s[0]+'x'+s[1])
  30934. .button()
  30935. );
  30936. }
  30937. });
  30938. if (!hasC) {
  30939. return $();
  30940. } else {
  30941. return sets;
  30942. }
  30943. })(),
  30944. presetc = preset.clone(true),
  30945. useSaveAs = fm.uploadMimeCheck(file.mime, file.phash),
  30946. dMinBtn, base;
  30947. size0 = size1 = file.size;
  30948. uiresize.append(
  30949. $(row).append($(label).text(fm.i18n('width')), width),
  30950. $(row).append($(label).text(fm.i18n('height')), height, $('<div class="elfinder-resize-whctrls">').append(constr, reset)),
  30951. (quality? $(row).append($(label).text(fm.i18n('quality')), quality, $('<span></span>')) : $()),
  30952. (isJpeg? $(row).append($(label).text(fm.i18n('8pxgrid')).addClass('elfinder-resize-grid8'), grid8px) : $()),
  30953. $(row).append($(label).text(fm.i18n('scale')), uiprop),
  30954. $(row).append(preset)
  30955. );
  30956. if (api2) {
  30957. uicrop.append(
  30958. $(row).append($(label).text('X'), pointX),
  30959. $(row).append($(label).text('Y')).append(pointY),
  30960. $(row).append($(label).text(fm.i18n('width')), offsetX),
  30961. $(row).append($(label).text(fm.i18n('height')), offsetY, $('<div class="elfinder-resize-whctrls">').append(constrc, reset.clone(true))),
  30962. (quality? $(row).append($(label).text(fm.i18n('quality')), quality.clone(true), $('<span></span>')) : $()),
  30963. (isJpeg? $(row).append($(label).text(fm.i18n('8pxgrid')).addClass('elfinder-resize-grid8')) : $()),
  30964. $(row).append(presetc)
  30965. );
  30966. uirotate.append(
  30967. $(row).addClass('elfinder-resize-degree').append(
  30968. $(label).text(fm.i18n('rotate')),
  30969. degree,
  30970. $('<span></span>').text(fm.i18n('degree')),
  30971. $('<div></div>').append(uideg270, uideg90)[ctrgrup]()
  30972. ),
  30973. $(row).css('height', '20px').append(uidegslider),
  30974. ((quality)? $(row)[losslessRotate < 1? 'show' : 'hide']().addClass('elfinder-resize-quality').append(
  30975. $(label).text(fm.i18n('quality')),
  30976. quality.clone(true),
  30977. $('<span></span>')) : $()
  30978. ),
  30979. $(row).append($(label).text(fm.i18n('bgcolor')), bg, picker, reseter),
  30980. $(row).css('height', '20px').append(pallet)
  30981. );
  30982. uideg270.on('click', function() {
  30983. rdegree = rdegree - 90;
  30984. rotate.update(rdegree);
  30985. });
  30986. uideg90.on('click', function(){
  30987. rdegree = rdegree + 90;
  30988. rotate.update(rdegree);
  30989. });
  30990. }
  30991. dialog.append(uitype).on('resize', function(e){
  30992. e.stopPropagation();
  30993. });
  30994. if (api2) {
  30995. control.append(/*$(row), */uiresize, uicrop.hide(), uirotate.hide());
  30996. } else {
  30997. control.append(/*$(row), */uiresize);
  30998. }
  30999. rhandle.append('<div class="'+hline+' '+hline+'-top"></div>',
  31000. '<div class="'+hline+' '+hline+'-bottom"></div>',
  31001. '<div class="'+vline+' '+vline+'-left"></div>',
  31002. '<div class="'+vline+' '+vline+'-right"></div>',
  31003. '<div class="'+rpoint+' '+rpoint+'-e"></div>',
  31004. '<div class="'+rpoint+' '+rpoint+'-se"></div>',
  31005. '<div class="'+rpoint+' '+rpoint+'-s"></div>');
  31006. preview.append(spinner).append(rhandle.hide()).append(img.hide());
  31007. if (api2) {
  31008. rhandlec.css('position', 'absolute')
  31009. .append('<div class="'+hline+' '+hline+'-top"></div>',
  31010. '<div class="'+hline+' '+hline+'-bottom"></div>',
  31011. '<div class="'+vline+' '+vline+'-left"></div>',
  31012. '<div class="'+vline+' '+vline+'-right"></div>',
  31013. '<div class="'+rpoint+' '+rpoint+'-n"></div>',
  31014. '<div class="'+rpoint+' '+rpoint+'-e"></div>',
  31015. '<div class="'+rpoint+' '+rpoint+'-s"></div>',
  31016. '<div class="'+rpoint+' '+rpoint+'-w"></div>',
  31017. '<div class="'+rpoint+' '+rpoint+'-ne"></div>',
  31018. '<div class="'+rpoint+' '+rpoint+'-se"></div>',
  31019. '<div class="'+rpoint+' '+rpoint+'-sw"></div>',
  31020. '<div class="'+rpoint+' '+rpoint+'-nw"></div>');
  31021. preview.append(basec.css('position', 'absolute').hide().append(imgc, rhandlec.append(coverc)));
  31022. preview.append(imgr.hide());
  31023. }
  31024. preview.css('overflow', 'hidden');
  31025. dialog.append(preview, control);
  31026. buttons[fm.i18n('btnApply')] = save;
  31027. if (useSaveAs) {
  31028. buttons[fm.i18n('btnSaveAs')] = function() { requestAnimationFrame(saveAs); };
  31029. }
  31030. buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); };
  31031. dialog.find('input,button').addClass('elfinder-tabstop');
  31032. base = self.fmDialog(dialog, {
  31033. title : fm.escape(file.name),
  31034. width : dialogWidth,
  31035. resizable : false,
  31036. buttons : buttons,
  31037. open : function() {
  31038. var doDimReq = function(force) {
  31039. dimreq = fm.request({
  31040. data : {cmd : 'dim', target : file.hash, substitute : substituteImg? 400 : ''},
  31041. preventDefault : true
  31042. })
  31043. .done(function(data) {
  31044. if (!data.url && needPng) {
  31045. dialog.elfinderdialog('close');
  31046. fm.error(['errOpen', file.name]);
  31047. } else {
  31048. if (data.dim) {
  31049. var dim = data.dim.split('x');
  31050. file.width = dim[0];
  31051. file.height = dim[1];
  31052. setdim(dim);
  31053. if (data.url) {
  31054. img.attr('src', data.url);
  31055. imgc.attr('src', data.url);
  31056. imgr.attr('src', data.url);
  31057. }
  31058. return init();
  31059. }
  31060. }
  31061. });
  31062. },
  31063. needPng = !{'image/jpeg':true,'image/png':true,'image/gif':true,}[file.mime],
  31064. substituteImg = fm.option('substituteImg', file.hash) && (needPng || file.size > options.dimSubImgSize)? true : false,
  31065. hasSize = (file.width && file.height)? true : false;
  31066. dMinBtn = base.find('.ui-dialog-titlebar .elfinder-titlebar-minimize').hide();
  31067. fm.bind('resize', dinit);
  31068. img.attr('src', src).one('error.dimreq', function() {
  31069. doDimReq(true);
  31070. });
  31071. imgc.attr('src', src);
  31072. imgr.attr('src', src);
  31073. if (api2) {
  31074. imgr.on('mousedown touchstart', rotate.start)
  31075. .on('touchend', rotate.stop);
  31076. base.on('mouseup', rotate.stop);
  31077. }
  31078. if (hasSize && !substituteImg) {
  31079. return init();
  31080. }
  31081. if (file.size > (options.getDimThreshold || 0)) {
  31082. img.off('error.dimreq');
  31083. doDimReq();
  31084. } else if (hasSize) {
  31085. return init();
  31086. }
  31087. },
  31088. close : function() {
  31089. if (api2) {
  31090. imgr.off('mousedown touchstart', rotate.start)
  31091. .off('touchend', rotate.stop);
  31092. $(document).off('mouseup', rotate.stop);
  31093. }
  31094. fm.unbind('resize', dinit);
  31095. $(this).elfinderdialog('destroy');
  31096. },
  31097. resize : function(e, data) {
  31098. if (data && data.minimize === 'off') {
  31099. dinit();
  31100. }
  31101. }
  31102. }).attr('id', id).closest('.ui-dialog').addClass(clsediting);
  31103. // for IE < 9 dialog mising at open second+ time.
  31104. if (fm.UA.ltIE8) {
  31105. $('.elfinder-dialog').css('filter', '');
  31106. }
  31107. coverc.css({ 'opacity': 0.2, 'background-color': '#fff', 'position': 'absolute'}),
  31108. rhandlec.css('cursor', 'move');
  31109. rhandlec.find('.elfinder-resize-handle-point').css({
  31110. 'background-color' : '#fff',
  31111. 'opacity': 0.5,
  31112. 'border-color':'#000'
  31113. });
  31114. if (! api2) {
  31115. uitype.find('.api2').remove();
  31116. }
  31117. control.find('input,select').prop('disabled', true);
  31118. control.find('input.elfinder-resize-quality')
  31119. .next('span').addClass('elfinder-resize-jpgsize').attr('title', fm.i18n('roughFileSize'));
  31120. },
  31121. id, dialog, size0, size1
  31122. ;
  31123. if (!files.length || files[0].mime.indexOf('image/') === -1) {
  31124. return dfrd.reject();
  31125. }
  31126. id = 'resize-'+fm.namespace+'-'+files[0].hash;
  31127. dialog = fmnode.find('#'+id);
  31128. if (dialog.length) {
  31129. dialog.elfinderdialog('toTop');
  31130. return dfrd.resolve();
  31131. }
  31132. fm.openUrl(files[0].hash, 'sameorigin', function(src) {
  31133. open(files[0], id, src);
  31134. });
  31135. return dfrd;
  31136. };
  31137. };
  31138. (function ($) {
  31139. var findProperty = function (styleObject, styleArgs) {
  31140. var i = 0 ;
  31141. for( i in styleArgs) {
  31142. if (typeof styleObject[styleArgs[i]] != 'undefined')
  31143. return styleArgs[i];
  31144. }
  31145. styleObject[styleArgs[i]] = '';
  31146. return styleArgs[i];
  31147. };
  31148. $.cssHooks.rotate = {
  31149. get: function(elem, computed, extra) {
  31150. return $(elem).rotate();
  31151. },
  31152. set: function(elem, value) {
  31153. $(elem).rotate(value);
  31154. return value;
  31155. }
  31156. };
  31157. $.cssHooks.transform = {
  31158. get: function(elem, computed, extra) {
  31159. var name = findProperty( elem.style ,
  31160. ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] );
  31161. return elem.style[name];
  31162. },
  31163. set: function(elem, value) {
  31164. var name = findProperty( elem.style ,
  31165. ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] );
  31166. elem.style[name] = value;
  31167. return value;
  31168. }
  31169. };
  31170. $.fn.rotate = function(val) {
  31171. var r;
  31172. if (typeof val == 'undefined') {
  31173. if (!!window.opera) {
  31174. r = this.css('transform').match(/rotate\((.*?)\)/);
  31175. return ( r && r[1])?
  31176. Math.round(parseFloat(r[1]) * 180 / Math.PI) : 0;
  31177. } else {
  31178. r = this.css('transform').match(/rotate\((.*?)\)/);
  31179. return ( r && r[1])? parseInt(r[1]) : 0;
  31180. }
  31181. }
  31182. this.css('transform',
  31183. this.css('transform').replace(/none|rotate\(.*?\)/, '') + 'rotate(' + parseInt(val) + 'deg)');
  31184. return this;
  31185. };
  31186. $.fx.step.rotate = function(fx) {
  31187. if ( fx.state == 0 ) {
  31188. fx.start = $(fx.elem).rotate();
  31189. fx.now = fx.start;
  31190. }
  31191. $(fx.elem).rotate(fx.now);
  31192. };
  31193. if (typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined") { // IE & IE<9
  31194. var GetAbsoluteXY = function(element) {
  31195. var pnode = element;
  31196. var x = pnode.offsetLeft;
  31197. var y = pnode.offsetTop;
  31198. while ( pnode.offsetParent ) {
  31199. pnode = pnode.offsetParent;
  31200. if (pnode != document.body && pnode.currentStyle['position'] != 'static') {
  31201. break;
  31202. }
  31203. if (pnode != document.body && pnode != document.documentElement) {
  31204. x -= pnode.scrollLeft;
  31205. y -= pnode.scrollTop;
  31206. }
  31207. x += pnode.offsetLeft;
  31208. y += pnode.offsetTop;
  31209. }
  31210. return { x: x, y: y };
  31211. };
  31212. var StaticToAbsolute = function (element) {
  31213. if ( element.currentStyle['position'] != 'static') {
  31214. return ;
  31215. }
  31216. var xy = GetAbsoluteXY(element);
  31217. element.style.position = 'absolute' ;
  31218. element.style.left = xy.x + 'px';
  31219. element.style.top = xy.y + 'px';
  31220. };
  31221. var IETransform = function(element,transform){
  31222. var r;
  31223. var m11 = 1;
  31224. var m12 = 1;
  31225. var m21 = 1;
  31226. var m22 = 1;
  31227. if (typeof element.style['msTransform'] != 'undefined'){
  31228. return true;
  31229. }
  31230. StaticToAbsolute(element);
  31231. r = transform.match(/rotate\((.*?)\)/);
  31232. var rotate = ( r && r[1]) ? parseInt(r[1]) : 0;
  31233. rotate = rotate % 360;
  31234. if (rotate < 0) rotate = 360 + rotate;
  31235. var radian= rotate * Math.PI / 180;
  31236. var cosX =Math.cos(radian);
  31237. var sinY =Math.sin(radian);
  31238. m11 *= cosX;
  31239. m12 *= -sinY;
  31240. m21 *= sinY;
  31241. m22 *= cosX;
  31242. element.style.filter = (element.style.filter || '').replace(/progid:DXImageTransform\.Microsoft\.Matrix\([^)]*\)/, "" ) +
  31243. ("progid:DXImageTransform.Microsoft.Matrix(" +
  31244. "M11=" + m11 +
  31245. ",M12=" + m12 +
  31246. ",M21=" + m21 +
  31247. ",M22=" + m22 +
  31248. ",FilterType='bilinear',sizingMethod='auto expand')")
  31249. ;
  31250. var ow = parseInt(element.style.width || element.width || 0 );
  31251. var oh = parseInt(element.style.height || element.height || 0 );
  31252. radian = rotate * Math.PI / 180;
  31253. var absCosX =Math.abs(Math.cos(radian));
  31254. var absSinY =Math.abs(Math.sin(radian));
  31255. var dx = (ow - (ow * absCosX + oh * absSinY)) / 2;
  31256. var dy = (oh - (ow * absSinY + oh * absCosX)) / 2;
  31257. element.style.marginLeft = Math.floor(dx) + "px";
  31258. element.style.marginTop = Math.floor(dy) + "px";
  31259. return(true);
  31260. };
  31261. var transform_set = $.cssHooks.transform.set;
  31262. $.cssHooks.transform.set = function(elem, value) {
  31263. transform_set.apply(this, [elem, value] );
  31264. IETransform(elem,value);
  31265. return value;
  31266. };
  31267. }
  31268. })(jQuery);
  31269. /*
  31270. * File: /js/commands/restore.js
  31271. */
  31272. /**
  31273. * @class elFinder command "restore"
  31274. * Restore items from the trash
  31275. *
  31276. * @author Naoki Sawada
  31277. **/
  31278. (elFinder.prototype.commands.restore = function() {
  31279. "use strict";
  31280. var self = this,
  31281. fm = this.fm,
  31282. fakeCnt = 0,
  31283. getFilesRecursively = function(files) {
  31284. var dfd = $.Deferred(),
  31285. dirs = [],
  31286. results = [],
  31287. reqs = [],
  31288. phashes = [],
  31289. getFile;
  31290. dfd._xhrReject = function() {
  31291. $.each(reqs, function() {
  31292. this && this.reject && this.reject();
  31293. });
  31294. getFile && getFile._xhrReject();
  31295. };
  31296. $.each(files, function(i, f) {
  31297. f.mime === 'directory'? dirs.push(f) : results.push(f);
  31298. });
  31299. if (dirs.length) {
  31300. $.each(dirs, function(i, d) {
  31301. reqs.push(fm.request({
  31302. data : {cmd : 'open', target : d.hash},
  31303. preventDefault : true,
  31304. asNotOpen : true
  31305. }));
  31306. phashes[i] = d.hash;
  31307. });
  31308. $.when.apply($, reqs).fail(function() {
  31309. dfd.reject();
  31310. }).done(function() {
  31311. var items = [];
  31312. $.each(arguments, function(i, r) {
  31313. var files;
  31314. if (r.files) {
  31315. if (r.files.length) {
  31316. items = items.concat(r.files);
  31317. } else {
  31318. items.push({
  31319. hash: 'fakefile_' + (fakeCnt++),
  31320. phash: phashes[i],
  31321. mime: 'fakefile',
  31322. name: 'fakefile',
  31323. ts: 0
  31324. });
  31325. }
  31326. }
  31327. });
  31328. fm.cache(items);
  31329. getFile = getFilesRecursively(items).done(function(res) {
  31330. results = results.concat(res);
  31331. dfd.resolve(results);
  31332. });
  31333. });
  31334. } else {
  31335. dfd.resolve(results);
  31336. }
  31337. return dfd;
  31338. },
  31339. restore = function(dfrd, files, targets, ops) {
  31340. var rHashes = {},
  31341. others = [],
  31342. found = false,
  31343. dirs = [],
  31344. opts = ops || {},
  31345. id = +new Date(),
  31346. tm, getFile;
  31347. fm.lockfiles({files : targets});
  31348. dirs = $.map(files, function(f) {
  31349. return f.mime === 'directory'? f.hash : null;
  31350. });
  31351. dfrd.done(function() {
  31352. dirs && fm.exec('rm', dirs, {forceRm : true, quiet : true});
  31353. }).always(function() {
  31354. fm.unlockfiles({files : targets});
  31355. });
  31356. tm = setTimeout(function() {
  31357. fm.notify({type : 'search', id : id, cnt : 1, hideCnt : true, cancel : function() {
  31358. getFile && getFile._xhrReject();
  31359. dfrd.reject();
  31360. }});
  31361. }, fm.notifyDelay);
  31362. fakeCnt = 0;
  31363. getFile = getFilesRecursively(files).always(function() {
  31364. tm && clearTimeout(tm);
  31365. fm.notify({type : 'search', id: id, cnt : -1, hideCnt : true});
  31366. }).fail(function() {
  31367. dfrd.reject('errRestore', 'errFileNotFound');
  31368. }).done(function(res) {
  31369. var errFolderNotfound = ['errRestore', 'errFolderNotFound'],
  31370. dirTop = '';
  31371. if (res.length) {
  31372. $.each(res, function(i, f) {
  31373. var phash = f.phash,
  31374. pfile,
  31375. srcRoot, tPath;
  31376. while(phash) {
  31377. if (srcRoot = fm.trashes[phash]) {
  31378. if (! rHashes[srcRoot]) {
  31379. if (found) {
  31380. // Keep items of other trash
  31381. others.push(f.hash);
  31382. return null; // continue $.each
  31383. }
  31384. rHashes[srcRoot] = {};
  31385. found = true;
  31386. }
  31387. tPath = fm.path(f.hash).substr(fm.path(phash).length).replace(/\\/g, '/');
  31388. tPath = tPath.replace(/\/[^\/]+?$/, '');
  31389. if (tPath === '') {
  31390. tPath = '/';
  31391. }
  31392. if (!rHashes[srcRoot][tPath]) {
  31393. rHashes[srcRoot][tPath] = [];
  31394. }
  31395. if (f.mime === 'fakefile') {
  31396. fm.updateCache({removed:[f.hash]});
  31397. } else {
  31398. rHashes[srcRoot][tPath].push(f.hash);
  31399. }
  31400. if (!dirTop || dirTop.length > tPath.length) {
  31401. dirTop = tPath;
  31402. }
  31403. break;
  31404. }
  31405. // Go up one level for next check
  31406. pfile = fm.file(phash);
  31407. if (!pfile) {
  31408. phash = false;
  31409. // Detection method for search results
  31410. $.each(fm.trashes, function(ph) {
  31411. var file = fm.file(ph),
  31412. filePath = fm.path(ph);
  31413. if ((!file.volumeid || f.hash.indexOf(file.volumeid) === 0) && fm.path(f.hash).indexOf(filePath) === 0) {
  31414. phash = ph;
  31415. return false;
  31416. }
  31417. });
  31418. } else {
  31419. phash = pfile.phash;
  31420. }
  31421. }
  31422. });
  31423. if (found) {
  31424. $.each(rHashes, function(src, dsts) {
  31425. var dirs = Object.keys(dsts),
  31426. cnt = dirs.length;
  31427. fm.request({
  31428. data : {cmd : 'mkdir', target : src, dirs : dirs},
  31429. notify : {type : 'chkdir', cnt : cnt},
  31430. preventFail : true
  31431. }).fail(function(error) {
  31432. dfrd.reject(error);
  31433. fm.unlockfiles({files : targets});
  31434. }).done(function(data) {
  31435. var cmdPaste, hashes;
  31436. if (hashes = data.hashes) {
  31437. cmdPaste = fm.getCommand('paste');
  31438. if (cmdPaste) {
  31439. // wait until file cache made
  31440. fm.one('mkdirdone', function() {
  31441. var hasErr = false;
  31442. $.each(dsts, function(dir, files) {
  31443. if (hashes[dir]) {
  31444. if (files.length) {
  31445. if (fm.file(hashes[dir])) {
  31446. fm.clipboard(files, true);
  31447. fm.exec('paste', [ hashes[dir] ], {_cmd : 'restore', noToast : (opts.noToast || dir !== dirTop)})
  31448. .done(function(data) {
  31449. if (data && (data.error || data.warning)) {
  31450. hasErr = true;
  31451. }
  31452. })
  31453. .fail(function() {
  31454. hasErr = true;
  31455. })
  31456. .always(function() {
  31457. if (--cnt < 1) {
  31458. dfrd[hasErr? 'reject' : 'resolve']();
  31459. if (others.length) {
  31460. // Restore items of other trash
  31461. fm.exec('restore', others);
  31462. }
  31463. }
  31464. });
  31465. } else {
  31466. dfrd.reject(errFolderNotfound);
  31467. }
  31468. } else {
  31469. if (--cnt < 1) {
  31470. dfrd.resolve();
  31471. if (others.length) {
  31472. // Restore items of other trash
  31473. fm.exec('restore', others);
  31474. }
  31475. }
  31476. }
  31477. }
  31478. });
  31479. });
  31480. } else {
  31481. dfrd.reject(['errRestore', 'errCmdNoSupport', '(paste)']);
  31482. }
  31483. } else {
  31484. dfrd.reject(errFolderNotfound);
  31485. }
  31486. });
  31487. });
  31488. } else {
  31489. dfrd.reject(errFolderNotfound);
  31490. }
  31491. } else {
  31492. dfrd.reject('errFileNotFound');
  31493. dirs && fm.exec('rm', dirs, {forceRm : true, quiet : true});
  31494. }
  31495. });
  31496. };
  31497. // for to be able to overwrite
  31498. this.restore = restore;
  31499. this.linkedCmds = ['copy', 'paste', 'mkdir', 'rm'];
  31500. this.updateOnSelect = false;
  31501. this.init = function() {
  31502. // re-assign for extended command
  31503. self = this;
  31504. fm = this.fm;
  31505. };
  31506. this.getstate = function(sel, e) {
  31507. sel = sel || fm.selected();
  31508. return sel.length && $.grep(sel, function(h) {var f = fm.file(h); return f && ! f.locked && ! fm.isRoot(f)? true : false; }).length == sel.length
  31509. ? 0 : -1;
  31510. };
  31511. this.exec = function(hashes, opts) {
  31512. var dfrd = $.Deferred()
  31513. .fail(function(error) {
  31514. error && fm.error(error);
  31515. }),
  31516. files = self.files(hashes);
  31517. if (! files.length) {
  31518. return dfrd.reject();
  31519. }
  31520. $.each(files, function(i, file) {
  31521. if (fm.isRoot(file)) {
  31522. return !dfrd.reject(['errRestore', file.name]);
  31523. }
  31524. if (file.locked) {
  31525. return !dfrd.reject(['errLocked', file.name]);
  31526. }
  31527. });
  31528. if (dfrd.state() === 'pending') {
  31529. this.restore(dfrd, files, hashes, opts);
  31530. }
  31531. return dfrd;
  31532. };
  31533. }).prototype = { forceLoad : true }; // this is required command
  31534. /*
  31535. * File: /js/commands/rm.js
  31536. */
  31537. /**
  31538. * @class elFinder command "rm"
  31539. * Delete files
  31540. *
  31541. * @author Dmitry (dio) Levashov
  31542. * @author Naoki Sawada
  31543. **/
  31544. elFinder.prototype.commands.rm = function() {
  31545. "use strict";
  31546. var self = this,
  31547. fm = this.fm,
  31548. tpl = '<div class="ui-helper-clearfix elfinder-rm-title"><span class="elfinder-cwd-icon {class} ui-corner-all"></span>{title}<div class="elfinder-rm-desc">{desc}</div></div>',
  31549. confirm = function(dfrd, targets, files, tHash, addTexts) {
  31550. var cnt = targets.length,
  31551. cwd = fm.cwd().hash,
  31552. descs = [],
  31553. spinner = fm.i18n('calc') + '<span class="elfinder-spinner"></span>',
  31554. dialog, text, tmb, size, f, fname;
  31555. if (cnt > 1) {
  31556. size = 0;
  31557. $.each(files, function(h, f) {
  31558. if (f.size && f.size != 'unknown' && f.mime !== 'directory') {
  31559. var s = parseInt(f.size);
  31560. if (s >= 0 && size >= 0) {
  31561. size += s;
  31562. }
  31563. } else {
  31564. size = 'unknown';
  31565. return false;
  31566. }
  31567. });
  31568. getSize = (size === 'unknown');
  31569. descs.push(fm.i18n('size')+': '+(getSize? spinner : fm.formatSize(size)));
  31570. text = [$(tpl.replace('{class}', 'elfinder-cwd-icon-group').replace('{title}', '<strong>' + fm.i18n('items')+ ': ' + cnt + '</strong>').replace('{desc}', descs.join('<br>')))];
  31571. } else {
  31572. f = files[0];
  31573. tmb = fm.tmb(f);
  31574. getSize = (f.mime === 'directory');
  31575. descs.push(fm.i18n('size')+': '+(getSize? spinner : fm.formatSize(f.size)));
  31576. descs.push(fm.i18n('modify')+': '+fm.formatDate(f));
  31577. fname = fm.escape(f.i18 || f.name).replace(/([_.])/g, '&#8203;$1');
  31578. text = [$(tpl.replace('{class}', fm.mime2class(f.mime)).replace('{title}', '<strong>' + fname + '</strong>').replace('{desc}', descs.join('<br>')))];
  31579. }
  31580. if (addTexts) {
  31581. text = text.concat(addTexts);
  31582. }
  31583. text.push(tHash? 'confirmTrash' : 'confirmRm');
  31584. dialog = fm.confirm({
  31585. title : self.title,
  31586. text : text,
  31587. accept : {
  31588. label : 'btnRm',
  31589. callback : function() {
  31590. if (tHash) {
  31591. self.toTrash(dfrd, targets, tHash);
  31592. } else {
  31593. remove(dfrd, targets);
  31594. }
  31595. }
  31596. },
  31597. cancel : {
  31598. label : 'btnCancel',
  31599. callback : function() {
  31600. fm.unlockfiles({files : targets});
  31601. if (targets.length === 1 && fm.file(targets[0]).phash !== cwd) {
  31602. fm.select({selected : targets});
  31603. } else {
  31604. fm.selectfiles({files : targets});
  31605. }
  31606. dfrd.reject();
  31607. }
  31608. }
  31609. });
  31610. // load thumbnail
  31611. if (tmb) {
  31612. $('<img/>')
  31613. .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  31614. .attr('src', tmb.url);
  31615. }
  31616. if (getSize) {
  31617. getSize = fm.getSize($.map(files, function(f) { return f.mime === 'directory'? f.hash : null; })).done(function(data) {
  31618. dialog.find('span.elfinder-spinner').parent().html(fm.i18n('size')+': '+data.formated);
  31619. }).fail(function() {
  31620. dialog.find('span.elfinder-spinner').parent().html(fm.i18n('size')+': '+fm.i18n('unknown'));
  31621. }).always(function() {
  31622. getSize = false;
  31623. });
  31624. }
  31625. },
  31626. toTrash = function(dfrd, targets, tHash) {
  31627. var dsts = {},
  31628. itemCnt = targets.length,
  31629. maxCnt = self.options.toTrashMaxItems,
  31630. checkDirs = [],
  31631. reqDfd = $.Deferred(),
  31632. req, dirs, cnt;
  31633. if (itemCnt > maxCnt) {
  31634. self.confirm(dfrd, targets, self.files(targets), null, [fm.i18n('tooManyToTrash')]);
  31635. return;
  31636. }
  31637. // Directory preparation preparation and directory enumeration
  31638. $.each(targets, function(i, h) {
  31639. var file = fm.file(h),
  31640. path = fm.path(h).replace(/\\/g, '/'),
  31641. m = path.match(/^[^\/]+?(\/(?:[^\/]+?\/)*)[^\/]+?$/);
  31642. if (file) {
  31643. if (m) {
  31644. m[1] = m[1].replace(/(^\/.*?)\/?$/, '$1');
  31645. if (! dsts[m[1]]) {
  31646. dsts[m[1]] = [];
  31647. }
  31648. dsts[m[1]].push(h);
  31649. }
  31650. if (file.mime === 'directory') {
  31651. checkDirs.push(h);
  31652. }
  31653. }
  31654. });
  31655. // Check directory information
  31656. if (checkDirs.length) {
  31657. req = fm.request({
  31658. data : {cmd : 'size', targets : checkDirs},
  31659. notify : {type: 'readdir', cnt: 1, hideCnt: true},
  31660. preventDefault : true
  31661. }).done(function(data) {
  31662. var cnt = 0;
  31663. data.fileCnt && (cnt += parseInt(data.fileCnt));
  31664. data.dirCnt && (cnt += parseInt(data.dirCnt));
  31665. reqDfd[cnt > maxCnt ? 'reject' : 'resolve']();
  31666. }).fail(function() {
  31667. reqDfd.reject();
  31668. });
  31669. setTimeout(function() {
  31670. var xhr = (req && req.xhr)? req.xhr : null;
  31671. if (xhr && xhr.state() == 'pending') {
  31672. req.syncOnFail(false);
  31673. req.reject();
  31674. reqDfd.reject();
  31675. }
  31676. }, self.options.infoCheckWait * 1000);
  31677. } else {
  31678. reqDfd.resolve();
  31679. }
  31680. // Directory creation and paste command execution
  31681. reqDfd.done(function() {
  31682. dirs = Object.keys(dsts);
  31683. cnt = dirs.length;
  31684. if (cnt) {
  31685. fm.request({
  31686. data : {cmd : 'mkdir', target : tHash, dirs : dirs},
  31687. notify : {type : 'chkdir', cnt : cnt},
  31688. preventFail : true
  31689. })
  31690. .fail(function(error) {
  31691. dfrd.reject(error);
  31692. fm.unlockfiles({files : targets});
  31693. })
  31694. .done(function(data) {
  31695. var margeRes = function(data, phash, reqData) {
  31696. var undo, prevUndo, redo, prevRedo;
  31697. $.each(data, function(k, v) {
  31698. if (Array.isArray(v)) {
  31699. if (res[k]) {
  31700. res[k] = res[k].concat(v);
  31701. } else {
  31702. res[k] = v;
  31703. }
  31704. }
  31705. });
  31706. if (data.sync) {
  31707. res.sync = 1;
  31708. }
  31709. if (data.added && data.added.length) {
  31710. undo = function() {
  31711. var targets = [],
  31712. dirs = $.map(data.added, function(f) { return f.mime === 'directory'? f.hash : null; });
  31713. $.each(data.added, function(i, f) {
  31714. if ($.inArray(f.phash, dirs) === -1) {
  31715. targets.push(f.hash);
  31716. }
  31717. });
  31718. return fm.exec('restore', targets, {noToast: true});
  31719. };
  31720. redo = function() {
  31721. return fm.request({
  31722. data : reqData,
  31723. notify : {type : 'redo', cnt : targets.length}
  31724. });
  31725. };
  31726. if (res.undo) {
  31727. prevUndo = res.undo;
  31728. res.undo = function() {
  31729. undo();
  31730. prevUndo();
  31731. };
  31732. } else {
  31733. res.undo = undo;
  31734. }
  31735. if (res.redo) {
  31736. prevRedo = res.redo;
  31737. res.redo = function() {
  31738. redo();
  31739. prevRedo();
  31740. };
  31741. } else {
  31742. res.redo = redo;
  31743. }
  31744. }
  31745. },
  31746. err = ['errTrash'],
  31747. res = {},
  31748. hasNtf = function() {
  31749. return fm.ui.notify.children('.elfinder-notify-trash').length;
  31750. },
  31751. hashes, tm, prg, prgSt;
  31752. if (hashes = data.hashes) {
  31753. prg = 1 / cnt * 100;
  31754. prgSt = cnt === 1? 100 : 5;
  31755. tm = setTimeout(function() {
  31756. fm.notify({type : 'trash', cnt : 1, hideCnt : true, progress : prgSt});
  31757. }, fm.notifyDelay);
  31758. $.each(dsts, function(dir, files) {
  31759. var phash = fm.file(files[0]).phash,
  31760. reqData;
  31761. if (hashes[dir]) {
  31762. reqData = {cmd : 'paste', dst : hashes[dir], targets : files, cut : 1};
  31763. fm.request({
  31764. data : reqData,
  31765. preventDefault : true
  31766. })
  31767. .fail(function(error) {
  31768. if (error) {
  31769. err = err.concat(error);
  31770. }
  31771. })
  31772. .done(function(data) {
  31773. data = fm.normalize(data);
  31774. fm.updateCache(data);
  31775. margeRes(data, phash, reqData);
  31776. if (data.warning) {
  31777. err = err.concat(data.warning);
  31778. delete data.warning;
  31779. }
  31780. // fire some event to update cache/ui
  31781. data.removed && data.removed.length && fm.remove(data);
  31782. data.added && data.added.length && fm.add(data);
  31783. data.changed && data.changed.length && fm.change(data);
  31784. // fire event with command name
  31785. fm.trigger('paste', data);
  31786. // fire event with command name + 'done'
  31787. fm.trigger('pastedone');
  31788. // force update content
  31789. data.sync && fm.sync();
  31790. })
  31791. .always(function() {
  31792. var hashes = [], addTexts, end = 2;
  31793. if (hasNtf()) {
  31794. fm.notify({type : 'trash', cnt : 0, hideCnt : true, progress : prg});
  31795. } else {
  31796. prgSt+= prg;
  31797. }
  31798. if (--cnt < 1) {
  31799. tm && clearTimeout(tm);
  31800. hasNtf() && fm.notify({type : 'trash', cnt : -1});
  31801. fm.unlockfiles({files : targets});
  31802. if (Object.keys(res).length) {
  31803. if (err.length > 1) {
  31804. if (res.removed || res.removed.length) {
  31805. hashes = $.grep(targets, function(h) {
  31806. return $.inArray(h, res.removed) === -1? true : false;
  31807. });
  31808. }
  31809. if (hashes.length) {
  31810. if (err.length > end) {
  31811. end = (fm.messages[err[end-1]] || '').indexOf('$') === -1? end : end + 1;
  31812. }
  31813. dfrd.reject();
  31814. fm.exec('rm', hashes, { addTexts: err.slice(0, end), forceRm: true });
  31815. } else {
  31816. fm.error(err);
  31817. }
  31818. }
  31819. res._noSound = true;
  31820. if (res.undo && res.redo) {
  31821. res.undo = {
  31822. cmd : 'trash',
  31823. callback : res.undo,
  31824. };
  31825. res.redo = {
  31826. cmd : 'trash',
  31827. callback : res.redo
  31828. };
  31829. }
  31830. dfrd.resolve(res);
  31831. } else {
  31832. dfrd.reject(err);
  31833. }
  31834. }
  31835. });
  31836. }
  31837. });
  31838. } else {
  31839. dfrd.reject('errFolderNotFound');
  31840. fm.unlockfiles({files : targets});
  31841. }
  31842. });
  31843. } else {
  31844. dfrd.reject(['error', 'The folder hierarchy to be deleting can not be determined.']);
  31845. fm.unlockfiles({files : targets});
  31846. }
  31847. }).fail(function() {
  31848. self.confirm(dfrd, targets, self.files(targets), null, [fm.i18n('tooManyToTrash')]);
  31849. });
  31850. },
  31851. remove = function(dfrd, targets, quiet) {
  31852. var notify = quiet? {} : {type : 'rm', cnt : targets.length};
  31853. fm.request({
  31854. data : {cmd : 'rm', targets : targets},
  31855. notify : notify,
  31856. preventFail : true
  31857. })
  31858. .fail(function(error) {
  31859. dfrd.reject(error);
  31860. })
  31861. .done(function(data) {
  31862. if (data.error || data.warning) {
  31863. data.sync = true;
  31864. }
  31865. dfrd.resolve(data);
  31866. })
  31867. .always(function() {
  31868. fm.unlockfiles({files : targets});
  31869. });
  31870. },
  31871. getTHash = function(targets) {
  31872. var thash = null,
  31873. root1st;
  31874. if (targets && targets.length) {
  31875. if (targets.length > 1 && fm.searchStatus.state === 2) {
  31876. root1st = fm.file(fm.root(targets[0])).volumeid;
  31877. if (!$.grep(targets, function(h) { return h.indexOf(root1st) !== 0? true : false ; }).length) {
  31878. thash = fm.option('trashHash', targets[0]);
  31879. }
  31880. } else {
  31881. thash = fm.option('trashHash', targets[0]);
  31882. }
  31883. }
  31884. return thash;
  31885. },
  31886. getSize = false;
  31887. // for to be able to overwrite
  31888. this.confirm = confirm;
  31889. this.toTrash = toTrash;
  31890. this.remove = remove;
  31891. this.syncTitleOnChange = true;
  31892. this.updateOnSelect = false;
  31893. this.shortcuts = [{
  31894. pattern : 'delete ctrl+backspace shift+delete'
  31895. }];
  31896. this.value = 'rm';
  31897. this.init = function() {
  31898. // re-assign for extended command
  31899. self = this;
  31900. fm = this.fm;
  31901. // bind function of change
  31902. self.change(function() {
  31903. var targets;
  31904. delete self.extra;
  31905. self.title = fm.i18n('cmd' + self.value);
  31906. self.className = self.value;
  31907. self.button && self.button.children('span.elfinder-button-icon')[self.value === 'trash'? 'addClass' : 'removeClass']('elfinder-button-icon-trash');
  31908. if (self.value === 'trash') {
  31909. self.extra = {
  31910. icon: 'rm',
  31911. node: $('<span></span>')
  31912. .attr({title: fm.i18n('cmdrm')})
  31913. .on('ready', function(e, data) {
  31914. targets = data.targets;
  31915. })
  31916. .on('click touchstart', function(e){
  31917. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  31918. return;
  31919. }
  31920. e.stopPropagation();
  31921. e.preventDefault();
  31922. fm.getUI().trigger('click'); // to close the context menu immediately
  31923. fm.exec('rm', targets, {_userAction: true, forceRm : true});
  31924. })
  31925. };
  31926. }
  31927. });
  31928. };
  31929. this.getstate = function(select) {
  31930. var sel = this.hashes(select);
  31931. return sel.length && $.grep(sel, function(h) { var f = fm.file(h); return f && ! f.locked && ! fm.isRoot(f)? true : false; }).length == sel.length
  31932. ? 0 : -1;
  31933. };
  31934. this.exec = function(hashes, cOpts) {
  31935. var opts = cOpts || {},
  31936. dfrd = $.Deferred()
  31937. .always(function() {
  31938. if (getSize && getSize.state && getSize.state() === 'pending') {
  31939. getSize.reject();
  31940. }
  31941. })
  31942. .fail(function(error) {
  31943. error && fm.error(error);
  31944. }).done(function(data) {
  31945. !opts.quiet && !data._noSound && data.removed && data.removed.length && fm.trigger('playsound', {soundFile : 'rm.wav'});
  31946. }),
  31947. files = self.files(hashes),
  31948. cnt = files.length,
  31949. tHash = null,
  31950. addTexts = opts.addTexts? opts.addTexts : null,
  31951. forceRm = opts.forceRm,
  31952. quiet = opts.quiet,
  31953. targets;
  31954. if (! cnt) {
  31955. return dfrd.reject();
  31956. }
  31957. $.each(files, function(i, file) {
  31958. if (fm.isRoot(file)) {
  31959. return !dfrd.reject(['errRm', file.name, 'errPerm']);
  31960. }
  31961. if (file.locked) {
  31962. return !dfrd.reject(['errLocked', file.name]);
  31963. }
  31964. });
  31965. if (dfrd.state() === 'pending') {
  31966. targets = self.hashes(hashes);
  31967. cnt = files.length;
  31968. if (forceRm || (self.event && self.event.originalEvent && self.event.originalEvent.shiftKey)) {
  31969. tHash = '';
  31970. self.title = fm.i18n('cmdrm');
  31971. }
  31972. if (tHash === null) {
  31973. tHash = getTHash(targets);
  31974. }
  31975. fm.lockfiles({files : targets});
  31976. if (tHash && self.options.quickTrash) {
  31977. self.toTrash(dfrd, targets, tHash);
  31978. } else {
  31979. if (quiet) {
  31980. remove(dfrd, targets, quiet);
  31981. } else {
  31982. self.confirm(dfrd, targets, files, tHash, addTexts);
  31983. }
  31984. }
  31985. }
  31986. return dfrd;
  31987. };
  31988. fm.bind('select contextmenucreate closecontextmenu', function(e) {
  31989. var targets = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected();
  31990. if (targets && targets.length) {
  31991. self.update(void(0), (targets? getTHash(targets) : fm.option('trashHash'))? 'trash' : 'rm');
  31992. }
  31993. });
  31994. };
  31995. /*
  31996. * File: /js/commands/search.js
  31997. */
  31998. /**
  31999. * @class elFinder command "search"
  32000. * Find files
  32001. *
  32002. * @author Dmitry (dio) Levashov
  32003. **/
  32004. elFinder.prototype.commands.search = function() {
  32005. "use strict";
  32006. this.title = 'Find files';
  32007. this.options = {ui : 'searchbutton'};
  32008. this.alwaysEnabled = true;
  32009. this.updateOnSelect = false;
  32010. /**
  32011. * Return command status.
  32012. * Search does not support old api.
  32013. *
  32014. * @return Number
  32015. **/
  32016. this.getstate = function() {
  32017. return 0;
  32018. };
  32019. /**
  32020. * Send search request to backend.
  32021. *
  32022. * @param String search string
  32023. * @return $.Deferred
  32024. **/
  32025. this.exec = function(q, target, mime, type) {
  32026. var fm = this.fm,
  32027. reqDef = [],
  32028. sType = type || '',
  32029. onlyMimes = fm.options.onlyMimes,
  32030. phash, targetVolids = [],
  32031. setType = function(data) {
  32032. if (sType && sType !== 'SearchName' && sType !== 'SearchMime') {
  32033. data.type = sType;
  32034. }
  32035. return data;
  32036. },
  32037. rootCnt;
  32038. if (typeof q == 'string' && q) {
  32039. if (typeof target == 'object') {
  32040. mime = target.mime || '';
  32041. target = target.target || '';
  32042. }
  32043. target = target? target : '';
  32044. if (mime) {
  32045. mime = $.trim(mime).replace(',', ' ').split(' ');
  32046. if (onlyMimes.length) {
  32047. mime = $.map(mime, function(m){
  32048. m = $.trim(m);
  32049. return m && ($.inArray(m, onlyMimes) !== -1
  32050. || $.grep(onlyMimes, function(om) { return m.indexOf(om) === 0? true : false; }).length
  32051. )? m : null;
  32052. });
  32053. }
  32054. } else {
  32055. mime = [].concat(onlyMimes);
  32056. }
  32057. fm.trigger('searchstart', setType({query : q, target : target, mimes : mime}));
  32058. if (! onlyMimes.length || mime.length) {
  32059. if (target === '' && fm.api >= 2.1) {
  32060. rootCnt = Object.keys(fm.roots).length;
  32061. $.each(fm.roots, function(id, hash) {
  32062. reqDef.push(fm.request({
  32063. data : setType({cmd : 'search', q : q, target : hash, mimes : mime}),
  32064. notify : {type : 'search', cnt : 1, hideCnt : (rootCnt > 1? false : true)},
  32065. cancel : true,
  32066. preventDone : true
  32067. }));
  32068. });
  32069. } else {
  32070. reqDef.push(fm.request({
  32071. data : setType({cmd : 'search', q : q, target : target, mimes : mime}),
  32072. notify : {type : 'search', cnt : 1, hideCnt : true},
  32073. cancel : true,
  32074. preventDone : true
  32075. }));
  32076. if (target !== '' && fm.api >= 2.1 && Object.keys(fm.leafRoots).length) {
  32077. $.each(fm.leafRoots, function(hash, roots) {
  32078. phash = hash;
  32079. while(phash) {
  32080. if (target === phash) {
  32081. $.each(roots, function() {
  32082. var f = fm.file(this);
  32083. f && f.volumeid && targetVolids.push(f.volumeid);
  32084. reqDef.push(fm.request({
  32085. data : setType({cmd : 'search', q : q, target : this, mimes : mime}),
  32086. notify : {type : 'search', cnt : 1, hideCnt : false},
  32087. cancel : true,
  32088. preventDone : true
  32089. }));
  32090. });
  32091. }
  32092. phash = (fm.file(phash) || {}).phash;
  32093. }
  32094. });
  32095. }
  32096. }
  32097. } else {
  32098. reqDef = [$.Deferred().resolve({files: []})];
  32099. }
  32100. fm.searchStatus.mixed = (reqDef.length > 1)? targetVolids : false;
  32101. return $.when.apply($, reqDef).done(function(data) {
  32102. var argLen = arguments.length,
  32103. i;
  32104. data.warning && fm.error(data.warning);
  32105. if (argLen > 1) {
  32106. data.files = (data.files || []);
  32107. for(i = 1; i < argLen; i++) {
  32108. arguments[i].warning && fm.error(arguments[i].warning);
  32109. if (arguments[i].files) {
  32110. data.files.push.apply(data.files, arguments[i].files);
  32111. }
  32112. }
  32113. }
  32114. // because "preventDone : true" so update files cache
  32115. data.files && data.files.length && fm.cache(data.files);
  32116. fm.lazy(function() {
  32117. fm.trigger('search', data);
  32118. }).then(function() {
  32119. // fire event with command name + 'done'
  32120. return fm.lazy(function() {
  32121. fm.trigger('searchdone');
  32122. });
  32123. }).then(function() {
  32124. // force update content
  32125. data.sync && fm.sync();
  32126. });
  32127. });
  32128. }
  32129. fm.getUI('toolbar').find('.'+fm.res('class', 'searchbtn')+' :text').trigger('focus');
  32130. return $.Deferred().reject();
  32131. };
  32132. };
  32133. /*
  32134. * File: /js/commands/selectall.js
  32135. */
  32136. /**
  32137. * @class elFinder command "selectall"
  32138. * Select ALL of cwd items
  32139. *
  32140. * @author Naoki Sawada
  32141. **/
  32142. elFinder.prototype.commands.selectall = function() {
  32143. "use strict";
  32144. var self = this,
  32145. state = 0;
  32146. this.fm.bind('select', function(e) {
  32147. state = (e.data && e.data.selectall)? -1 : 0;
  32148. });
  32149. this.state = 0;
  32150. this.updateOnSelect = false;
  32151. this.getstate = function() {
  32152. return state;
  32153. };
  32154. this.exec = function() {
  32155. $(document).trigger($.Event('keydown', { keyCode: 65, ctrlKey : true, shiftKey : false, altKey : false, metaKey : false }));
  32156. return $.Deferred().resolve();
  32157. };
  32158. };
  32159. /*
  32160. * File: /js/commands/selectinvert.js
  32161. */
  32162. /**
  32163. * @class elFinder command "selectinvert"
  32164. * Invert Selection of cwd items
  32165. *
  32166. * @author Naoki Sawada
  32167. **/
  32168. elFinder.prototype.commands.selectinvert = function() {
  32169. "use strict";
  32170. this.updateOnSelect = false;
  32171. this.getstate = function() {
  32172. return 0;
  32173. };
  32174. this.exec = function() {
  32175. $(document).trigger($.Event('keydown', { keyCode: 73, ctrlKey : true, shiftKey : true, altKey : false, metaKey : false }));
  32176. return $.Deferred().resolve();
  32177. };
  32178. };
  32179. /*
  32180. * File: /js/commands/selectnone.js
  32181. */
  32182. /**
  32183. * @class elFinder command "selectnone"
  32184. * Unselect ALL of cwd items
  32185. *
  32186. * @author Naoki Sawada
  32187. **/
  32188. elFinder.prototype.commands.selectnone = function() {
  32189. "use strict";
  32190. var self = this,
  32191. fm = this.fm,
  32192. state = -1;
  32193. fm.bind('select', function(e) {
  32194. state = (e.data && e.data.unselectall)? -1 : 0;
  32195. });
  32196. this.state = -1;
  32197. this.updateOnSelect = false;
  32198. this.getstate = function() {
  32199. return state;
  32200. };
  32201. this.exec = function() {
  32202. fm.getUI('cwd').trigger('unselectall');
  32203. return $.Deferred().resolve();
  32204. };
  32205. };
  32206. /*
  32207. * File: /js/commands/sort.js
  32208. */
  32209. /**
  32210. * @class elFinder command "sort"
  32211. * Change sort files rule
  32212. *
  32213. * @author Dmitry (dio) Levashov
  32214. **/
  32215. elFinder.prototype.commands.sort = function() {
  32216. "use strict";
  32217. var self = this,
  32218. fm = self.fm,
  32219. setVar = function() {
  32220. self.variants = [];
  32221. $.each(fm.sortRules, function(name, value) {
  32222. if (fm.sorters[name]) {
  32223. var arr = (name === fm.sortType)? (fm.sortOrder === 'asc'? 'n' : 's') : '';
  32224. self.variants.push([name, (arr? '<span class="ui-icon ui-icon-arrowthick-1-'+arr+'"></span>' : '') + '&nbsp;' + fm.i18n('sort'+name)]);
  32225. }
  32226. });
  32227. self.variants.push('|');
  32228. self.variants.push([
  32229. 'stick',
  32230. (fm.sortStickFolders? '<span class="ui-icon ui-icon-check"></span>' : '') + '&nbsp;' + fm.i18n('sortFoldersFirst')
  32231. ]);
  32232. if (fm.ui.tree && fm.options.sortAlsoTreeview !== null) {
  32233. self.variants.push('|');
  32234. self.variants.push([
  32235. 'tree',
  32236. (fm.sortAlsoTreeview? '<span class="ui-icon ui-icon-check"></span>' : '') + '&nbsp;' + fm.i18n('sortAlsoTreeview')
  32237. ]);
  32238. }
  32239. updateContextmenu();
  32240. },
  32241. updateContextmenu = function() {
  32242. var cm = fm.getUI('contextmenu'),
  32243. icon, sub;
  32244. if (cm.is(':visible')) {
  32245. icon = cm.find('span.elfinder-button-icon-sort');
  32246. sub = icon.siblings('div.elfinder-contextmenu-sub');
  32247. sub.find('span.ui-icon').remove();
  32248. sub.children('div.elfinder-contextsubmenu-item').each(function() {
  32249. var tgt = $(this).children('span'),
  32250. name = tgt.text().trim(),
  32251. arr;
  32252. if (name === (i18Name.stick || (i18Name.stick = fm.i18n('sortFoldersFirst')))) {
  32253. if (fm.sortStickFolders) {
  32254. tgt.prepend('<span class="ui-icon ui-icon-check"></span>');
  32255. }
  32256. } else if (name === (i18Name.tree || (i18Name.tree = fm.i18n('sortAlsoTreeview')))) {
  32257. if (fm.sortAlsoTreeview) {
  32258. tgt.prepend('<span class="ui-icon ui-icon-check"></span>');
  32259. }
  32260. } else if (name === (i18Name[fm.sortType] || (i18Name[fm.sortType] = fm.i18n('sort' + fm.sortType)))) {
  32261. arr = fm.sortOrder === 'asc'? 'n' : 's';
  32262. tgt.prepend('<span class="ui-icon ui-icon-arrowthick-1-'+arr+'"></span>');
  32263. }
  32264. });
  32265. }
  32266. },
  32267. i18Name = {};
  32268. /**
  32269. * Command options
  32270. *
  32271. * @type Object
  32272. */
  32273. this.options = {ui : 'sortbutton'};
  32274. this.keepContextmenu = true;
  32275. fm.bind('sortchange', setVar)
  32276. .bind('sorterupdate', function() {
  32277. setVar();
  32278. fm.getUI().children('.elfinder-button-sort-menu').children('.elfinder-button-menu-item').each(function() {
  32279. var tgt = $(this),
  32280. rel = tgt.attr('rel');
  32281. tgt.toggle(!!(! rel || fm.sorters[rel]));
  32282. });
  32283. })
  32284. .bind('cwdrender', function() {
  32285. var cols = $(fm.cwd).find('div.elfinder-cwd-wrapper-list table');
  32286. if (cols.length) {
  32287. $.each(fm.sortRules, function(name, value) {
  32288. var td = cols.find('thead tr td.elfinder-cwd-view-th-'+name);
  32289. if (td.length) {
  32290. var current = ( name == fm.sortType),
  32291. sort = {
  32292. type : name,
  32293. order : current ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder
  32294. },arr;
  32295. if (current) {
  32296. td.addClass('ui-state-active');
  32297. arr = fm.sortOrder == 'asc' ? 'n' : 's';
  32298. $('<span class="ui-icon ui-icon-triangle-1-'+arr+'"></span>').appendTo(td);
  32299. }
  32300. $(td).on('click', function(e){
  32301. if (! $(this).data('dragging')) {
  32302. e.stopPropagation();
  32303. if (! fm.getUI('cwd').data('longtap')) {
  32304. fm.exec('sort', [], sort);
  32305. }
  32306. }
  32307. })
  32308. .on('mouseenter mouseleave', function(e) {
  32309. $(this).toggleClass('ui-state-hover', e.type === 'mouseenter');
  32310. });
  32311. }
  32312. });
  32313. }
  32314. });
  32315. this.getstate = function() {
  32316. return 0;
  32317. };
  32318. this.exec = function(hashes, cOpt) {
  32319. var fm = this.fm,
  32320. sortopt = $.isPlainObject(cOpt)? cOpt : (function() {
  32321. cOpt += '';
  32322. var sOpts = {};
  32323. if (cOpt === 'stick') {
  32324. sOpts.stick = !fm.sortStickFolders;
  32325. } else if (cOpt === 'tree') {
  32326. sOpts.tree = !fm.sortAlsoTreeview;
  32327. } else if (fm.sorters[cOpt]) {
  32328. if (fm.sortType === cOpt) {
  32329. sOpts.order = fm.sortOrder === 'asc'? 'desc' : 'asc';
  32330. } else {
  32331. sOpts.type = cOpt;
  32332. }
  32333. }
  32334. return sOpts;
  32335. })(),
  32336. sort = Object.assign({
  32337. type : fm.sortType,
  32338. order : fm.sortOrder,
  32339. stick : fm.sortStickFolders,
  32340. tree : fm.sortAlsoTreeview
  32341. }, sortopt);
  32342. return fm.lazy(function() {
  32343. fm.setSort(sort.type, sort.order, sort.stick, sort.tree);
  32344. this.resolve();
  32345. });
  32346. };
  32347. };
  32348. /*
  32349. * File: /js/commands/undo.js
  32350. */
  32351. /**
  32352. * @class elFinder command "undo"
  32353. * Undo previous commands
  32354. *
  32355. * @author Naoki Sawada
  32356. **/
  32357. elFinder.prototype.commands.undo = function() {
  32358. "use strict";
  32359. var self = this,
  32360. fm = this.fm,
  32361. setTitle = function(undo) {
  32362. if (undo) {
  32363. self.title = fm.i18n('cmdundo') + ' ' + fm.i18n('cmd'+undo.cmd);
  32364. self.state = 0;
  32365. } else {
  32366. self.title = fm.i18n('cmdundo');
  32367. self.state = -1;
  32368. }
  32369. self.change();
  32370. },
  32371. cmds = [];
  32372. this.alwaysEnabled = true;
  32373. this.updateOnSelect = false;
  32374. this.shortcuts = [{
  32375. pattern : 'ctrl+z'
  32376. }];
  32377. this.syncTitleOnChange = true;
  32378. this.getstate = function() {
  32379. return cmds.length? 0 : -1;
  32380. };
  32381. this.setUndo = function(undo, redo) {
  32382. var _undo = {};
  32383. if (undo) {
  32384. if ($.isPlainObject(undo) && undo.cmd && undo.callback) {
  32385. Object.assign(_undo, undo);
  32386. if (redo) {
  32387. delete redo.undo;
  32388. _undo.redo = redo;
  32389. } else {
  32390. fm.getCommand('redo').setRedo(null);
  32391. }
  32392. cmds.push(_undo);
  32393. setTitle(_undo);
  32394. }
  32395. }
  32396. };
  32397. this.exec = function() {
  32398. var redo = fm.getCommand('redo'),
  32399. dfd = $.Deferred(),
  32400. undo, res, _redo = {};
  32401. if (cmds.length) {
  32402. undo = cmds.pop();
  32403. if (undo.redo) {
  32404. Object.assign(_redo, undo.redo);
  32405. delete undo.redo;
  32406. } else {
  32407. _redo = null;
  32408. }
  32409. dfd.done(function() {
  32410. if (_redo) {
  32411. redo.setRedo(_redo, undo);
  32412. }
  32413. });
  32414. setTitle(cmds.length? cmds[cmds.length-1] : void(0));
  32415. res = undo.callback();
  32416. if (res && res.done) {
  32417. res.done(function() {
  32418. dfd.resolve();
  32419. }).fail(function() {
  32420. dfd.reject();
  32421. });
  32422. } else {
  32423. dfd.resolve();
  32424. }
  32425. if (cmds.length) {
  32426. this.update(0, cmds[cmds.length - 1].name);
  32427. } else {
  32428. this.update(-1, '');
  32429. }
  32430. } else {
  32431. dfd.reject();
  32432. }
  32433. return dfd;
  32434. };
  32435. fm.bind('exec', function(e) {
  32436. var data = e.data || {};
  32437. if (data.opts && data.opts._userAction) {
  32438. if (data.dfrd && data.dfrd.done) {
  32439. data.dfrd.done(function(res) {
  32440. if (res && res.undo && res.redo) {
  32441. res.undo.redo = res.redo;
  32442. self.setUndo(res.undo);
  32443. }
  32444. });
  32445. }
  32446. }
  32447. });
  32448. };
  32449. /**
  32450. * @class elFinder command "redo"
  32451. * Redo previous commands
  32452. *
  32453. * @author Naoki Sawada
  32454. **/
  32455. elFinder.prototype.commands.redo = function() {
  32456. "use strict";
  32457. var self = this,
  32458. fm = this.fm,
  32459. setTitle = function(redo) {
  32460. if (redo && redo.callback) {
  32461. self.title = fm.i18n('cmdredo') + ' ' + fm.i18n('cmd'+redo.cmd);
  32462. self.state = 0;
  32463. } else {
  32464. self.title = fm.i18n('cmdredo');
  32465. self.state = -1;
  32466. }
  32467. self.change();
  32468. },
  32469. cmds = [];
  32470. this.alwaysEnabled = true;
  32471. this.updateOnSelect = false;
  32472. this.shortcuts = [{
  32473. pattern : 'shift+ctrl+z ctrl+y'
  32474. }];
  32475. this.syncTitleOnChange = true;
  32476. this.getstate = function() {
  32477. return cmds.length? 0 : -1;
  32478. };
  32479. this.setRedo = function(redo, undo) {
  32480. if (redo === null) {
  32481. cmds = [];
  32482. setTitle();
  32483. } else {
  32484. if (redo && redo.cmd && redo.callback) {
  32485. if (undo) {
  32486. redo.undo = undo;
  32487. }
  32488. cmds.push(redo);
  32489. setTitle(redo);
  32490. }
  32491. }
  32492. };
  32493. this.exec = function() {
  32494. var undo = fm.getCommand('undo'),
  32495. dfd = $.Deferred(),
  32496. redo, res, _undo = {}, _redo = {};
  32497. if (cmds.length) {
  32498. redo = cmds.pop();
  32499. if (redo.undo) {
  32500. Object.assign(_undo, redo.undo);
  32501. Object.assign(_redo, redo);
  32502. delete _redo.undo;
  32503. dfd.done(function() {
  32504. undo.setUndo(_undo, _redo);
  32505. });
  32506. }
  32507. setTitle(cmds.length? cmds[cmds.length-1] : void(0));
  32508. res = redo.callback();
  32509. if (res && res.done) {
  32510. res.done(function() {
  32511. dfd.resolve();
  32512. }).fail(function() {
  32513. dfd.reject();
  32514. });
  32515. } else {
  32516. dfd.resolve();
  32517. }
  32518. return dfd;
  32519. } else {
  32520. return dfd.reject();
  32521. }
  32522. };
  32523. };
  32524. /*
  32525. * File: /js/commands/up.js
  32526. */
  32527. /**
  32528. * @class elFinder command "up"
  32529. * Go into parent directory
  32530. *
  32531. * @author Dmitry (dio) Levashov
  32532. **/
  32533. (elFinder.prototype.commands.up = function() {
  32534. "use strict";
  32535. this.alwaysEnabled = true;
  32536. this.updateOnSelect = false;
  32537. this.shortcuts = [{
  32538. pattern : 'ctrl+up'
  32539. }];
  32540. this.getstate = function() {
  32541. return this.fm.cwd().phash ? 0 : -1;
  32542. };
  32543. this.exec = function() {
  32544. var fm = this.fm,
  32545. cwdhash = fm.cwd().hash;
  32546. return this.fm.cwd().phash ? this.fm.exec('open', this.fm.cwd().phash).done(function() {
  32547. fm.one('opendone', function() {
  32548. fm.selectfiles({files : [cwdhash]});
  32549. });
  32550. }) : $.Deferred().reject();
  32551. };
  32552. }).prototype = { forceLoad : true }; // this is required command
  32553. /*
  32554. * File: /js/commands/upload.js
  32555. */
  32556. /**
  32557. * @class elFinder command "upload"
  32558. * Upload files using iframe or XMLHttpRequest & FormData.
  32559. * Dialog allow to send files using drag and drop
  32560. *
  32561. * @type elFinder.command
  32562. * @author Dmitry (dio) Levashov
  32563. */
  32564. elFinder.prototype.commands.upload = function() {
  32565. "use strict";
  32566. var hover = this.fm.res('class', 'hover');
  32567. this.disableOnSearch = true;
  32568. this.updateOnSelect = false;
  32569. // Shortcut opens dialog
  32570. this.shortcuts = [{
  32571. pattern : 'ctrl+u'
  32572. }];
  32573. /**
  32574. * Return command state
  32575. *
  32576. * @return Number
  32577. **/
  32578. this.getstate = function(select) {
  32579. var fm = this.fm, f,
  32580. sel = (select || [fm.cwd().hash]);
  32581. if (!this._disabled && sel.length == 1) {
  32582. f = fm.file(sel[0]);
  32583. }
  32584. return (f && f.mime == 'directory' && f.write)? 0 : -1;
  32585. };
  32586. this.exec = function(data) {
  32587. var fm = this.fm,
  32588. cwdHash = fm.cwd().hash,
  32589. getTargets = function() {
  32590. var tgts = data && (data instanceof Array)? data : null,
  32591. sel;
  32592. if (! data || data instanceof Array) {
  32593. if (! tgts && (sel = fm.selected()).length === 1 && fm.file(sel[0]).mime === 'directory') {
  32594. tgts = sel;
  32595. } else if (!tgts || tgts.length !== 1 || fm.file(tgts[0]).mime !== 'directory') {
  32596. tgts = [ cwdHash ];
  32597. }
  32598. }
  32599. return tgts;
  32600. },
  32601. targets = getTargets(),
  32602. check = targets? targets[0] : (data && data.target? data.target : null),
  32603. targetDir = check? fm.file(check) : fm.cwd(),
  32604. fmUpload = function(data) {
  32605. fm.upload(data)
  32606. .fail(function(error) {
  32607. dfrd.reject(error);
  32608. })
  32609. .done(function(data) {
  32610. var cwd = fm.getUI('cwd'),
  32611. node;
  32612. dfrd.resolve(data);
  32613. if (data && data.added && data.added[0] && ! fm.ui.notify.children('.elfinder-notify-upload').length) {
  32614. var newItem = fm.findCwdNodes(data.added);
  32615. if (newItem.length) {
  32616. newItem.trigger('scrolltoview');
  32617. } else {
  32618. if (targetDir.hash !== cwdHash) {
  32619. node = $('<div></div>').append(
  32620. $('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all elfinder-tabstop"><span class="ui-button-text">'+fm.i18n('cmdopendir')+'</span></button>')
  32621. .on('mouseenter mouseleave', function(e) {
  32622. $(this).toggleClass('ui-state-hover', e.type == 'mouseenter');
  32623. }).on('click', function() {
  32624. fm.exec('open', check).done(function() {
  32625. fm.one('opendone', function() {
  32626. fm.trigger('selectfiles', {files : $.map(data.added, function(f) {return f.hash;})});
  32627. });
  32628. });
  32629. })
  32630. );
  32631. } else {
  32632. fm.trigger('selectfiles', {files : $.map(data.added, function(f) {return f.hash;})});
  32633. }
  32634. fm.toast({msg: fm.i18n(['complete', fm.i18n('cmdupload')]), extNode: node});
  32635. }
  32636. }
  32637. })
  32638. .progress(function() {
  32639. dfrd.notifyWith(this, Array.from(arguments));
  32640. });
  32641. },
  32642. upload = function(data) {
  32643. dialog.elfinderdialog('close');
  32644. if (targets) {
  32645. data.target = targets[0];
  32646. }
  32647. fmUpload(data);
  32648. },
  32649. getSelector = function() {
  32650. var hash = targetDir.hash,
  32651. dirs = $.map(fm.files(hash), function(f) {
  32652. return (f.mime === 'directory' && f.write)? f : null;
  32653. });
  32654. if (! dirs.length) {
  32655. return $();
  32656. }
  32657. return $('<div class="elfinder-upload-dirselect elfinder-tabstop" title="' + fm.i18n('folders') + '"></div>')
  32658. .on('click', function(e) {
  32659. e.stopPropagation();
  32660. e.preventDefault();
  32661. dirs = fm.sortFiles(dirs);
  32662. var $this = $(this),
  32663. cwd = fm.cwd(),
  32664. base = dialog.closest('div.ui-dialog'),
  32665. getRaw = function(f, icon) {
  32666. return {
  32667. label : fm.escape(f.i18 || f.name),
  32668. icon : icon,
  32669. remain : false,
  32670. callback : function() {
  32671. var title = base.children('.ui-dialog-titlebar:first').find('span.elfinder-upload-target');
  32672. targets = [ f.hash ];
  32673. title.html(' - ' + fm.escape(f.i18 || f.name));
  32674. $this.trigger('focus');
  32675. },
  32676. options : {
  32677. className : (targets && targets.length && f.hash === targets[0])? 'ui-state-active' : '',
  32678. iconClass : f.csscls || '',
  32679. iconImg : f.icon || ''
  32680. }
  32681. };
  32682. },
  32683. raw = [ getRaw(targetDir, 'opendir'), '|' ];
  32684. $.each(dirs, function(i, f) {
  32685. raw.push(getRaw(f, 'dir'));
  32686. });
  32687. $this.trigger('blur');
  32688. fm.trigger('contextmenu', {
  32689. raw: raw,
  32690. x: e.pageX || $(this).offset().left,
  32691. y: e.pageY || $(this).offset().top,
  32692. prevNode: base,
  32693. fitHeight: true
  32694. });
  32695. }).append('<span class="elfinder-button-icon elfinder-button-icon-dir" ></span>');
  32696. },
  32697. inputButton = function(type, caption) {
  32698. var button,
  32699. input = $('<input type="file" ' + type + '/>')
  32700. .on('click', function() {
  32701. // for IE's bug
  32702. if (fm.UA.IE) {
  32703. setTimeout(function() {
  32704. form.css('display', 'none').css('position', 'relative');
  32705. requestAnimationFrame(function() {
  32706. form.css('display', '').css('position', '');
  32707. });
  32708. }, 100);
  32709. }
  32710. })
  32711. .on('change', function() {
  32712. upload({input : input.get(0), type : 'files'});
  32713. })
  32714. .on('dragover', function(e) {
  32715. e.originalEvent.dataTransfer.dropEffect = 'copy';
  32716. }),
  32717. form = $('<form></form>').append(input).on('click', function(e) {
  32718. e.stopPropagation();
  32719. });
  32720. return $('<div class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only elfinder-tabstop elfinder-focus"><span class="ui-button-text">'+fm.i18n(caption)+'</span></div>')
  32721. .append(form)
  32722. .on('click', function(e) {
  32723. e.stopPropagation();
  32724. e.preventDefault();
  32725. input.trigger('click');
  32726. })
  32727. .on('mouseenter mouseleave', function(e) {
  32728. $(this).toggleClass(hover, e.type === 'mouseenter');
  32729. });
  32730. },
  32731. dfrd = $.Deferred(),
  32732. dialog, dropbox, pastebox, dropUpload, paste, dirs, spinner, uidialog;
  32733. dropUpload = function(e) {
  32734. e.stopPropagation();
  32735. e.preventDefault();
  32736. var file = false,
  32737. type = '',
  32738. elfFrom = null,
  32739. mycwd = '',
  32740. data = null,
  32741. target = e._target || null,
  32742. trf = e.dataTransfer || null,
  32743. kind = (trf.items && trf.items.length && trf.items[0].kind)? trf.items[0].kind : '',
  32744. errors;
  32745. if (trf) {
  32746. try {
  32747. elfFrom = trf.getData('elfinderfrom');
  32748. if (elfFrom) {
  32749. mycwd = window.location.href + fm.cwd().hash;
  32750. if ((!target && elfFrom === mycwd) || target === mycwd) {
  32751. dfrd.reject();
  32752. return;
  32753. }
  32754. }
  32755. } catch(e) {}
  32756. if (kind === 'file' && (trf.items[0].getAsEntry || trf.items[0].webkitGetAsEntry)) {
  32757. file = trf;
  32758. type = 'data';
  32759. } else if (kind !== 'string' && trf.files && trf.files.length && $.inArray('Text', trf.types) === -1) {
  32760. file = trf.files;
  32761. type = 'files';
  32762. } else {
  32763. try {
  32764. if ((data = trf.getData('text/html')) && data.match(/<(?:img|a)/i)) {
  32765. file = [ data ];
  32766. type = 'html';
  32767. }
  32768. } catch(e) {}
  32769. if (! file) {
  32770. if (data = trf.getData('text')) {
  32771. file = [ data ];
  32772. type = 'text';
  32773. } else if (trf && trf.files) {
  32774. // maybe folder uploading but this UA dose not support it
  32775. kind = 'file';
  32776. }
  32777. }
  32778. }
  32779. }
  32780. if (file) {
  32781. fmUpload({files : file, type : type, target : target, dropEvt : e});
  32782. } else {
  32783. errors = ['errUploadNoFiles'];
  32784. if (kind === 'file') {
  32785. errors.push('errFolderUpload');
  32786. }
  32787. fm.error(errors);
  32788. dfrd.reject();
  32789. }
  32790. };
  32791. if (!targets && data) {
  32792. if (data.input || data.files) {
  32793. data.type = 'files';
  32794. fmUpload(data);
  32795. } else if (data.dropEvt) {
  32796. dropUpload(data.dropEvt);
  32797. }
  32798. return dfrd;
  32799. }
  32800. paste = function(ev) {
  32801. var e = ev.originalEvent || ev;
  32802. var files = [], items = [];
  32803. var file;
  32804. if (e.clipboardData) {
  32805. if (e.clipboardData.items && e.clipboardData.items.length){
  32806. items = e.clipboardData.items;
  32807. for (var i=0; i < items.length; i++) {
  32808. if (e.clipboardData.items[i].kind == 'file') {
  32809. file = e.clipboardData.items[i].getAsFile();
  32810. files.push(file);
  32811. }
  32812. }
  32813. } else if (e.clipboardData.files && e.clipboardData.files.length) {
  32814. files = e.clipboardData.files;
  32815. }
  32816. if (files.length) {
  32817. upload({files : files, type : 'files', clipdata : true});
  32818. return;
  32819. }
  32820. }
  32821. var my = e.target || e.srcElement;
  32822. requestAnimationFrame(function() {
  32823. var type = 'text',
  32824. src;
  32825. if (my.innerHTML) {
  32826. $(my).find('img').each(function(i, v){
  32827. if (v.src.match(/^webkit-fake-url:\/\//)) {
  32828. // For Safari's bug.
  32829. // ref. https://bugs.webkit.org/show_bug.cgi?id=49141
  32830. // https://dev.ckeditor.com/ticket/13029
  32831. $(v).remove();
  32832. }
  32833. });
  32834. if ($(my).find('a,img').length) {
  32835. type = 'html';
  32836. }
  32837. src = my.innerHTML;
  32838. my.innerHTML = '';
  32839. upload({files : [ src ], type : type});
  32840. }
  32841. });
  32842. };
  32843. dialog = $('<div class="elfinder-upload-dialog-wrapper"></div>')
  32844. .append(inputButton('multiple', 'selectForUpload'));
  32845. if (! fm.UA.Mobile && (function(input) {
  32846. return (typeof input.webkitdirectory !== 'undefined' || typeof input.directory !== 'undefined');})(document.createElement('input'))) {
  32847. dialog.append(inputButton('multiple webkitdirectory directory', 'selectFolder'));
  32848. }
  32849. if (targetDir.dirs) {
  32850. if (targetDir.hash === cwdHash || fm.navHash2Elm(targetDir.hash).hasClass('elfinder-subtree-loaded')) {
  32851. getSelector().appendTo(dialog);
  32852. } else {
  32853. spinner = $('<div class="elfinder-upload-dirselect" title="' + fm.i18n('nowLoading') + '"></div>')
  32854. .append('<span class="elfinder-button-icon elfinder-button-icon-spinner" ></span>')
  32855. .appendTo(dialog);
  32856. fm.request({cmd : 'tree', target : targetDir.hash})
  32857. .done(function() {
  32858. fm.one('treedone', function() {
  32859. spinner.replaceWith(getSelector());
  32860. uidialog.elfinderdialog('tabstopsInit');
  32861. });
  32862. })
  32863. .fail(function() {
  32864. spinner.remove();
  32865. });
  32866. }
  32867. }
  32868. if (fm.dragUpload) {
  32869. dropbox = $('<div class="ui-corner-all elfinder-upload-dropbox elfinder-tabstop" contenteditable="true" data-ph="'+fm.i18n('dropPasteFiles')+'"></div>')
  32870. .on('paste', function(e){
  32871. paste(e);
  32872. })
  32873. .on('mousedown click', function(){
  32874. $(this).trigger('focus');
  32875. })
  32876. .on('focus', function(){
  32877. this.innerHTML = '';
  32878. })
  32879. .on('mouseover', function(){
  32880. $(this).addClass(hover);
  32881. })
  32882. .on('mouseout', function(){
  32883. $(this).removeClass(hover);
  32884. })
  32885. .on('dragenter', function(e) {
  32886. e.stopPropagation();
  32887. e.preventDefault();
  32888. $(this).addClass(hover);
  32889. })
  32890. .on('dragleave', function(e) {
  32891. e.stopPropagation();
  32892. e.preventDefault();
  32893. $(this).removeClass(hover);
  32894. })
  32895. .on('dragover', function(e) {
  32896. e.stopPropagation();
  32897. e.preventDefault();
  32898. e.originalEvent.dataTransfer.dropEffect = 'copy';
  32899. $(this).addClass(hover);
  32900. })
  32901. .on('drop', function(e) {
  32902. dialog.elfinderdialog('close');
  32903. targets && (e.originalEvent._target = targets[0]);
  32904. dropUpload(e.originalEvent);
  32905. })
  32906. .prependTo(dialog)
  32907. .after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0];
  32908. } else {
  32909. pastebox = $('<div class="ui-corner-all elfinder-upload-dropbox" contenteditable="true">'+fm.i18n('dropFilesBrowser')+'</div>')
  32910. .on('paste drop', function(e){
  32911. paste(e);
  32912. })
  32913. .on('mousedown click', function(){
  32914. $(this).trigger('focus');
  32915. })
  32916. .on('focus', function(){
  32917. this.innerHTML = '';
  32918. })
  32919. .on('dragenter mouseover', function(){
  32920. $(this).addClass(hover);
  32921. })
  32922. .on('dragleave mouseout', function(){
  32923. $(this).removeClass(hover);
  32924. })
  32925. .prependTo(dialog)
  32926. .after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0];
  32927. }
  32928. uidialog = this.fmDialog(dialog, {
  32929. title : this.title + '<span class="elfinder-upload-target">' + (targetDir? ' - ' + fm.escape(targetDir.i18 || targetDir.name) : '') + '</span>',
  32930. modal : true,
  32931. resizable : false,
  32932. destroyOnClose : true,
  32933. propagationEvents : ['mousemove', 'mouseup', 'click'],
  32934. close : function() {
  32935. var cm = fm.getUI('contextmenu');
  32936. if (cm.is(':visible')) {
  32937. cm.click();
  32938. }
  32939. }
  32940. });
  32941. return dfrd;
  32942. };
  32943. };
  32944. /*
  32945. * File: /js/commands/view.js
  32946. */
  32947. /**
  32948. * @class elFinder command "view"
  32949. * Change current directory view (icons/list)
  32950. *
  32951. * @author Dmitry (dio) Levashov
  32952. **/
  32953. elFinder.prototype.commands.view = function() {
  32954. "use strict";
  32955. var self = this,
  32956. fm = this.fm,
  32957. subMenuRaw;
  32958. this.value = fm.viewType;
  32959. this.alwaysEnabled = true;
  32960. this.updateOnSelect = false;
  32961. this.options = { ui : 'viewbutton'};
  32962. this.getstate = function() {
  32963. return 0;
  32964. };
  32965. this.extra = {
  32966. icon: 'menu',
  32967. node: $('<span></span>')
  32968. .attr({title: fm.i18n('viewtype')})
  32969. .on('click touchstart', function(e){
  32970. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  32971. return;
  32972. }
  32973. var node = $(this);
  32974. e.stopPropagation();
  32975. e.preventDefault();
  32976. fm.trigger('contextmenu', {
  32977. raw: getSubMenuRaw(),
  32978. x: node.offset().left,
  32979. y: node.offset().top
  32980. });
  32981. })
  32982. };
  32983. this.exec = function() {
  32984. var self = this,
  32985. value = this.value == 'list' ? 'icons' : 'list';
  32986. fm.storage('view', value);
  32987. return fm.lazy(function() {
  32988. fm.viewchange();
  32989. self.update(void(0), value);
  32990. this.resolve();
  32991. });
  32992. };
  32993. fm.bind('init', function() {
  32994. subMenuRaw = (function() {
  32995. var cwd = fm.getUI('cwd'),
  32996. raws = [],
  32997. sizeNames = fm.options.uiOptions.cwd.iconsView.sizeNames,
  32998. max = fm.options.uiOptions.cwd.iconsView.sizeMax,
  32999. i, size;
  33000. for (i = 0; i <= max; i++) {
  33001. raws.push(
  33002. {
  33003. label : fm.i18n(sizeNames[i] || ('Size-' + i + ' icons')),
  33004. icon : 'view',
  33005. callback : (function(s) {
  33006. return function() {
  33007. cwd.trigger('iconpref', {size: s});
  33008. fm.storage('iconsize', s);
  33009. if (self.value === 'list') {
  33010. self.exec();
  33011. }
  33012. };
  33013. })(i)
  33014. }
  33015. );
  33016. }
  33017. raws.push('|');
  33018. raws.push(
  33019. {
  33020. label : fm.i18n('viewlist'),
  33021. icon : 'view-list',
  33022. callback : function() {
  33023. if (self.value !== 'list') {
  33024. self.exec();
  33025. }
  33026. }
  33027. }
  33028. );
  33029. return raws;
  33030. })();
  33031. }).bind('contextmenucreate', function() {
  33032. self.extra = {
  33033. icon: 'menu',
  33034. node: $('<span></span>')
  33035. .attr({title: fm.i18n('cmdview')})
  33036. .on('click touchstart', function(e){
  33037. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  33038. return;
  33039. }
  33040. var node = $(this),
  33041. raw = subMenuRaw.concat(),
  33042. idx, i;
  33043. if (self.value === 'list') {
  33044. idx = subMenuRaw.length - 1;
  33045. } else {
  33046. idx = parseInt(fm.storage('iconsize') || 0);
  33047. }
  33048. for (i = 0; i < subMenuRaw.length; i++) {
  33049. if (subMenuRaw[i] !== '|') {
  33050. subMenuRaw[i].options = (i === idx? {'className': 'ui-state-active'} : void(0))
  33051. ;
  33052. }
  33053. }
  33054. e.stopPropagation();
  33055. e.preventDefault();
  33056. fm.trigger('contextmenu', {
  33057. raw: subMenuRaw,
  33058. x: node.offset().left,
  33059. y: node.offset().top
  33060. });
  33061. })
  33062. };
  33063. });
  33064. };
  33065. return elFinder;
  33066. }));